Skip to content

google/adk-recipes

A collection of agent recipes, reference patterns, and vertical plugins built with Agent Development Kit (ADK)

https://skillcdn.ai/gh/google/adk-recipes

Connect this address to your AI to use this skill. How to connect

  • Unverified
  • Default branchmain
  • Commit5aacb99
  • LicenseApache-2.0
View on GitHub

policy

Inspect the per-workspace tool-policy overlay (.lha/policies.jsonl) that gates destructive or sensitive tool calls, and tell the user what to change if they want a rule added or removed.

Path
core/python/long-horizon-harness/horizon/builtin_skills/policy/SKILL.md
License
Apache-2.0

Inspect per-workspace tool policies

The policies_guard callback consults a JSONL policy file on every tool call. Policies live in two layers:

  • Default seed at horizon/guardrails/default_policies.jsonl (read-only, ships with the agent). Provides baseline blocks for catastrophic operations (literal dd if=/dev/zero, mkfs, fork bombs, >/dev/sd*, nc -l/ncat -l/socat bind listeners) and credential reads (cat ~/.ssh/id_rsa, cat ~/.aws/credentials). The seed no longer carries brittle substring/regex rules for rm -rf or chmod -R — those are now classified by an argv-structural parser (horizon/guardrails/command_safety.py) that lexes the command into tokens and inspects structure instead of pattern-matching raw strings.
  • User overlay at .lha/policies.jsonl under the workspace root. The overlay is appended to the seed — new rules add restrictions on top of the defaults. Mtime-cached, so edits take effect on the next tool call. You can read this file, but you cannot write it: .lha/* and */.lha/* are themselves hard-denied destructive-path patterns for write and edit, and the same paths are hard-denied for bash (append, sed -i, cp/mv, rm, chmod into .lha/ all match a seed destructive_commands_regex rule). This is by design — see docs/security-model.md — the agent cannot self-edit its own guard config.

When to use this skill

  • The user asks what policies are currently in force.
  • The user asks you to block a specific command or path pattern, or to relax (remove) an overlay rule — in both cases you inspect the current file and hand the user the exact change to make; you do not make it yourself.

If the user is asking about a one-time approval for a call that was just blocked, tell them to type /grant <command> themselves — that slash command is a user-only surface, not something you can invoke.

File format

One JSON object per line. Blank lines and # comments are skipped. Every rule must include canonical_tool_name; that field gates which tool the rule applies to.

A rule may carry one or more of these fields. They are evaluated independently — set as many as you need on a single rule.

FieldTypeEffect
destructivetrueAlways block the tool.
destructive_commands{arg: [substring, ...]}Block when the string arg contains any substring (case-insensitive).
destructive_commands_regex{arg: [regex, ...]}Block when any regex matches the string arg (case-insensitive). Use when substrings are too blunt. Tenant-authored regexes (overlay + grant rules) are validated for length and nested-quantifier patterns before compilation; malformed regexes are skipped with a warning.
destructive_paths[prefix, ...]Block when a path-shaped arg (path, file_path, target_path) starts with any prefix.
destructive_path_patterns[fnmatch-glob, ...]Block when a path-shaped arg matches any fnmatch glob. Use for per-user paths like */.ssh/*.

Demotable destructive commands (argv classification)

Before the overlay/seed rules run, command_safety.py lexes shell commands into argv tokens (quote- and operator-aware via stdlib shlex) and inspects structure. It returns a verdict: "deny" (catastrophic, always blocks), "ask" (risky, blocks children/headless, prompts the root agent via an interactive approval card), or None (no opinion). Examples:

  • "deny" — rm -rf /, rm -rf /etc, rm -rf $HOME, rm -rf /*, etc. (recursive force-delete of system/home roots or their subdirectories).
  • "ask" — find . -delete, git push --force, chmod -R 777 ., sudo apt install ..., curl <url> | bash.

On an "ask" verdict, the root agent sees an interactive four-button approval card; the child/headless chain treats it as a hard deny (no regression in unattended contexts). This replaces the fragile substring/regex rules that shipped in older seeds — the new seed (default_policies.jsonl) only carries literal catastrophic commands + credential reads.

Approval modes

The root agent's interactive approval can be set to auto-approve demotable-ask verdicts (ONLY) via /yolo (toggles between default and yolo modes). YOLO mode auto-approves the Layer-D interactive ask; it does not bypass the exfil guard or Layer-C hard-deny rules (catastrophic

  • credential reads + seed overlays). Use it when you trust the session and want fewer prompts for risky-but-non-catastrophic operations. Approval mode is per-session state, not persisted.

Tool-narrowing enforcement

Permission rules in .lha/permissions.jsonl or granted via the interactive approval card may include a commandPrefix, commandRegex, or argsPattern to narrow blanket allow rules. Overlay and grant rules (source = "overlay" or "grant") that target bash or process but carry no such narrowing field are rejected at load time — you cannot grant a blanket "always allow bash" from the overlay or a session approval; only the default seed may carry that. This prevents accidental over-granting.

Integration with the permission layer

A policy block returns {"error": ..., "confirmation_required": True}. Tell the user why the call was blocked; if they want a one-time approval, they type /grant <command> themselves. To force a prompt (not a hard block) or block a specific command for the user, use the permission layer instead: add an ask_user or deny rule to .lha/permissions.jsonl (see docs/permission-model.md). This overlay is hard-block only, and it lives under the same .lha/ write restriction as the policy overlay — the user edits it directly, not you.

Workflow

List active rules
read(".lha/policies.jsonl")

If the file does not exist, the user has no overlay rules yet — only the seed is active. The seed itself lives outside the workspace and is not readable through your tools; describe its coverage from this skill's own "Default seed" section above instead of trying to read it.

The user wants a rule added

Read the current overlay so your suggestion appends correctly, then give the user the exact line to add themselves — you cannot write .lha/policies.jsonl.

existing = read(".lha/policies.jsonl")   # may be missing → treat as ""
new_rule = {"canonical_tool_name": "bash",
            "destructive_commands": {"command": ["rm -rf node_modules"]}}

Tell the user: "Add this line to .lha/policies.jsonl (create the file if it doesn't exist yet, one JSON object per line):" followed by json.dumps(new_rule).

The user wants a rule removed

Read the overlay, identify the line to drop (0-based index into the overlay, ignoring blank/comment lines), and tell the user which line number and content to delete from .lha/policies.jsonl. Confirm the target with them first — overlay rules typically exist because they (or you on their behalf) added them to plug a gap.

Worked example

User: "Block npm publish from the terminal."

  1. Read .lha/policies.jsonl (returns "" if missing).
  2. Build:
    {"canonical_tool_name": "bash", "destructive_commands": {"command": ["npm publish"]}}
    
  3. Tell the user: "I can't edit .lha/policies.jsonl myself — add this line to it (create the file if needed):" followed by the JSON above. "Once saved, the next npm publish attempt will be blocked."

Notes

  • The default seed is read-only and out of your reach either way — you cannot edit it, and it lives outside the workspace root so you cannot read it through your tools.
  • Malformed JSONL fails closed at parse time (the bad line is skipped with a warning) — tell the user to double-check a new rule parses by reading the file back after they save it.

Files of this skill

This skill has no supporting files.

Browse all supporting files