This module provides pure capability-mask semantics for caller-provided agent tool universes.
Each entry identifies an integration or system scope. It selects :all, :read,
or :none. The entry can add explicit tool names. Callers build universes and
control credentials, persistence, and baselines.
Resolution fails closed for malformed stored entries. Use the resulting MapSet
to advertise tools and authorize dispatch.
Summary
Types
The per-scope default level when a mask has no entry for a scope.
A parsed mask entry. The credential_id and extra fields are optional.
A maskable scope with a read or write classification for each tool.
A scope identity with kind and scope fields. It does not contain tools.
Selected tool names for each scope. Editors use this type as working state.
Functions
Renders a parsed mask to the string-keyed stored representation.
Folds a per-scope selection into the most permissive equivalent mask.
Canonicalizes a mask and drops entries equal to their caller-supplied baseline.
Parses the string-keyed stored representation into the atom-keyed policy shape.
Returns the allowed tool names over universe.
Validates that each mask entry is well-formed and belongs to universe.
Types
The per-scope default level when a mask has no entry for a scope.
@type entry() :: %{ :kind => kind(), :scope => String.t(), :level => level(), optional(:credential_id) => String.t() | nil, optional(:extra) => [String.t()] }
A parsed mask entry. The credential_id and extra fields are optional.
@type kind() :: :integration | :system
@type level() :: :all | :read | :none
@type scope() :: %{ kind: kind(), scope: String.t(), tools: [%{name: String.t(), read: boolean()}] }
A maskable scope with a read or write classification for each tool.
A scope identity with kind and scope fields. It does not contain tools.
@type selection() :: [ %{ :kind => kind(), :scope => String.t(), :tools => [String.t()], optional(:credential_id) => String.t() | nil } ]
Selected tool names for each scope. Editors use this type as working state.
@type tool_access() :: [entry()]
@type universe() :: [scope()]
Functions
@spec dump(tool_access()) :: [map()]
Renders a parsed mask to the string-keyed stored representation.
@spec fold(selection(), universe()) :: tool_access()
Folds a per-scope selection into the most permissive equivalent mask.
A full scope becomes :all. All reads with optional writes become :read. Other
selections become :none with sorted extra values.
@spec normalize(tool_access(), universe(), baseline()) :: tool_access()
Canonicalizes a mask and drops entries equal to their caller-supplied baseline.
normalize/3 never removes a credential-bearing entry because it matches the baseline.
@spec parse([map()] | tool_access()) :: tool_access()
Parses the string-keyed stored representation into the atom-keyed policy shape.
Unknown kinds and levels become the :__invalid__ sentinel without creating
atoms. Non-map entries also become an invalid sentinel. resolve/3 treats these
sentinels as denials. validate/2 reports them.
@spec resolve(tool_access(), universe(), baseline()) :: MapSet.t(String.t())
Returns the allowed tool names over universe.
Each scope resolves independently through its matching entry or the caller's
baseline. The union is the MapSet to use for advertisement and dispatch.
@spec validate(tool_access(), universe()) :: :ok | {:error, String.t()}
Validates that each mask entry is well-formed and belongs to universe.