Skip to content

feat(policies): admin gates for permission bypass, modes, and models - #4029

Open
yaoharry wants to merge 1 commit into
omnigent-ai:mainfrom
yaoharry:harry-yao/admin-policy-gates
Open

feat(policies): admin gates for permission bypass, modes, and models#4029
yaoharry wants to merge 1 commit into
omnigent-ai:mainfrom
yaoharry:harry-yao/admin-policy-gates

Conversation

@yaoharry

@yaoharry yaoharry commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Related issue

N/A

Summary

Three server-wide policy factories an operator attaches via default_policies to bound what a session may do, plus the event plumbing one of them needed.

  • deny_harness_permission_bypass — denies shell commands that launch a nested agent with --dangerously-skip-permissions, --dangerously-bypass-approvals-and-sandbox, or --yolo. An agent with shell access can otherwise start a second agent outside the approval path, escaping every other guardrail on the session in one call. This is a backstop for a real sandbox, not a replacement — a flag assembled at runtime still slips through.
  • deny_permissive_permission_mode — denies a session whose own harness was launched with prompting disabled. Denies bypassPermissions by default; acceptEdits still prompts for shell, so it is opt-in via denied_modes. Abstains when the mode is absent, since web / API sessions never stamp one and reading unstamped as bypassed would deny every non-native session.
  • restrict_models — allowlist / denylist by exact model id on llm_request. Complements routing.deny_trivial_to_expensive_model, which keeps an expensive model available for complex work; this removes it outright.

All three are registered in POLICY_REGISTRY, so they are browsable via GET /v1/policy-registry and their params are schema-validated on attach.

ELI5

An admin can already sandbox agents. These add three more knobs: don't let an agent spawn another agent with the safety prompts turned off, don't let a session run at all if its prompts are off, and restrict which models it may call.

Test Plan

  • tests/policies/builtins/test_safety_admin_gates.py — 59 tests across the
    three layers this repo's policy tests use: direct callable, spec resolution
    through resolve_function_policy (proving the DENY threads through the engine
    boundary, including that permission_mode survives the hop), and registry
    discovery + schema validation of good / bad params.
  • Full suite green: pytest tests/policies/ → 685 passed;
    + tests/test_claude_native_hook.py → 724 passed.
  • pyrefly check → 0 errors. ruff check / ruff format --check clean.
  • Verified end-to-end on a live server through Claude Code's own PreToolUse
    hook protocol: bypassPermissions / acceptEditspermissionDecision: "deny"; default / absent → allow.
pytest tests/policies/builtins/test_safety_admin_gates.py -q

Deployed omnigent locally and curled request to hit these policy checks

Demo

N/A — no UI change. The new gates surface in the existing policy-registry list.

Type of change

  • Bug fix
  • Feature
  • UI / frontend change
  • Refactor / chore
  • Docs
  • Test / CI
  • Breaking change

Test coverage

  • Unit tests added / updated
  • Integration tests added / updated
  • E2E tests added / updated
  • Manual verification completed
  • Existing tests cover this change
  • Not applicable

Coverage notes

Manual verification drove a live local server through Claude Code's real
PreToolUse hook protocol, which covers the one part unit tests can't reach on
their own: that a real harness actually stamps permission_mode on the
payload. The unit tests cover the gate logic and the EvaluationContext → event
hop; the live run confirms the upstream field is really there to be read.

Changelog

Admins can gate nested permission-bypass launches, sessions whose harness has approvals disabled, and which models a session may call

@github-actions github-actions Bot added the size/XL Pull request size: XL label Aug 4, 2026
@yaoharry
yaoharry force-pushed the harry-yao/admin-policy-gates branch from e6c2c5c to 681f304 Compare August 5, 2026 06:06
@yaoharry yaoharry changed the title Harry yao/admin policy gates feat(policies): admin gates for permission bypass, modes, and models Aug 5, 2026
@yaoharry
yaoharry marked this pull request as ready for review August 5, 2026 06:14
@github-actions
github-actions Bot requested a review from dbczumar August 5, 2026 06:14
@yaoharry
yaoharry force-pushed the harry-yao/admin-policy-gates branch from 681f304 to 05d2f54 Compare August 5, 2026 06:21
Three server-wide policy factories an operator attaches via default_policies to
bound what a session may do, plus the plumbing one of them needed.

- deny_harness_permission_bypass: denies shell commands launching a nested
  agent with --dangerously-skip-permissions,
  --dangerously-bypass-approvals-and-sandbox, or --yolo. An agent with shell
  access can otherwise start a second agent outside the approval path,
  escaping every guardrail on the session in one call. Backstop for a real
  sandbox, not a replacement: a flag assembled at runtime slips through.

- deny_permissive_permission_mode: denies a session whose own harness was
  launched with prompting disabled. The launch flag never reaches the policy
  layer, but Claude Code re-reports the resulting mode on every hook payload,
  where it was consumed only by the permission-request path. Thread it onto
  the evaluate event, mirroring how harness and model are already carried:

    hook payload.permission_mode
      -> event.context.permission_mode        (claude_native_hook)
      -> EvaluationContext.permission_mode    (_build_evaluation_context)
      -> event["context"]["permission_mode"]  (policies/function)

  Denies bypassPermissions by default; acceptEdits still prompts for shell so
  it is opt-in via denied_modes. Abstains when the mode is absent — web and
  API sessions never stamp one.

- restrict_models: allowlist/denylist by exact model id on llm_request.
  Complements routing.deny_trivial_to_expensive_model, which keeps an
  expensive model available for complex work; this removes it outright.

All three are registered in POLICY_REGISTRY, so they are browsable via
GET /v1/policy-registry and their params are schema-validated on attach. The
registry schema's advertised denied_modes default is asserted against the
callable's real default — the UI pre-fills that value, so a schema naming a
mode the callable does not deny would silently widen the gate for anyone who
accepts the form defaults.

Not included, deliberately: sandbox posture is already covered by the existing
safety.enforce_sandbox, which rewrites an agent's sandbox config on
sys_agent_start. Agent swarms and runaway loops are covered by
orchestration.spawn_bounds (max_dispatches_per_turn: 0 blocks them outright)
and safety.max_tool_calls_per_session / detect_loop. Fast mode and ultra think
are not gateable here — there is no session-launch policy phase and no harness
re-reports them, so that needs a launch-time phase or enforcement in
harness_startup_config.

60 tests across the three layers this repo's policy tests use (callable,
resolve_function_policy, registry discovery + schema).

Co-authored-by: Isaac
Signed-off-by: harry-yao_data <harry.yao@databricks.com>
@yaoharry
yaoharry force-pushed the harry-yao/admin-policy-gates branch from 05d2f54 to 8f3fcc5 Compare August 5, 2026 06:25
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

@yaoharry This PR is a Bug fix, Feature, or UI / frontend change but the Demo section is missing or only contains a placeholder.

These change types require a screenshot or screen recording so reviewers can see the new behaviour without checking out the branch. Please update the Demo section with:

  • A screenshot or screen recording of the change, or
  • A link to a hosted video or GIF showing the new behaviour.

Use N/A only when the change has no user-visible effect whatsoever (e.g. a pure refactor or test-only change). If that's the case, uncheck the relevant type box and check Refactor / chore or Test / CI instead.

@github-actions github-actions Bot added the needs-demo PR needs a demo screenshot or recording label Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-demo PR needs a demo screenshot or recording size/XL Pull request size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants