Files
2026-07-13 13:12:00 +08:00

18 KiB

Policies

Policies are declarative gates that enforce rules on agent behavior. They evaluate agent actions at specific enforcement points and return one of three verdicts:

  • ALLOW -- the action proceeds.
  • DENY -- the action is blocked; the agent receives an error.
  • ASK -- the action is paused for user approval; approved becomes ALLOW, refused becomes DENY.

Policies compose: multiple policies can be active at once. The engine evaluates them in declaration order. A DENY from any policy short-circuits the rest.

Who configures policies

Policies are set at three levels. Each level serves a different persona:

Level Who How Evaluated
Server-wide Admin policies in server config YAML, or REST API Last
Agent spec Agent developer policies in agent YAML Middle
Session End user Session settings panel in the UI First

Session policies evaluate first and can short-circuit (DENY) before spec or admin policies run.

Policy trust model


For server admins

Setting up server-wide policies

Server-wide (default) policies apply to every session. They act as organizational guardrails.

1. Choose policies. Browse the builtin policy registry (see Builtin policies below) or install community/custom policy modules.

2. Register custom policy modules (optional). If you use policies outside the builtins, add their module paths to the server config so they appear in the registry:

# server_config.yaml
policy_modules:
  - myorg_policies
  - github_mcp_policy

3. Add policies to the server config.

# server_config.yaml
policies:
  session_budget:
    type: function
    handler: omnigent.policies.builtins.cost.cost_budget
    factory_params:
      max_cost_usd: 10.00
      ask_thresholds_usd: [5.00]
  global_rate_limit:
    type: function
    handler: omnigent.policies.builtins.safety.max_tool_calls_per_session
    factory_params:
      limit: 200

4. Start the server.

omnigent server --config server_config.yaml

After starting, you can also add or remove policies at runtime through the REST API (see Admin policy REST API).


For agent developers

Adding policies to an agent spec

Policies are declared under policies at the top level of the agent YAML. They are evaluated in declaration order.

name: github_agent
prompt: You are a coding assistant with access to GitHub.

executor:
  harness: claude-sdk
  model: databricks-claude-sonnet-4-6

tools:
  github:
    type: mcp
    url: https://api.githubcopilot.com/mcp/

policies:
  limit_tool_calls:
    type: function
    handler: omnigent.policies.builtins.safety.max_tool_calls_per_session
    factory_params:
      limit: 100
  github_access:
    type: function
    handler: omnigent.policies.builtins.github.github_policy
    factory_params:
      write_repos:
        - myorg/my-repo
      write_branches:
        - "feature/*"
  google_policy:
    type: function
    handler: omnigent.policies.builtins.google.gdrive_policy
    factory_params:
      read_all: true
      allow_create: true

Policy declaration syntax

Each policy entry has:

Field Required Description
type yes "function"
handler yes Dotted Python import path to the callable or factory
factory_params no Key-value arguments passed to a factory at build time

Direct callable (no parameters):

approve_file_ops:
  type: function
  handler: omnigent.policies.builtins.safety.ask_on_os_tools

Factory (with parameters -- called once at build time to produce the evaluator):

rate_limit:
  type: function
  handler: omnigent.policies.builtins.safety.max_tool_calls_per_session
  factory_params:
    limit: 50

For session users

Adding policies to a running session

Session-level policies let you customize agent behavior for your current task. There are two ways to add them:

  1. UI -- Open the information window to browse available policies and toggle them on or off.
  2. Chat -- Tell the agent directly, e.g. "add a policy that asks me before running shell commands". The agent has a built-in sys_add_policy tool and will configure the policy for you.

Session policies evaluate before agent spec and admin policies, so they can enforce stricter rules or add additional gates for your specific workflow.


Builtin policies

Safety

max_tool_calls_per_session

Limits the total number of tool calls in a session. DENYs after the limit is reached.

Parameter Type Default Description
limit integer 100 Maximum tool calls allowed
rate_limit:
  type: function
  handler: omnigent.policies.builtins.safety.max_tool_calls_per_session
  factory_params:
    limit: 50

ask_on_os_tools

Requires user approval before any sys_os_read, sys_os_write, sys_os_edit, or sys_os_shell tool call. No parameters (direct callable).

approve_file_ops:
  type: function
  handler: omnigent.policies.builtins.safety.ask_on_os_tools

block_skills

Prevents the agent from loading specific skills.

Parameter Type Default Description
blocked string[] (required) Skill names to block (case-insensitive)
no_deploy_skill:
  type: function
  handler: omnigent.policies.builtins.safety.block_skills
  factory_params:
    blocked: [deploy, rollback]

enforce_sandbox

Forces a specific sandbox configuration on agent start.

Parameter Type Default Description
sandbox_type string "linux_bwrap" Sandbox backend (linux_bwrap, darwin_seatbelt, none)
allow_network boolean true Allow network access
write_paths string[] null Writable paths (null inherits agent config)
read_paths string[] null Read-only paths (null inherits agent config)
env_passthrough string[] null Env vars allowed through sandbox

deny_pii_in_llm_request

Scans user messages and LLM prompts for PII patterns (SSN, credit card, email, phone).

