From 36822717a187841d42f4ac21a463ee3d640c54da Mon Sep 17 00:00:00 2001 From: Ollie Agent Date: Wed, 5 Aug 2026 19:10:08 +0200 Subject: [PATCH] Move 'Tool registry: zero built-in tools' to doc/resources/tool-registry.md --- README.md | 108 +------------------------------- doc/resources/tool-registry.md | 109 +++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 107 deletions(-) create mode 100644 doc/resources/tool-registry.md diff --git a/README.md b/README.md index 2dd1491..a59a694 100644 --- a/README.md +++ b/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 diff --git a/doc/resources/tool-registry.md b/doc/resources/tool-registry.md new file mode 100644 index 0000000..f769814 --- /dev/null +++ b/doc/resources/tool-registry.md @@ -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. \ No newline at end of file