mirror of
https://github.com/NCBM/plyngent.git
synced 2026-11-07 23:47: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,
|
reason: str,
|
||||||
timeout_seconds: float,
|
timeout_seconds: float,
|
||||||
) -> AccessDecision | str | None:
|
) -> AccessDecision | str | None:
|
||||||
"""Directory-access confirm: YOLO/``--yes`` auto-approves, else ask."""
|
"""Directory-access confirm: always ask, whatever YOLO mode says.
|
||||||
from plyngent.cli.limits import prompt_directory_access_confirm
|
|
||||||
from plyngent.tools.access import AccessDecision as Decision
|
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)
|
return prompt_directory_access_confirm(path, mode, reason, timeout_seconds)
|
||||||
|
|
||||||
def _install_config_access(self) -> None:
|
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:`WorkspacePolicy.config_allow` — static TOML pre-allow (never prompts),
|
||||||
- :attr:`SessionState.access_grants` — human-approved for this session,
|
- :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
|
``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
|
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``
|
workspace root``, passing the ``mode`` that error names. ``mode`` is ``read``
|
||||||
(inspect files), ``write`` (also modify files) or ``exec`` (also use it as
|
(inspect files), ``write`` (also modify files) or ``exec`` (also use it as
|
||||||
``run_argv`` / PTY cwd); the human may approve a different level. Approval
|
``run_argv`` / PTY cwd); the human may approve a different level. Approval
|
||||||
lasts for the session (or for this process when ``--yes`` auto-approves).
|
lasts for the session (or for this process when the host answers without
|
||||||
Paths matching the configured denylist can never be granted.
|
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)
|
requested = parse_access_mode(mode)
|
||||||
if requested is None:
|
if requested is None:
|
||||||
|
|||||||
@@ -114,7 +114,8 @@ class WorkspacePolicy:
|
|||||||
policy_confirm_timeout_seconds: float = DEFAULT_POLICY_CONFIRM_TIMEOUT_SECONDS
|
policy_confirm_timeout_seconds: float = DEFAULT_POLICY_CONFIRM_TIMEOUT_SECONDS
|
||||||
# Static TOML pre-allow (resolved path → mode); never prompts.
|
# Static TOML pre-allow (resolved path → mode); never prompts.
|
||||||
config_allow: dict[Path, AccessMode] = field(default_factory=dict)
|
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)
|
yolo_allow: dict[Path, AccessMode] = field(default_factory=dict)
|
||||||
# Host-contributed read-only roots (skill directories): every path tool may
|
# 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
|
# read them, nothing may write them. Not a grant, so they stay out of
|
||||||
|
|||||||
Reference in New Issue
Block a user