Parameter Type Default Description
pii_types string[] ["ssn", "credit_card", "email", "phone"] PII categories to scan
action string "DENY" Action when PII detected (DENY or ASK)

Cost

cost_budget

Gates a session on cumulative LLM spend, at the request phase (before the LLM turn, so text-only turns are budgeted too) and the tool-call phase. ASKs the first time spend crosses each soft warning threshold. At the hard limit it acts as a downgrade gate, not a hard stop: it DENYs (the whole turn on request, or each tool call on tool_call) only while the session is on an expensive model -- telling the user to switch to a cheaper one with /model -- and allows them again once the session has switched.

Parameter Type Default Description
max_cost_usd number (required) Hard spend limit in USD. Once reached, the turn / tool calls are blocked while the session is on an expensive model.
ask_thresholds_usd number[] null Soft warning checkpoints that ASK the first time spend crosses each (each must be < max_cost_usd)
expensive_models string[] Fable + Opus + GPT-5 (excl. -mini/-nano) Case-insensitive substring tokens for the model tiers blocked once over budget (e.g. "opus" matches any Opus deployment). The default's broad gpt-5 token matches the whole GPT-5 family except the cheap -mini/-nano variants; an explicit list is matched literally with no exclusions. [] disables the hard limit, leaving only the soft thresholds.
budget:
  type: function
  handler: omnigent.policies.builtins.cost.cost_budget
  factory_params:
    max_cost_usd: 5.00
    ask_thresholds_usd: [1.00, 3.00]
    expensive_models: ["opus", "gpt-5"]

user_daily_cost_budget

Same ASK / downgrade-gate behavior as cost_budget, but the budget is the session owner's cumulative spend across all their sessions for the current UTC day. The soft-threshold approval is remembered per user+day, so an approved checkpoint won't re-prompt that user again today -- even from a different session. Useful as a server-wide per-user daily cap.

Parameter Type Default Description
max_cost_usd number (required) Hard daily limit in USD. Once the owner's spend for the day reaches it, the turn / tool calls are blocked while on an expensive model.
ask_thresholds_usd number[] null Soft daily warning checkpoints that ASK the first time the owner's daily spend crosses each (each must be < max_cost_usd)
expensive_models string[] Fable + Opus + GPT-5 (excl. -mini/-nano) Case-insensitive substring tokens for the model tiers blocked once over the daily budget. An explicit list is matched literally with no exclusions. [] disables the hard limit, leaving only the soft thresholds.
# server_config.yaml -- a per-user daily cap applied to every session
daily_budget:
  type: function
  handler: omnigent.policies.builtins.cost.user_daily_cost_budget
  factory_params:
    max_cost_usd: 25.00
    ask_thresholds_usd: [10.00, 20.00]

GitHub

github_policy

Controls GitHub access across MCP tools and git/gh shell commands. Restricts reads to an allowlist (unless read_all) and writes to specific repos/branches. Shell commands with ambiguous targets return ASK.

Parameter Type Default Description
read_all boolean true Allow all reads
read_repos string[] [] Repos readable when read_all is false (owner/repo or URLs)
write_repos string[] [] Repos the agent may write to
write_branches string[] [] Branches writable within allowed repos (empty = any)
mcp_tool_prefixes string[] ["mcp__github__", "github__"] Tool-name prefixes to match
shell_tools string[] ["sys_os_shell"] Shell tools whose commands are parsed for git/gh
github_access:
  type: function
  handler: omnigent.policies.builtins.github.github_policy
  factory_params:
    write_repos:
      - myorg/frontend
      - myorg/backend
    write_branches:
      - "feature/*"
      - "fix/*"

Google Workspace

gdrive_policy

Controls Google Drive / Docs / Sheets / Slides access. Writes are restricted to files the agent created in the current session plus explicitly allowed files.

Parameter Type Default Description
read_all boolean true Allow all reads
read_files string[] [] File IDs or URLs readable when read_all is false
allow_create boolean false Allow creating new files
write_files string[] [] File IDs or URLs always writable
comment_files string[] [] File IDs or URLs the agent may comment on
tool_prefixes string[] ["mcp__google__", "google__"] Tool-name prefixes to match

gmail_policy

Controls Gmail access. Defaults to read + draft but no send.

Parameter Type Default Description
allow_read boolean true Allow reading mail
allow_send boolean false Allow sending mail
allow_drafts boolean true Allow creating/editing own drafts
allow_modify boolean false Allow modifying messages (labels, trash)
tool_prefixes string[] ["mcp__google__", "google__"] Tool-name prefixes

gcalendar_policy

Controls Google Calendar access. Defaults to read-only.

Parameter Type Default Description
allow_read boolean true Allow reading calendars/events
allow_create_events boolean false Allow creating events
allow_modify_events boolean false Allow updating/deleting events
tool_prefixes string[] ["mcp__google__", "google__"] Tool-name prefixes

Working directory

block_working_dir_changes

Blocks shell commands that change the working directory (cd, pushd, git -C) or manage git worktrees. Parses chained and wrapped commands to prevent trivial bypasses.

