fix(approval): reduce prompt friction and make approval cards answerable forever

Approval friction (harmless commands kept prompting):
- Persist "Allow for this session" grants to approval_allowlist.yaml under a
  new sessions scope (capped LRU), hydrated lazily, so they survive `opc ui`
  restarts and re-entering the session instead of living only in memory.
- Safe-prefix matching now accepts compound read-only commands: every segment
  must match a safe prefix, and fd-duplication / /dev/null redirections
  (2>&1, 2>/dev/null) no longer disqualify a command; real write redirections
  (>, >>, <) still do. Default safe prefixes gain common read-only commands
  (cd, cat, head, grep, git log, ...).
- First-use approval now gates only MEDIUM+ risk; heuristically LOW actions
  proceed without a card.
- Shell-substitution detection flags eval/source only at command position of a
  segment (no more false positives on `grep source file`); $(...) and
  backticks still flag anywhere.

Approval card timeout redesign (deferred decisions):
- The card's structured approval context (action, allowlist patterns, scopes)
  now travels through the escalation event into the persisted card metadata.
- Timeout without a default action no longer marks the card timed out, and the
  session-detail reconciler no longer stales deferred-capable cards: the card
  stays pending and clickable indefinitely, including across restarts.
- Clicking after the inline wait expired applies the allowlist grant
  (approve-once grants the exact command at session scope), resolves the card,
  and rewrites the reply to target the parked AWAITING_HUMAN checkpoint so the
  task resumes through the normal message pipeline and the retried command
  auto-approves. With no parked checkpoint the grant still lands and a helper
  reply explains the state.

Verified: approval engine suite (40) incl. new deferred-decision and
compound-command tests, ws_handler + runtime suites green, real escalated
commands from project 999 replayed against the user's config now auto-approve
while pip install / $(...) / rm -rf / write redirects still prompt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
LZH-YS1998
2026-07-07 22:19:22 +08:00
parent e1c28c3889
commit c901800062
6 changed files with 682 additions and 60 deletions
+6
View File
@@ -994,6 +994,12 @@ class AutonomyConfig(BaseModel):
safe_command_prefixes: list[str] = Field(default_factory=lambda: [
"ls", "pwd", "echo", "rg", "find", "git status", "git diff", "python -V",
"python3 -V", "node -v", "npm -v", "curl", "wget", "yt-dlp", "aria2c", "ffmpeg",
# Read-only commands agents chain constantly; each segment of a compound
# command must match one of these for the whole command to stay LOW risk.
"cd", "cat", "head", "tail", "grep", "wc", "sort", "uniq", "cut", "tr",
"stat", "file", "which", "date", "du", "df", "tree", "basename", "dirname",
"realpath", "readlink", "uname", "nproc", "whoami", "hostname", "git log",
"git show", "git rev-parse",
])
permissions_v2: PermissionsV2Config = Field(default_factory=PermissionsV2Config)