jeevesagent.security.permissions
================================

.. py:module:: jeevesagent.security.permissions

.. autoapi-nested-parse::

   Permission decisions for tool calls.

   Three modes mirror the Claude Agent SDK so users don't relearn:

   - ``DEFAULT`` — allow non-destructive tools, ask on destructive
   - ``ACCEPT_EDITS`` — auto-approve filesystem writes; otherwise like default
   - ``BYPASS`` — allow everything (CI / sandbox use only)

   Allow- and deny-lists win over modes; deny-list wins over allow-list.
   The decision flow:

       1. Tool in deny-list → deny
       2. Allow-list set and tool not in it → deny
       3. Mode == BYPASS → allow
       4. Mode == ACCEPT_EDITS and call is a non-destructive edit → allow
       5. Tool is destructive → ask
       6. Otherwise → allow



Classes
-------

.. autoapisummary::

   jeevesagent.security.permissions.AllowAll
   jeevesagent.security.permissions.Mode
   jeevesagent.security.permissions.PerUserPermissions
   jeevesagent.security.permissions.StandardPermissions


Module Contents
---------------

.. py:class:: AllowAll

   Trivial permission policy: every call is allowed.

   The default for :class:`Agent` when no permissions are configured.


   .. py:method:: check(call: jeevesagent.core.types.ToolCall, *, context: collections.abc.Mapping[str, Any], user_id: str | None = None) -> jeevesagent.core.types.PermissionDecision
      :async:



.. py:class:: Mode

   Bases: :py:obj:`enum.StrEnum`


   Enum where members are also (and must be) strings

   Initialize self.  See help(type(self)) for accurate signature.


   .. py:attribute:: ACCEPT_EDITS
      :value: 'acceptEdits'



   .. py:attribute:: BYPASS
      :value: 'bypassPermissions'



   .. py:attribute:: DEFAULT
      :value: 'default'



.. py:class:: PerUserPermissions(*, policies: collections.abc.Mapping[str | None, Any], default: Any)

   Map ``user_id`` to a different permission policy.

   The common multi-tenant shape: admins get one policy, paid
   users get another, free users get a third. Construct with a
   mapping of ``user_id -> Permissions`` plus a ``default``
   fallback for unmapped users (including ``None``)::

       from jeevesagent import (
           PerUserPermissions, StandardPermissions, Mode,
       )

       permissions = PerUserPermissions(
           policies={
               "admin_alice": StandardPermissions(mode=Mode.BYPASS),
               "paid_user_42": StandardPermissions(
                   mode=Mode.ACCEPT_EDITS,
               ),
           },
           default=StandardPermissions(
               mode=Mode.DEFAULT,
               denied_tools=["bash", "delete_user"],
           ),
       )
       agent = Agent("...", permissions=permissions)

   Each ``check`` call routes to the policy keyed by ``user_id``
   (the live :class:`~jeevesagent.RunContext`'s value, threaded
   through by the agent loop). When no policy matches, the
   ``default`` decides — most apps want a strict default and add
   permissive policies for trusted users.


   .. py:method:: check(call: jeevesagent.core.types.ToolCall, *, context: collections.abc.Mapping[str, Any], user_id: str | None = None) -> jeevesagent.core.types.PermissionDecision
      :async:



.. py:class:: StandardPermissions(*, mode: Mode = Mode.DEFAULT, allowed_tools: list[str] | None = None, denied_tools: list[str] | None = None)

   Mode + allow/deny-list permission policy.


   .. py:method:: check(call: jeevesagent.core.types.ToolCall, *, context: collections.abc.Mapping[str, Any], user_id: str | None = None) -> jeevesagent.core.types.PermissionDecision
      :async:



   .. py:method:: strict() -> StandardPermissions
      :classmethod:


      Convenience: default-mode permissions with no overrides.



