How ClawTalk Orchestrates Message Routing Between Subagents in ClosedClaw
ClawTalk acts as an inter-agent bus that parses incoming prompts, classifies intent, and injects the correct subagent profile—Research, System, Code, or Memory—into the next execution context.
ClawTalk is the central routing layer in the asafelobotomy/closedclaw repository. It transforms raw user messages into structured routing decisions that determine which specialized subagent handles a given task. By combining an intent classifier, a static directory of agent capabilities, and a runtime hook, ClawTalk ensures that requests like "search the web" reach the Research agent while "refactor this function" trigger the Code agent.
The Three Core Components of ClawTalk Routing
The orchestration relies on three tightly-coupled modules defined in src/agents/clawtalk/:
| Component | Responsibility | Source File |
|---|---|---|
| Encoder | Parses raw text into a ClawTalkMessage, extracts intent and confidence scores. |
src/agents/clawtalk/encoder.ts |
| Directory | Maintains SUBAGENT_PROFILES for Research, System, Code, Memory, and others; matches intent to the best profile. |
src/agents/clawtalk/directory.ts |
| Hook | Entry point called by the Claw engine via before_agent_start. Runs the encoder, queries the Directory, handles escalation, and injects the chosen subagent’s configuration into the runtime. |
src/agents/clawtalk/clawtalk-hook.ts |
Step-by-Step Message Routing Flow
When a user prompt enters the system, clawtalkBeforeAgentStartHandler in clawtalk-hook.ts executes the following pipeline:
-
Intent Classification
The hook callsrouteMessage(), which invokesencode(userMessage)fromencoder.ts. This returns anEncodedMessagecontaining:intent: A string like"web_search"or"code_generate"confidence: A 0–1 scorewire: The raw CT/1 protocol string
-
Directory Lookup
The hook fetches a singletonDirectoryviagetDirectory()(lines 94–98). It then callsdirectory.routeMessage(encoded.message, encoded.intent)(lines 102–104).
The Directory scansSUBAGENT_PROFILES(defined indirectory.tslines 14–41, 60–91, and 95–119), filters for profiles whosecapabilitiesarray includes the detected intent, and sorts bypriority. If no match exists, it falls back toCONVERSATION_PROFILE(lines 92–94). -
Escalation Check
shouldEscalate()(lines 106–112) evaluates whether to switch to a cloud model based onconfidence, message length, and theescalationThresholddefined intypes.ts(line 107). -
TPC Transport Decision
For agent-to-agent communication (isAgentToAgent()at lines 46–48), the hook determines whether to enable the Tonal Pulse Communication (TPC) runtime (lines 115–138). If the TPC runtime is ready andruntime.shouldFallbackToText()returns false, the message is encoded as a tonal pulse; otherwise, it falls back to text ifallowTextFallbackis enabled (config lines 129–130). -
Build Routing Result
AClawTalkRoutingobject (lines 56–81) is constructed containing:agentId: The chosen subagent identifier (e.g.,"research","code","memory")systemPrompt: The profile’s prompt to be injectedtools: The allow-listed tools for that subagentmodelOverride: Set only if escalation occurredtpc: Boolean flag for TPC usage
-
Inject into Agent Context
The hook returns aPluginHookBeforeAgentStartResult(lines 93–110) that prependssystemPromptto the context, restricts available tools to thetoolAllowlist, and optionally overrides the model. The Claw runtime then launches the selected subagent with these parameters.
How Specific Subagents Are Selected
The SUBAGENT_PROFILES array in directory.ts maps intents to specialized handlers:
| Subagent | Handled Intents | Profile Location | Example Trigger |
|---|---|---|---|
| Research | web_search, summarize, browse |
directory.ts lines 16–20 |
"Find the latest news about quantum computing" |
| System | read_file, write_file, run_command |
directory.ts lines 34–38 |
"Read the file README.md" |
| Code | code_generate, code_review, code_debug, code_refactor |
directory.ts lines 60–64 |
"Refactor this function to use async/await" |
| Memory | remember, recall |
directory.ts lines 78–82 |
"Remember my favorite color is blue" |
| Browser | browser_automate |
directory.ts lines 95–99 |
"Take a screenshot of example.com" |
| Automation | schedule_task |
directory.ts lines 124–128 |
"Set a reminder for tomorrow 9am" |
| Conversation | conversation, unknown (fallback) |
directory.ts lines 44–48 |
General chat not matching other intents |
When the Directory receives an intent like code_refactor, it filters the array for profiles where capabilities.includes("code_refactor") is true, finds the Code profile, and returns it to the hook for injection.
Code Examples
Manually Invoking the Router
You can test the routing logic directly by importing routeMessage from the hook file:
import { routeMessage } from "./clawtalk-hook";
import { DEFAULT_CONFIG } from "./types";
// Example research query
const prompt = "Summarize the key points of the Wikipedia article on quantum computing.";
// Simulate an agent-to-agent call
const routing = routeMessage(prompt, { agentToAgent: true });
console.log(`Chosen sub-agent: ${routing.agentId}`);
console.log(`System prompt injected:\n${routing.systemPrompt}`);
console.log(`Allowed tools: ${routing.tools.join(", ")}`);
This outputs routing.agentId as "research" and includes the Research profile’s system prompt and tool allow-list.
Hook Handler Implementation
The clawtalkBeforeAgentStartHandler function in clawtalk-hook.ts demonstrates how the routing result is applied to the agent context:
export function clawtalkBeforeAgentStartHandler(
event: PluginHookBeforeAgentStartEvent,
ctx: PluginHookAgentContext,
): PluginHookBeforeAgentStartResult | void {
// ... intent classification and directory lookup ...
const routing = routeMessage(prompt, { agentToAgent });
const result: PluginHookBeforeAgentStartResult = {};
// Inject sub-agent system prompt
if (routing.systemPrompt) {
result.prependContext = routing.systemPrompt;
}
// Restrict to sub-agent's tool allowlist
if (routing.tools.length) {
result.toolAllowlist = routing.tools;
}
// Override model on escalation
if (routing.modelOverride) {
result.modelOverride = routing.modelOverride;
}
return result;
}
The returned result is merged into the next agent execution, effectively routing the conversation to the selected subagent with the correct tools and context.
Key Files and Implementation Details
| File | Role | Link |
|---|---|---|
src/agents/clawtalk/clawtalk-hook.ts |
Entry point that performs encoding, directory lookup, escalation, and TPC decisions. | clawtalk-hook.ts |
src/agents/clawtalk/directory.ts |
Holds SUBAGENT_PROFILES and implements route() / routeMessage(). |
directory.ts |
src/agents/clawtalk/types.ts |
Core type definitions including EncodedMessage, ClawTalkRouting, and config thresholds. |
types.ts |
src/agents/clawtalk/encoder.ts |
Turns raw user strings into ClawTalkMessage objects with intent and confidence. |
encoder.ts |
src/agents/clawtalk/escalation.ts |
Logic that decides when to switch to a cloud model based on confidence thresholds. | escalation.ts |
src/agents/clawtalk/tpc/index.ts |
Implements the Tonal Pulse Communication runtime for agent-to-agent encoding. | tpc/index.ts |
docs/concepts/clawtalk.md |
Human-readable specification of the ClawTalk protocol and CT/1 wire format. | clawtalk.md |
These files collectively demonstrate how ClawTalk turns plain user requests into routed, context-aware subagent executions, ensuring that each specialized handler receives exactly the tools and prompts it needs to complete its task.
Summary
- ClawTalk serves as the inter-agent bus in
closedclaw, routing every user prompt to the correct specialized subagent. - The Encoder (
encoder.ts) extracts intent and confidence from raw text, producing anEncodedMessagewith a CT/1 wire format. - The Directory (
directory.ts) maintainsSUBAGENT_PROFILESfor Research, System, Code, Memory, and others, matching intents to the best profile by capability and priority. - The Hook (
clawtalk-hook.ts) orchestrates the flow: it classifies input, queries the Directory, checks escalation thresholds, optionally enables TPC for agent-to-agent communication, and injects the chosen subagent’s system prompt, tool allowlist, and model override into the next execution context. - Subagents are selected based on intent filters (e.g.,
code_refactorroutes to Code,web_searchroutes to Research), with a fallback to the Conversation profile for unmatched intents.
Frequently Asked Questions
How does ClawTalk decide which subagent handles a request?
ClawTalk uses the Encoder to classify the user prompt into an intent (such as web_search or code_generate) with a confidence score. The Directory then scans SUBAGENT_PROFILES in directory.ts to find a profile whose capabilities array includes that intent, sorting by priority and returning the best match. If no profile matches, it falls back to the CONVERSATION_PROFILE.
What is the role of the TPC runtime in message routing?
The Tonal Pulse Communication (TPC) runtime, implemented in src/agents/clawtalk/tpc/index.ts, handles agent-to-agent encoding when isAgentToAgent() returns true. The hook checks runtime.shouldFallbackToText() to decide whether to transmit the message as a tonal pulse or fall back to plain text. This allows specialized subagents to communicate via compressed audio-like signals rather than text when appropriate.
Can ClawTalk escalate to a cloud model during routing?
Yes. The shouldEscalate() function in clawtalk-hook.ts (lines 106–112) evaluates whether to override the local model with a cloud-based alternative. It considers the confidence score from the Encoder, message length, and the escalationThreshold defined in types.ts. When triggered, the routing result includes a modelOverride field that the Claw runtime applies to the next agent execution.
How are tools restricted when routing to a specific subagent?
Each subagent profile in directory.ts defines a tools array listing exactly which capabilities that agent may use (for example, the Research profile includes web_search, fetch_url, and current_time). When the hook builds the ClawTalkRouting result, it copies this array into routing.tools. The returned PluginHookBeforeAgentStartResult then sets result.toolAllowlist to this array, ensuring the downstream agent can only invoke the tools relevant to its assigned role.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →