{
  "$id": "https://raw.githubusercontent.com/Mearman/agent-permissions/main/agent-permissions.schema.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "$schema": {
      "type": "string",
      "description": "JSON Schema URI for editor validation and autocomplete."
    },
    "defaultMode": {
      "type": "string",
      "enum": [
        "standard",
        "autonomous",
        "restricted",
        "readonly",
        "plan",
        "dontAsk",
        "acceptEdits",
        "bypassPermissions",
        "default",
        "auto"
      ],
      "description": "Default permission mode. Agent Permission Policy modes: standard, autonomous, restricted, readonly. Claude Code compatible modes: plan, dontAsk, acceptEdits, bypassPermissions, default.",
      "default": "standard"
    },
    "activeProfile": {
      "type": "string",
      "description": "Name of the active profile from `profiles`. If unset, use the top-level permission tiers directly."
    },
    "permissions": {
      "type": "object",
      "properties": {
        "allow": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Tools auto-approved without prompting. Evaluated after deny rules.",
          "examples": [
            "Bash(git status)",
            "Bash(npm run test:*)",
            "Read",
            "Grep"
          ]
        },
        "deny": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Tools always denied — short-circuits before allow and ask. A deny cannot be overridden by an allow in any other source.",
          "examples": [
            "Bash(sudo:*)",
            "Bash(rm -rf /)",
            "Read(./.env)"
          ]
        },
        "ask": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Tools that always prompt, even in autonomous mode. Evaluated after deny but before allow.",
          "examples": [
            "Bash(git push:*)",
            "Bash(npm publish:*)"
          ]
        },
        "additionalDirectories": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Directories beyond project root that agents may access.",
          "examples": [
            "../shared-libs/",
            "/tmp/build-cache"
          ]
        },
        "defaultMode": {
          "type": "string",
          "enum": [
            "standard",
            "autonomous",
            "restricted",
            "readonly",
            "plan",
            "dontAsk",
            "acceptEdits",
            "bypassPermissions",
            "default",
            "auto"
          ],
          "description": "Default permission mode. Accepted here for Claude Code compatibility, or at the top level for the canonical placement.",
          "default": "standard"
        }
      },
      "additionalProperties": false,
      "description": "Tool permission rules — evaluated in deny → ask → allow order."
    },
    "rules": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "tool": {
            "type": "string",
            "description": "Canonical tool name.",
            "examples": [
              "Bash",
              "Read",
              "Write",
              "Edit",
              "Grep",
              "WebFetch",
              "Agent",
              "mcp__github__create_issue"
            ]
          },
          "pattern": {
            "description": "Pattern to match against tool input. When absent, matches any input.",
            "examples": [
              "npm run *",
              "./config/**",
              "git push --force:*"
            ],
            "type": "string"
          },
          "tier": {
            "type": "string",
            "enum": [
              "allow",
              "deny",
              "ask"
            ],
            "description": "Permission tier to apply when this rule matches."
          },
          "when": {
            "description": "Optional conditions. All must match for the rule to apply (AND logic).",
            "type": "object",
            "properties": {
              "cwd": {
                "type": "string",
                "description": "Working directory pattern (glob)."
              },
              "branch": {
                "type": "string",
                "description": "Git branch name pattern (glob)."
              }
            },
            "additionalProperties": false
          }
        },
        "required": [
          "tool",
          "tier"
        ],
        "additionalProperties": false,
        "description": "Permission rule. Evaluated deny-first: all deny rules, then ask, then allow."
      },
      "description": "Permission rules. Evaluated deny-first: all deny rules checked, then ask, then allow. Falls back to defaultMode when no rule matches."
    },
    "profiles": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {
        "type": "object",
        "properties": {
          "allow": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tools auto-approved without prompting. Evaluated after deny rules.",
            "examples": [
              "Bash(git status)",
              "Bash(npm run test:*)",
              "Read",
              "Grep"
            ]
          },
          "deny": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tools always denied — short-circuits before allow and ask. A deny cannot be overridden by an allow in any other source.",
            "examples": [
              "Bash(sudo:*)",
              "Bash(rm -rf /)",
              "Read(./.env)"
            ]
          },
          "ask": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tools that always prompt, even in autonomous mode. Evaluated after deny but before allow.",
            "examples": [
              "Bash(git push:*)",
              "Bash(npm publish:*)"
            ]
          },
          "additionalDirectories": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Directories beyond project root that agents may access.",
            "examples": [
              "../shared-libs/",
              "/tmp/build-cache"
            ]
          },
          "defaultMode": {
            "type": "string",
            "enum": [
              "standard",
              "autonomous",
              "restricted",
              "readonly",
              "plan",
              "dontAsk",
              "acceptEdits",
              "bypassPermissions",
              "default",
              "auto"
            ],
            "description": "Default permission mode. Accepted here for Claude Code compatibility, or at the top level for the canonical placement.",
            "default": "standard"
          }
        },
        "additionalProperties": false
      },
      "description": "Named permission profiles.",
      "examples": [
        {
          "strict": {
            "deny": [
              "Write",
              "Edit",
              "Bash"
            ],
            "allow": [
              "Read",
              "Grep"
            ]
          },
          "relaxed": {
            "allow": [
              "Read",
              "Write",
              "Edit",
              "Bash(git:*)",
              "Bash(npm:*)"
            ]
          }
        }
      ]
    },
    "delegation": {
      "type": "object",
      "properties": {
        "maxDepth": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991,
          "description": "Maximum depth of agent nesting. 0 = no subagents allowed.",
          "default": 2
        },
        "nonDelegable": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Tools that cannot be delegated to subagents. Uses the same rule syntax.",
          "examples": [
            "Bash(sudo:*)",
            "Write(./.agents/**)"
          ]
        },
        "bubbleUp": {
          "type": "boolean",
          "description": "Whether subagents can request elevated permissions from their parent.",
          "default": true
        },
        "agents": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "object",
            "properties": {
              "allow": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Tools auto-approved without prompting. Evaluated after deny rules.",
                "examples": [
                  "Bash(git status)",
                  "Bash(npm run test:*)",
                  "Read",
                  "Grep"
                ]
              },
              "deny": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Tools always denied — short-circuits before allow and ask. A deny cannot be overridden by an allow in any other source.",
                "examples": [
                  "Bash(sudo:*)",
                  "Bash(rm -rf /)",
                  "Read(./.env)"
                ]
              },
              "ask": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Tools that always prompt, even in autonomous mode. Evaluated after deny but before allow.",
                "examples": [
                  "Bash(git push:*)",
                  "Bash(npm publish:*)"
                ]
              },
              "additionalDirectories": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Directories beyond project root that agents may access.",
                "examples": [
                  "../shared-libs/",
                  "/tmp/build-cache"
                ]
              },
              "defaultMode": {
                "type": "string",
                "enum": [
                  "standard",
                  "autonomous",
                  "restricted",
                  "readonly",
                  "plan",
                  "dontAsk",
                  "acceptEdits",
                  "bypassPermissions",
                  "default",
                  "auto"
                ],
                "description": "Default permission mode. Accepted here for Claude Code compatibility, or at the top level for the canonical placement.",
                "default": "standard"
              }
            },
            "additionalProperties": false
          },
          "description": "Per-agent permission overrides. Keys are agent names or glob patterns. Values replace (not merge with) the parent policy for that agent.",
          "examples": [
            {
              "review": {
                "deny": [
                  "Write",
                  "Edit",
                  "Bash"
                ],
                "allow": [
                  "Read",
                  "Grep"
                ]
              },
              "docs": {
                "allow": [
                  "Read",
                  "Write(./docs/**)",
                  "Bash(mdbook build:*)"
                ]
              }
            }
          ]
        }
      },
      "additionalProperties": false,
      "description": "Agent delegation controls — what subagents may do."
    },
    "sandbox": {
      "type": "object",
      "properties": {
        "mode": {
          "type": "string",
          "enum": [
            "readonly",
            "workspace-write",
            "full-access"
          ],
          "description": "Sandbox isolation mode.",
          "default": "workspace-write"
        },
        "writableRoots": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Additional paths the agent may write to (beyond project root). Only meaningful when mode is `workspace-write`.",
          "examples": [
            "/tmp/build-cache",
            "../shared-libs"
          ]
        },
        "networkAccess": {
          "type": "boolean",
          "description": "Whether the agent may make network requests from within the sandbox. When false, network access is blocked at the OS level.",
          "default": true
        }
      },
      "additionalProperties": false,
      "description": "OS-level sandbox configuration."
    },
    "network": {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "Whether network access is permitted at all. When false, WebFetch and WebSearch are denied regardless of allow rules.",
          "default": true
        },
        "domains": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {
            "type": "string",
            "enum": [
              "allow",
              "deny"
            ]
          },
          "description": "Domain-level allow/deny rules for HTTP(S) requests. Supplements the WebFetch(domain:...) permission rules.",
          "examples": [
            {
              "api.example.com": "allow",
              "evil.com": "deny"
            }
          ]
        }
      },
      "additionalProperties": false,
      "description": "Network access controls."
    },
    "env": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {
        "type": "string"
      },
      "description": "Environment variables injected into all agent sessions.",
      "examples": [
        {
          "NODE_ENV": "development",
          "DISABLE_TELEMETRY": "1"
        }
      ]
    },
    "with": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "claude-code",
          "codex",
          "kiro",
          "opencode",
          "crush"
        ]
      },
      "description": "Agent configs to include when loading. Only these native configs are read (plus canonical). Mutually exclusive with `without`.",
      "examples": [
        [
          "claude-code",
          "opencode"
        ]
      ]
    },
    "without": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "claude-code",
          "codex",
          "kiro",
          "opencode",
          "crush"
        ]
      },
      "description": "Agent configs to exclude when loading. Mutually exclusive with `with`.",
      "examples": [
        [
          "codex"
        ]
      ]
    },
    "up": {
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "all"
          ]
        },
        {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991
        }
      ],
      "description": "How far to walk up the directory tree when loading configs. 'all' walks to the filesystem root. A number limits parent directories (0 = cwd only).",
      "default": "all",
      "examples": [
        "all",
        0,
        3,
        5
      ]
    },
    "sync": {
      "type": "object",
      "properties": {
        "mode": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "sync",
                "watch"
              ]
            },
            {
              "type": "boolean",
              "const": false
            }
          ],
          "description": "Sync mode. 'sync' = one-shot at startup. 'watch' = continuous via fs.watch. false = disabled.",
          "default": false
        },
        "backup": {
          "type": "boolean",
          "description": "Write .bak files before overwriting.",
          "default": false
        }
      },
      "additionalProperties": false,
      "description": "Sync configuration for the MCP server and CLI sync command."
    }
  },
  "additionalProperties": false,
  "title": "Agent Permission Policy",
  "description": "Cross-agent permission policy for AI coding agents. Defines what tools agents may use, under what conditions, and how subagents are constrained. The `with`/`without`/`up` fields control which native agent configs are discovered and merged during walk-up loading. Placed at .agents/permissions.json (team, committed) or .agents/permissions.local.json (personal, gitignored)."
}
