Network Tools
When you call HubClient.register(agent, ...) with the default attach_plugin=True, the framework attaches NetworkPlugin to your agent. The plugin does two things:
- Adds an assembly policy that prefixes every LLM call with the agent's name and id plus a one-line list of network tool names.
- Adds six LLM-facing tools to
agent.toolsso the model can drive network operations directly:say,delegate,peers,channels,tasks,context.
These are the vocabulary the LLM uses to participate in the network — discovery, messaging, lifecycle, history — without your code having to interpret tool calls and proxy them.
Two flat tools cover the hot path#
| Tool | Signature | Purpose |
|---|---|---|
say | say(content, audience?, channel_id?) | Post EV_TEXT into the active channel (or a specified one). audience is a list of peer names (resolved to ids); None broadcasts. |
delegate | delegate(target, prompt, capability?, timeout=300) | One-shot consult: open a consulting channel with target, send prompt, await the single reply, return its text. |
say is the most common verb — every reply on a multi-party turn flows through it. delegate is the second-most-common because "ask one specialist a question, take their answer" is the canonical multi-agent pattern.
The framework resolves ChannelInject (current channel) and AgentClientInject (calling agent's hub client) automatically inside the notify handler, so the LLM never sees those parameters.
Four grouped action-dispatch tools#
Each grouped tool takes an action literal plus action-specific args, keeping the LLM's tool list short.
peers(action) — discovery#
| Action | Args | Returns |
|---|---|---|
"find" | query?, capability?, sort_by?, limit=20 | List of peer summaries (excludes the calling agent). |
"describe" | name | One peer's full profile: {passport, resume, skill_md}. skill_md falls back to a rendered passport+resume when no SKILL.md is registered. |
channels(action) — lifecycle#
| Action | Args | Returns |
|---|---|---|
"list" | state="active"\|"all" | Channels this agent participates in. |
"open" | type, target, knobs?, intent?, ttl? | Mirrors AgentClient.open. Returns {channel_id, type, participants}. |
"info" | channel_id | Full ChannelMetadata if the agent is a participant. |
"close" | channel_id? (defaults to current) | Closes the channel with reason "closed_by_agent". |
tasks(action) — task lifecycle#
Two halves: active actions (the agent is inside its own agent.task(...) block) and observation actions (any task the hub has observed).
| Action | Half | Args | Returns |
|---|---|---|---|
"progress" | active | payload | Emits TaskProgress on the active task. |
"complete" | active | result? | Terminal — emits TaskCompleted. |
"list" | observation | scope="own"\|"all", state="active"\|"all", limit=20 | Task summaries. |
"status" | observation | task_id | Refreshed TaskMetadata. |
"wait" | observation | task_id, timeout=300, poll_interval=0.1 | Blocks until the task reaches a terminal state. |
"cancel" | — | — | Not implemented; returns an error placeholder. |
"start" is intentionally not a tool — calling it from the LLM would bypass the async with agent.task(...) lifecycle that scopes TaskInject correctly. Owners start tasks in their own code; the LLM uses "progress"/"complete" once a task is active, and delegate for one-shot remote work.
context(action) — past content#
| Action | Args | Returns |
|---|---|---|
"search" | query, scope="channel"\|"knowledge", limit=10 | Excerpts of envelopes whose text matches query (case-insensitive substring). |
"quote" | speaker, recent_n=1, channel_id? | The last recent_n EV_TEXT envelopes from speaker in the current (or specified) channel. |
scope="knowledge" reaches into the calling agent's own KnowledgeStore. Substring search only — for vector / semantic search, the agent's own loop calls into framework-core recall directly.
What gets injected, automatically#
Every grouped tool accepts AgentClientInject, ChannelInject, and (for tasks) TaskInject parameters that the framework resolves from context.dependencies when the tool runs inside a notify handler. Your code does not need to wire them up — the default handler stamps them via stamp_dependencies before invoking the agent's turn.
If you are testing a tool outside the notify-handler context, pass them yourself:
Opting out#
Pass attach_plugin=False to HubClient.register for a bare agent — useful for headless workers or gateways that handle envelopes entirely in your own code without needing the LLM-facing tool surface.
See also#
- Agent Clients and Handlers — what the default handler does and how to replace it.
- Workflow — hand-written
Handoff-returning tools complementToolCalled → AgentTargettransitions for graph-driven routing.