Tool system: registration, visibility, truncation, and permission gates
A tool the model can call is not “visible because an implementation exists in the warehouse.” Agent Forge wraps each tool into one contract: input validation, execute, output truncation; then registry filters, plugin definition rewrites, and session permission gates decide what enters this turn’s streamText tool table. Overlong tool output is truncated to protect context, complementary to session-level compaction.
Unified wrapper
Section titled “Unified wrapper”After definition/wrapping, every tool has:
- Input validation (schema / zod parse)
- Original execute logic
- Output truncation (unless metadata already marks truncation)
The execute context carries session and message ids, current role, abort signal, a permission-ask entry, and a channel to write back tool Part metadata.
Registration sources
Section titled “Registration sources”flowchart TD B[Builtin tool explicit list] --> All[Candidate set] C[Config-dir tool scripts] --> All P[Plugin-registered tools] --> All All --> Reg[registry filter]Builtins are an explicit list, not auto-registration by scanning a whole tool source tree. Custom tools come from tool scripts under the project config directory, plus plugin injection. Candidates can be many; only the filtered subset is sent to the model.
Three visibility layers
Section titled “Three visibility layers”flowchart LR All[Candidates] --> L1[By model / provider / flags] L1 --> L2[plugin tool.definition] L2 --> L3[resolveTools + permission] L3 --> LLM[Sent to model]Layer one trims by model and provider capability (for example some search tools only under certain config; some models use patch edit, others edit/write). Layer two lets plugins rewrite descriptions and parameter schemas. Layer three filters again by role, session, and per-input tool overlays, combined with permission rules (allow / deny / ask).
How layer three closes:
- Permission names map to tool names; edit-family tools (write, patch, and so on) all fold into one
editpermission in config. - A tool is removed from this turn’s table only when the rule pattern is
*and the action isdeny; a deny with a specific pattern rejects that call when it matches. askdoes not hide a tool; a question goes out before execution and work continues after a decision.- Per-request tool booleans fold into allow / deny and join this layer.
Mutating paths may also enter human gates and audit; when no rule matches, execution follows the diagram. Permission config merge: Config and permission overlays.
Output truncation
Section titled “Output truncation”When a tool returns a string that is too long: truncate the body against one shared limit, mark it truncated, and land the full output at a file path for later inspection. Goal: stop a single tool result from blowing the context window. Truncation is an immediate gate; compaction is cross-turn folding. Use both.
Limits and what the role changes
Section titled “Limits and what the role changes”The limits come from one shared policy. The role affects the follow-up guidance handed back to the model:
| Part | Rule |
|---|---|
| Shared limit | Lines and bytes are checked together, defaulting to 2000 lines / 50 KB; over the limit keeps one preview and states how much and in what unit was cut |
| Full output | Written to the truncation cache directory with the path in the result; that directory stays readable for the role unless explicitly denied |
| Follow-up hint | If the role can still dispatch subtasks, the hint sends the full file to an explore subagent so the main context is not flooded; if subtasks are denied, the hint switches to search or offset-based reads |
| Per-tool caps | The read tool truncates any line past 2000 characters; directory listing and search tools return the first 100 entries by default |
A tool that already handled truncation and set the metadata marker is not truncated again by the shared wrapper.
Examples
Section titled “Examples”bashprints tens of thousands of log lines: the body keeps a preview of the first 2000 lines (within 50 KB), and the full file lands in the truncation directory. With subtasks available, the hint tells the model to have an explore subagent search that file; with subtasks denied, it switches to search or offset reads.readopens a minified single-line JSON: that line is truncated at 2000 characters, and the model searches for specific fields instead.- A search matches too many rows: only the first 100 come back with a truncated note; the model should narrow the path or pattern rather than raise the cap.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Check first |
|---|---|
| Model cannot see a tool | Registry rules, plugin rewrite, resolveTools, permission deny |
| Arg validation fails | Tool schema vs model-generated args |
| Context still explodes | Whether truncation applied; whether compaction is still needed |
| Tool execute stuck on ask | Whether the permission ask flow reached SSE and a decision completed |
Related reading: