Move 'Tool registry: zero built-in tools' to doc/resources/tool-registry.md
This commit is contained in:
parent
1e0e532183
commit
36822717a1
108
README.md
108
README.md
|
|
@ -120,113 +120,7 @@ filesystem, see [`doc/resources/9p.md`](doc/resources/9p.md).
|
|||
|
||||
## Tool registry: zero built-in tools
|
||||
|
||||
**No tools are compiled into the Go binary.** Every tool — including `shell` and `reasoning_think` — is an external executable loaded dynamically through the 9P filesystem. Tool definitions, schemas, and execution logic live entirely in the filesystem.
|
||||
|
||||
### Loading tools
|
||||
|
||||
Tools are loaded by writing their name to `session/{sname}/agent/{aname}/tools`:
|
||||
|
||||
```bash
|
||||
# List currently loaded tools
|
||||
ollie-9p read session/{sname}/agent/{aname}/tools
|
||||
|
||||
# Load a tool — becomes a native callable function
|
||||
echo file_glob | ollie-9p write session/{sname}/agent/{aname}/tools
|
||||
|
||||
# Global catalog: all discoverable tools on disk
|
||||
ollie-9p read tools
|
||||
```
|
||||
|
||||
The `tool_load` tool is itself a bash script that does exactly this — it wraps the 9P write in a callable function so the LLM can load tools during a turn.
|
||||
|
||||
### Agent autoLoad configuration
|
||||
|
||||
Agent configs declare which tools to load at startup via `autoLoad`:
|
||||
|
||||
```json
|
||||
{
|
||||
"autoLoad": ["file_read", "file_edit", "file_glob", "file_grep", "file_write", "tool_load"]
|
||||
}
|
||||
```
|
||||
|
||||
Each agent profile (default, copilot, explorer, librarian, navigator, taskmanager, theo) has its own `autoLoad` list. The `allowTools` field provides a secondary security gate.
|
||||
|
||||
### Declarative registration
|
||||
|
||||
Tools are executables in `~/.config/ollie/tools/`. Each one has a `.meta` sidecar
|
||||
file declaring its JSON schema, documentation, concurrency class, tier, and
|
||||
privilege requirements. The registry reads `.meta` files — it never inspects the
|
||||
executable itself.
|
||||
|
||||
This is **declarative tool registration**: write a `.meta` file, drop an
|
||||
executable in `$OLLIE_TOOLS_PATH`, and the tool is live. No server restart,
|
||||
no config file, no code change.
|
||||
|
||||
The `.meta` file (`file_glob.meta`):
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Find files by glob pattern, sorted by mtime (newest first).",
|
||||
"args": {"type":"object","properties":{"pattern":{"type":"string"}},"required":["pattern"]},
|
||||
"tier": "cold",
|
||||
"readOnly": true
|
||||
}
|
||||
```
|
||||
|
||||
Everything is sandboxed by default via Landlock. Tools that need to escape
|
||||
request elevation explicitly (`"elevated": true`).
|
||||
|
||||
### Privileged tools: `sudo`
|
||||
|
||||
Tools that need root privileges declare `"sudo": true` in their `.meta`. The
|
||||
dispatch chain becomes a two-gate sequence: elevation approval (escape sandbox)
|
||||
→ credential prompt → `sudo -S` execution. The tool itself has no knowledge of
|
||||
sudo — the privilege wrapping is entirely in the dispatch layer.
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Read system logs (dmesg).",
|
||||
"sudo": true,
|
||||
"cmd": "system_logs_dmesg",
|
||||
"args": {"type":"object","properties":{"lines":{"type":"string"}}},
|
||||
"readOnly": true
|
||||
}
|
||||
```
|
||||
|
||||
Credentials are prompted via `kdialog`/`zenity` on desktop, or forwarded over
|
||||
SSH for remote execution. See [`doc/resources/writing-tools.md`](doc/resources/writing-tools.md) for
|
||||
details.
|
||||
|
||||
### Host-conditional variants
|
||||
|
||||
A single `.meta` file can describe a tool that works differently on different
|
||||
hosts. Variants are gated by match conditions (`binary`, `file`, `os`, `arch`,
|
||||
`env`, `nenv`). The first matching variant determines the tool's schema,
|
||||
documentation, and executable. If no variant matches, the tool is hidden from
|
||||
the registry — it doesn't exist on this host.
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Read system logs.",
|
||||
"variants": [
|
||||
{
|
||||
"match": {"binary": "journalctl"},
|
||||
"cmd": "system_logs_journald",
|
||||
"args": {"type":"object","properties":{"unit":{"type":"string"},"since":{"type":"string"}}}
|
||||
},
|
||||
{
|
||||
"match": {"file": "/var/log/syslog"},
|
||||
"cmd": "system_logs_syslog",
|
||||
"args": {"type":"object","properties":{"lines":{"type":"string"}}}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
On a systemd host the model sees unit filtering and time ranges. On Alpine it
|
||||
sees tail + grep. On a host with neither, the tool doesn't appear. One `.meta`
|
||||
deploys everywhere. See [`doc/resources/writing-tools.md`](doc/resources/writing-tools.md) for the
|
||||
full variant specification.
|
||||
**No tools are compiled into the Go binary.** Everything is an external executable loaded dynamically through the 9P filesystem. See [`doc/resources/tool-registry.md`](doc/resources/tool-registry.md) for the full details — loading, declarative registration, autoLoad, privileged tools, and host-conditional variants.
|
||||
|
||||
## What you can do
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
## Tool registry: zero built-in tools
|
||||
|
||||
**No tools are compiled into the Go binary.** Every tool — including `shell` and `reasoning_think` — is an external executable loaded dynamically through the 9P filesystem. Tool definitions, schemas, and execution logic live entirely in the filesystem.
|
||||
|
||||
### Loading tools
|
||||
|
||||
Tools are loaded by writing their name to `session/{sname}/agent/{aname}/tools`:
|
||||
|
||||
```bash
|
||||
# List currently loaded tools
|
||||
ollie-9p read session/{sname}/agent/{aname}/tools
|
||||
|
||||
# Load a tool — becomes a native callable function
|
||||
echo file_glob | ollie-9p write session/{sname}/agent/{aname}/tools
|
||||
|
||||
# Global catalog: all discoverable tools on disk
|
||||
ollie-9p read tools
|
||||
```
|
||||
|
||||
The `tool_load` tool is itself a bash script that does exactly this — it wraps the 9P write in a callable function so the LLM can load tools during a turn.
|
||||
|
||||
### Agent autoLoad configuration
|
||||
|
||||
Agent configs declare which tools to load at startup via `autoLoad`:
|
||||
|
||||
```json
|
||||
{
|
||||
"autoLoad": ["file_read", "file_edit", "file_glob", "file_grep", "file_write", "tool_load"]
|
||||
}
|
||||
```
|
||||
|
||||
Each agent profile (default, copilot, explorer, librarian, navigator, taskmanager, theo) has its own `autoLoad` list. The `allowTools` field provides a secondary security gate.
|
||||
|
||||
### Declarative registration
|
||||
|
||||
Tools are executables in `~/.config/ollie/tools/`. Each one has a `.meta` sidecar
|
||||
file declaring its JSON schema, documentation, concurrency class, tier, and
|
||||
privilege requirements. The registry reads `.meta` files — it never inspects the
|
||||
executable itself.
|
||||
|
||||
This is **declarative tool registration**: write a `.meta` file, drop an
|
||||
executable in `$OLLIE_TOOLS_PATH`, and the tool is live. No server restart,
|
||||
no config file, no code change.
|
||||
|
||||
The `.meta` file (`file_glob.meta`):
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Find files by glob pattern, sorted by mtime (newest first).",
|
||||
"args": {"type":"object","properties":{"pattern":{"type":"string"}},"required":["pattern"]},
|
||||
"tier": "cold",
|
||||
"readOnly": true
|
||||
}
|
||||
```
|
||||
|
||||
Everything is sandboxed by default via Landlock. Tools that need to escape
|
||||
request elevation explicitly (`"elevated": true`).
|
||||
|
||||
### Privileged tools: `sudo`
|
||||
|
||||
Tools that need root privileges declare `"sudo": true` in their `.meta`. The
|
||||
dispatch chain becomes a two-gate sequence: elevation approval (escape sandbox)
|
||||
→ credential prompt → `sudo -S` execution. The tool itself has no knowledge of
|
||||
sudo — the privilege wrapping is entirely in the dispatch layer.
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Read system logs (dmesg).",
|
||||
"sudo": true,
|
||||
"cmd": "system_logs_dmesg",
|
||||
"args": {"type":"object","properties":{"lines":{"type":"string"}}},
|
||||
"readOnly": true
|
||||
}
|
||||
```
|
||||
|
||||
Credentials are prompted via `kdialog`/`zenity` on desktop, or forwarded over
|
||||
SSH for remote execution. See [`doc/resources/writing-tools.md`](doc/resources/writing-tools.md) for
|
||||
details.
|
||||
|
||||
### Host-conditional variants
|
||||
|
||||
A single `.meta` file can describe a tool that works differently on different
|
||||
hosts. Variants are gated by match conditions (`binary`, `file`, `os`, `arch`,
|
||||
`env`, `nenv`). The first matching variant determines the tool's schema,
|
||||
documentation, and executable. If no variant matches, the tool is hidden from
|
||||
the registry — it doesn't exist on this host.
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "Read system logs.",
|
||||
"variants": [
|
||||
{
|
||||
"match": {"binary": "journalctl"},
|
||||
"cmd": "system_logs_journald",
|
||||
"args": {"type":"object","properties":{"unit":{"type":"string"},"since":{"type":"string"}}}
|
||||
},
|
||||
{
|
||||
"match": {"file": "/var/log/syslog"},
|
||||
"cmd": "system_logs_syslog",
|
||||
"args": {"type":"object","properties":{"lines":{"type":"string"}}}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
On a systemd host the model sees unit filtering and time ranges. On Alpine it
|
||||
sees tail + grep. On a host with neither, the tool doesn't appear. One `.meta`
|
||||
deploys everywhere. See [`doc/resources/writing-tools.md`](doc/resources/writing-tools.md) for the
|
||||
full variant specification.
|
||||
Loading…
Reference in New Issue