Config and permission overlays (multi-layer merge)
Permission and role behavior come from merging multiple config layers, not from a single file. Later layers override earlier ones; explicit permission overrides rules folded from legacy tools boolean maps. A single prompt can still narrow the tool set further; final closure happens at resolveTools and permission asks.
Merge order (later overrides earlier)
Section titled “Merge order (later overrides earlier)”Roughly low to high:
flowchart TD R[Remote / global config dir] --> F[Config file path override] F --> P[Project agentforge config] P --> A[In-dir agent / command scan] A --> C[Config content env vars] C --> H[Managed / account config] H --> M[Managed policy highest]Project side prefers .agentforge/ and agentforge.json(c); if disk only has a legacy directory layout, old paths remain readable so existing config is not split. Env vars use the AGENTFORGE_* family (including config path and content overrides).
tools and permission
Section titled “tools and permission”| Mechanism | Meaning |
|---|---|
permission | allow / deny / ask, optionally with path or command patterns |
tools (legacy) | Boolean switches, folded into permission at load |
Within one role: convert tools into permission first, then merge with explicit permission; explicit wins. Example: tools.bash=true and permission.bash=ask → ask. Global tools likewise fold into global permission. Runtime may also force overlays via permission-related env vars.
Example: project denies global allow
Section titled “Example: project denies global allow”Global permission.bash=allow, project permission.bash=deny → project loads later → deny.
Example: pattern-level rules
Section titled “Example: pattern-level rules”A rule value is either a single action or a map of “pattern → action.” A string means pattern: "*"; each key inside a nested object becomes one rule, matched by wildcard, and the last matching rule for a permission wins.
{ "permission": { // reads always allowed "read": "allow", // writes denied by default, only this project’s reports directory is open "edit": { "reports/**": "allow", "*": "deny" }, // shell auto-allows read-only commands, asks for everything else "bash": { "git status": "allow", "git diff *": "allow", "*": "ask" }, // no subtasks "task": "deny" }}Example: narrowing a single request
Section titled “Example: narrowing a single request”Config only supplies defaults. Within one session, a request can pass tools: { bash: false }, which folds into a deny("*") for bash, so the model sees no shell this turn; the next request without that field restores it.
Where deny and ask land on the chain
Section titled “Where deny and ask land on the chain”| Rule | Can the model see the tool | Behavior at execute time |
|---|---|---|
deny with pattern * | No; removed from this turn’s tool table | Never called |
deny with a specific pattern (for example one directory) | Yes | Blocked when a denied path is hit, with the reason written back |
ask | Yes | A question goes out before execution; work continues after a decision |
So “the model cannot see a tool” and “the tool got rejected” are different symptoms: the first is resolveTools plus whole-tool deny, the second is execute-time rule matching and the ask flow.
Agents and default roles
Section titled “Agents and default roles”default_agent: primary role when none is specifiedagent.{name}: model, prompts, step limits, permission, variants, and related fields- Legacy
mode.*merges into same-named primary roles; mode doc scan results also merge into the agent table
Config only supplies defaults. Per-prompt tools input, session-level permission, and execute-time Permission.ask keep closing at runtime. Structured output does not appear from natural-language field descriptions; the API must pass format explicitly. See Processor and LLM adaptation.
Relation to the execute chain
Section titled “Relation to the execute chain”flowchart LR Cfg[Merged permission] --> RT[resolveTools] In[Per-call input.tools] --> RT RT --> LLM[Tools visible to model] LLM --> Exec[execute] Exec --> Ask[ask / deny / allow]Check: after changing project config, confirm the instance reloaded for that directory; if files change without reload, the process may still hold the old merge. See Instance boundaries and concurrency.
Related reading: