Permission tiers

Every agent session operates in one of three tiers:

TierCapabilities
AutonomousAll capabilities — intended for sandboxed environments
SupervisedChat, memory, tools. Every tool call requires approval — an auto-approve rule, a supervisor LLM decision, or a human — before it runs. Self-modification actions (create skills, modify schedules, update USER.md) are one case of this, not the whole story
RestrictedChat and read-only tools only. No memory writes beyond MEMORY.md

Set the default tier globally:

[session]
tier = "supervised"

Or per-agent:

[[agents]]
name = "home-automation"
session_tier = "restricted"

Approval workflows

In supervised mode, every tool call — not just self-modification actions like skill creation or schedule modification — produces an approval request unless an auto-approve rule or supervisor already resolved it. The user is notified via Telegram (inline keyboard with Approve/Deny buttons) or the REST API.

Approval requests have a 24-hour TTL. Unapproved requests expire automatically.

To stop being asked about tools you trust — or to have a second LLM triage approvals while you are asleep — see Supervisors & Auto-Approval.

Approval via Telegram

When the agent wants to create a skill, the user sees a message with inline buttons:

Approval required: Create skill “weather-check”

[Approve] [Deny]

Approval via API

# List pending approvals
curl -H "Authorization: Bearer dk_..." https://localhost:8080/api/v1/approvals

# Approve
curl -X POST -H "Authorization: Bearer dk_..." \
  https://localhost:8080/api/v1/approvals/{id}/approve

Cost tracking

Denkeeper tracks LLM costs per session and globally. Two limits apply, both in USD:

[llm]
cost_limit_soft = 0.5   # warn but continue
cost_limit_hard = 1.0   # stop generation

Either can be overridden per agent:

[[agents]]
name = "research"
cost_limit_soft = 2.0
cost_limit_hard = 5.0

When the hard limit is reached, the agent refuses further LLM calls for that session. Fallback strategies can automatically switch to cheaper models before that happens — a [[llm.fallback]] rule with trigger = "cost_limit" fires on whichever limit its scope names ("soft" or "hard").

The older max_cost_per_session key is deprecated. It still loads — it is migrated to cost_limit_hard at startup — but new configs should use the two-limit form.