Render a bare tool name as an exact recipient, not a namespace pattern

#60

A client that offers unqualified tool names cannot address any of them.

The symptom

Every agent session opens with a rejected tool call. The model emits read.filePath, bash.command, glob.pattern -- the tool name with one of its own parameter names glued on. The harness has no such tool, rejects the call, and the model recovers on the retry, so the session works and every single task pays for a wasted round trip.

Where it comes from

render_system_meta derives a namespace from every offered tool and renders it as a wildcard:

{%- set tns = fn.name.split('.')[0] -%}
...
{%- set rns.recipients = rns.recipients + ['"' + tns + '.*"'] -%}

split('.')[0] on a name with no dot returns the whole name, so a client offering read, bash and glob gets:

# Valid recipients: "self", "read.*", "bash.*", "glob.*", "user".

Nothing called read is addressable any more -- only things under read. The model is being told the tool it wants does not exist and something inside it does, so it invents a second segment from whatever is at hand. The malformed call is the model following the prompt correctly.

render_tools has the same assumption: it emits a namespace metadata entry for tools that have no namespace.

The fix

Treat the dot as the signal it is. A name containing one is namespaced and keeps its "ns.*" pattern; a name without one is already the whole address and is rendered verbatim.

bare        # Valid recipients: "self", "read", "bash", "glob", "user".
namespaced  # Valid recipients: "self", "fs.*", "sh.*", "user".

Clients that offer namespaced names are unaffected -- byte-for-byte identical output. The change only reaches names the template was previously mangling.

Verification

Rendered both shapes with jinja2 and diffed the preamble, then ran the patched template end to end through OpenCode against a quantized ONNX export of this model: a file-read task and a multi-tool debugging task (read, edit, shell, todo) both completed with no invalid tool calls, where the unpatched template failed the first call of every task.

Ready to merge
This branch is ready to get merged automatically.

Sign up or log in to comment