Skip to content

Agent — a composable sub-agent

Clojure (JVM) + cljgo · package net.clojars.muthuishere/toolnexus · SPEC §7D

Define a sub-agent with its own toolkit, prompt and budget, callable as a tool by its parent.

In the full-tier ports an Agent is a declarative value — a name, a system prompt, its own toolkit, a model, a budget — that the runtime can spawn and the parent can call as if it were a tool. The appeal is scoping: a researcher child sees only search tools, a writer child only file tools, and neither inherits the parent’s full catalog or its transcript. Clojure has none of that machinery.

The equivalent scoping is a separate toolkit and a separate client, called from a plain function. A “sub-agent” is a closure over the pair, wrapped as a native tool so the parent’s model can reach it:

(require '[toolnexus.core :as core]
'[toolnexus.client :as client]
'[toolnexus.native :as native]
'[toolnexus.tool :as tool])
;; the child's world: its own toolkit, its own prompt, its own turn budget.
;; :builtins :tools drops names off the all-on baseline — the child keeps
;; read/grep/webfetch and never sees bash, write or edit.
(def researcher-tk
(core/build {:builtins {:tools {:bash false :write false :edit false
:apply_patch false :todowrite false
:question false :glob false}}}))
(def researcher
(client/create-client {:base-url "https://api.anthropic.com"
:style "anthropic"
:model "claude-sonnet-4-6"
:system-prompt "You research and report. Be terse."
:max-turns 6}))
(def research-tool
(native/native-tool
{:name "research"
:description "Delegate a research question to a scoped sub-agent."
:input-schema {:type "object"
:properties {:question {:type "string"}}
:required ["question"]}
:run (fn [args]
;; no :conversation-id ⇒ a one-shot run, no inherited transcript
(let [r (client/run researcher (str (:question args))
{:toolkit researcher-tk})]
(tool/success (:text r)
{:turns (:turns r)
:tool-calls (:tool-call-count r)
:usage (:usage r)})))}))
;; the parent sees one tool named "research" and nothing of the child's catalog
(def parent-tk (core/build {:skills "examples/skills" :tools [research-tool]}))

What this gives you: tool scoping, an independent system prompt, an independent model, an isolated transcript, and a turn cap via :max-turns. What it does not give you: a handle you can inspect or interrupt mid-flight, budgets enforced across a team of children, or a task tool the model can use to spawn children it invents on its own. The run is synchronous — it returns when the child is done.

For delegation that survives a restart, or that crosses a process or language boundary, run the child as a real agent behind toolnexus.serve/serve and call it with toolnexus.a2a/remote-agent. That is a genuine hop with a task id and a store, which is more machinery than a closure but is the only form of delegation here that can be paused, polled and resumed.