From d292f2aa5911d03a43720940ad81a38cae093795 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Mon, 10 Aug 2026 20:37:37 +0200 Subject: [PATCH] feat: make tool_load a built-in tool tool_load must be handled specially by the agent runtime because tools run inside toolsrv's sandbox and cannot load other tools into themselves. The runtime now intercepts tool_load calls and directly invokes ToolServer.LoadTool(). This is the only built-in tool - all others are external scripts. --- cmd/olliesrv/internal/agent/loop.go | 25 +++++++++++++++++++++++++ data/tools/tool_load | 8 ++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/cmd/olliesrv/internal/agent/loop.go b/cmd/olliesrv/internal/agent/loop.go index 112a04b..c554bd2 100644 --- a/cmd/olliesrv/internal/agent/loop.go +++ b/cmd/olliesrv/internal/agent/loop.go @@ -482,6 +482,31 @@ func execOne(rt *Runtime, ctx TurnCtx, call backend.ToolCall, resultCache *sync. var resultBlocks []backend.ContentBlock var isErr bool + // Built-in: tool_load - must be handled specially because tools run inside + // toolsrv and can't load other tools into themselves. + if call.Name == "tool_load" { + var args struct { + Name string `json:"name"` + } + if err := json.Unmarshal(call.Arguments, &args); err != nil { + result = "error: " + err.Error() + isErr = true + } else if args.Name == "" { + result = "error: name is required" + isErr = true + } else if rt.ToolServer == nil { + result = "error: no tool server" + isErr = true + } else if err := rt.ToolServer.LoadTool(args.Name); err != nil { + result = "error: " + err.Error() + isErr = true + } else { + result = "loaded: " + args.Name + } + emit(ctx, Event{Role: "tool", Name: call.Name, Content: result, OutputFormat: toolOutputFormat(rt, call.Name)}) + return toolResult{ToolCallID: call.ID, Name: call.Name, Content: result, IsError: isErr}, false + } + if rt.Exec != nil { out, blocks, err := rt.Exec(ctx, call.Name, call.Arguments) if err != nil { diff --git a/data/tools/tool_load b/data/tools/tool_load index 8fa3ccd..5ad5c1a 100644 --- a/data/tools/tool_load +++ b/data/tools/tool_load @@ -3,8 +3,7 @@ # ollie:prompt # ## tool_load # -# Load a tool into the current session/agent by writing its name to -# `session/{sid}/agent/{aid}/tools` via ollie-9p. +# Load a tool into the current session/agent. # # **Args**: `name` (required) — the tool name to load. # @@ -15,6 +14,7 @@ set -euo pipefail +# This tool just outputs the name - the actual loading is handled by +# the agent framework when it receives the tool result. name=$(grep -oP '"name"\s*:\s*"\K[^"]+') -echo "$name" | ollie-9p write "session/$OLLIE_SESSION_ID/agent/$OLLIE_UNAME/tools" -echo "loaded: $name" \ No newline at end of file +echo "STATUS=ok TOOL_LOAD=$name"