Parameter Type Default Description
block_cd boolean true Block cd/chdir/pushd/popd and git -C
block_worktree boolean true Block git worktree add/move/remove
allowed_dirs string[] [] Directories cd may move into (including subdirectories)
action string "deny" Verdict for gated commands (deny or ask)
shell_tools string[] ["sys_os_shell"] Shell tools to parse

Risk score

risk_score_policy

Accrues a per-session risk score from tool calls and sensitive data labels. Escalates guarded tools to ASK or DENY once the score exceeds a threshold.

Parameter Type Default Description
threshold integer 50 Score at which guarded tools escalate
tool_points object {} Tool name to points mapping, e.g. {"web_search": 10}
sensitive_labels object {} Data-classification label to points, e.g. {"Highly Confidential": 30}
guarded_tools string[] [] Tools gated once score reaches threshold
escalate_action string "ASK" Verdict for guarded tools over threshold
initial_scores_by_actor object {} Actor email to starting score offset
state_key string "risk_score" Session state key for the running score

Routing

deny_trivial_to_expensive_model

Classifies user messages as TRIVIAL or COMPLEX using the server LLM. Denies trivial tasks from using expensive models. Requires the server to have an llm: config block.

Parameter Type Default Description
expensive_models string[] (required) Model IDs to gate, e.g. ["databricks-claude-opus-4-6"]
classification_prompt string (builtin default) System instructions for the classifier
# server_config.yaml
llm:
  model: databricks-gpt-5-4-mini

policies:
  deny_trivial_opus:
    type: function
    handler: omnigent.policies.builtins.routing.deny_trivial_to_expensive_model
    factory_params:
      expensive_models:
        - databricks-claude-opus-4-6

Writing custom policies

Policy function interface

A policy function receives an event dict and returns a response dict (or None to abstain).

from omnigent.policies.schema import PolicyEvent, PolicyResponse

def my_policy(event: PolicyEvent) -> PolicyResponse | None:
    if event["type"] != "tool_call":
        return None  # abstain on non-tool phases
    tool = event["data"]["name"]
    if tool == "dangerous_tool":
        return {"result": "DENY", "reason": "This tool is blocked."}
    return {"result": "ALLOW"}

Factory form

For policies that need configuration, write a factory -- a function that accepts parameters and returns the actual evaluator:

def block_domains(blocked_domains: list[str]) -> callable:
    blocked = frozenset(d.lower() for d in blocked_domains)

    def evaluate(event: PolicyEvent) -> PolicyResponse | None:
        if event["type"] != "tool_call" or event["target"] != "web_fetch":
            return None
        url = event["data"]["arguments"].get("url", "")
        for domain in blocked:
            if domain in url.lower():
                return {"result": "DENY", "reason": f"Domain {domain} is blocked."}
        return {"result": "ALLOW"}

    return evaluate

Event and response examples

Event dict passed to the policy callable (example for a tool_call phase):

{
  "type": "tool_call",
  "target": "sys_os_shell",
  "data": {
    "name": "sys_os_shell",
    "arguments": {"command": "rm -rf /tmp/data"}
  },
  "context": {
    "actor": {"run_as": "alice@example.com", "client_id": "oauth_abc"},
    "usage": {
      "input_tokens": 1520,
      "output_tokens": 340,
      "total_tokens": 1860,
      "total_cost_usd": 0.012
    }
  },
  "session_state": {"call_count": 5},
  "request_data": null
}

Response dict returned by the policy callable:

{
  "result": "DENY",
  "reason": "Destructive shell command blocked.",
  "state_updates": [
    {"key": "call_count", "action": "increment", "value": 1}
  ]
}

result is the only required field. Valid values: "ALLOW", "DENY", "ASK". Return None to abstain.

state_updates supports four actions: "set", "increment", "delete", "append".

Making policies discoverable

To make custom policies appear in the UI, export a POLICY_REGISTRY list from your module:

# myorg/policies.py

POLICY_REGISTRY = [
    {
        "handler": "myorg.policies.block_domains",
        "kind": "factory",
        "name": "Block Domains",
        "description": "Block web access to specific domains.",
        "params_schema": {
            "type": "object",
            "properties": {
                "blocked_domains": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Domains to block"
                }
            },
            "required": ["blocked_domains"]
        }
    }
]

Then register the module in the server config:

policy_modules:
  - myorg.policies

Appendix: Admin policy REST API

After starting the server, admins can manage default policies at runtime through these endpoints:

# Create a server-wide policy
curl -X POST http://localhost:6767/v1/policies \
  -H "Content-Type: application/json" \
  -d '{
    "name": "global_rate_limit",
    "type": "python",
    "handler": "omnigent.policies.builtins.safety.max_tool_calls_per_session",
    "factory_params": {"limit": 200}
  }'
Method Endpoint Description
POST /v1/policies Create a default policy
GET /v1/policies List all default policies
GET /v1/policies/{policy_id} Get a specific policy
PATCH /v1/policies/{policy_id} Update (name, handler, enabled)
DELETE /v1/policies/{policy_id} Remove a policy

GET /v1/policy-registry lists all discoverable policies with parameter schemas -- useful for building admin UIs.