Runs one tool call in its own task (under a Task.Supervisor, not linked
to the loop). The before_tool_call gate runs first, inside the task, so
a gate that waits (a biometric approval) never blocks the loop;
once the gate allows, the task tells the loop ({:tool_running, pid}),
which starts the tool's timeout clock from then.
The task's result is {:ok, text} or {:error, text}. A raise, exit or
throw in the task (the gate's or the tool's) is formatted there
(Rho.Untrusted.format/4) and ends the task with that text as its exit
reason; the loop turns it, or any other abnormal end of the task (a kill,
a linked process's exit), into an error result without running anything
of the reason (Rho.Untrusted.exit_text/2). Before a
tool runs, the registry's sources are told its task
(Rho.ToolRegistry.watch/2), so a source of agent-written tools can
count a crash or timeout (the loop kills it) against them.
A Dyn tool's answer (Rho.Untrusted.untrusted?/1) is made plain data in
the task (Rho.Untrusted.plain/1: structs become maps, funs text)
before it is encoded or leaves the task, so neither the loop nor its
subscribers ever hold a value whose protocols run Dyn code.
A tool may answer with pictures for the model as well as text:
{:ok, {:images, [{mime_type, bytes}], text}} (photos), or
{:ok, {:image, mime_type, bytes, text}} for one (a screenshot).
And with data for the host's screens beside what the model reads (pi's
tool result details): {:ok, {:details, output, details}}, where
output is any of the above, or {:error, {:details, reason, details}}
for a failure. The model and the session get output (or reason);
details goes to the loop's subscribers, with tool_execution_end
(Rho.Events), so a screen can draw the answer (a passage, a table)
without parsing the text, and into the session only as far as the
host's :keep_details (Rho.Loop) keeps it.
Summary
Functions
The default gate: everything is allowed.
The error text for a tool task that ended abnormally (its :DOWN
reason), in the loop's process: nothing of the reason runs
(Rho.Untrusted.exit_text/2); a crash the task formatted itself reads
as that text.
Starts call (%{"arguments" => ...}) of module as a task under
task_sup, not linked: gate first, then the tool, with ctx as the
context Rho.Tool.run/2 gets. Once the gate allows, the task sends
loop {:tool_running, pid}; it returns to_result/1 of the tool's
answer. This is how Rho.Loop runs every call.
The error text for a call killed at module's timeout (Rho.Tool.timeout_ms/1).
A tool's return as the loop takes it: {:ok, text | images} or
{:error, text}, or {:details, result, details} for a tool that
answered with details (see the moduledoc).
Types
Functions
The default gate: everything is allowed.
The error text for a tool task that ended abnormally (its :DOWN
reason), in the loop's process: nothing of the reason runs
(Rho.Untrusted.exit_text/2); a crash the task formatted itself reads
as that text.
Starts call (%{"arguments" => ...}) of module as a task under
task_sup, not linked: gate first, then the tool, with ctx as the
context Rho.Tool.run/2 gets. Once the gate allows, the task sends
loop {:tool_running, pid}; it returns to_result/1 of the tool's
answer. This is how Rho.Loop runs every call.
The error text for a call killed at module's timeout (Rho.Tool.timeout_ms/1).
A tool's return as the loop takes it: {:ok, text | images} or
{:error, text}, or {:details, result, details} for a tool that
answered with details (see the moduledoc).