Skip to content

Agent permissions: team tools decide by their approved definition; denied tools are never offered - #466

Merged
nambok merged 1 commit into
mainfrom
live-tools-known
Nov 4, 2026
Merged

nambok merged 1 commit into
mainfrom
live-tools-known

Conversation

@nambok

@nambok nambok commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

On a self-hosted install the planner kept calling the team's own approved tools (tool_find_class and others) and every call was denied: "Nothing says what tool_find_class does, so it doesn't run. Unknown tools are always denied." The policy engine resolved risk only from a fixed built-in list, so a team's tools had none, yet the model was still offered them.

Team tools now get their risk from their approved definition, the same way built-ins do, and no agent is offered a tool the policy would refuse whatever the call says.

Changes

  • Tool families (agent_policy::registry): tools a team sets up (team tools tool_<name>, connection tools <server>__<tool>) register where they are offered, with the risk their definition gives. registry::known reads built-ins, then registrations, then a connection tool's name. Team tools: risk from the approved version's kind (ToolKind::risk, all four kinds are checked reads). Connection tools: the server's own hints (read only, destructive), else the name.
  • Plain refusals: tools_store::standing says why a team tool can't run (no approved version, switched off, changed since it was approved; "approve the new version in Settings, Tools"). The engine takes Known::Refused(reason) and denies with that reason.
  • Never offer what is always denied: Gate::offerable drops tools that are unknown, unrunnable, switched off for the agent or denied by a rule. Used by every worker model loop (agent::policy::offerable in provider::converse) and both chats (chat_policy::offer). Nothing is logged for the offer; attempted calls are still decided and logged.
  • Names for people: decision log lines carry policy.tool_label ("Find order"); /api/agents/activity fills it in for team tool events logged before this.
  • Test a call: the settings box reads a team tool's real standing.

Verification

  • cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, cargo test --workspace (common 1395, gateway 697, worker 1180): all pass.
  • New tests: an approved team tool runs as a read under the starting policy and is offered; a changed one is refused with "tool_find_order changed since it was approved..." and isn't offered; a never approved one says so; the worker's loop offers only read_file and the approved tool, and attempted calls to withheld tools are refused and logged; chats drop unknown tools; every_tool_family_has_a_risk_mapping and every_runtime_tool_name_belongs_to_a_family fail when a family or a runtime-built tool name has no risk.

…d denied tools are never offered

The planner was offered a team's approved tools (tool_find_order and the
like) but the policy engine had no risk for them, so every call was denied
as unknown and the model kept calling them.

- Tools a team sets up form families (team tools, connection tools). Each
  registers its tools where it offers them, with the risk its definition
  gives: a team tool from its approved version's kind (every kind is a
  checked read), a connection tool from the server's own hints, else its
  name. The engine reads built-ins, then registrations.
- A team tool with no runnable version stays denied with a plain reason:
  no approved version, switched off, or changed since it was approved.
- Every model loop (runs, plan chat, Chat) drops tools the policy denies
  whatever the call says before the model sees them; attempted calls are
  still decided and logged.
- Decision log lines carry the tool's name for people ("Find order"), and
  the activity endpoint fills it in for team tools logged before.
- The settings "test a call" box reads a team tool's standing.
- Tests: a team tool reads under the default policy, a changed one is
  denied with the reason, denied tools aren't offered (gate, worker, chat),
  and every runtime tool name belongs to a family with a risk mapping.
@nambok
nambok merged commit 7b615d8 into main Nov 4, 2026
10 checks passed
@nambok
nambok deleted the live-tools-known branch November 4, 2026 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant