Problem to solve
Aoide's main loop occasionally surfaces tool calls named after sister/sub-agent message channels — e.g. from_melete_goal, from_zero-width-sleuth, from_whitespace-scout. These names are not registered as tools; they are the prefixes the system uses when delivering messages to the agent. Calling them produces:
{"error":"Tools::UnknownToolError: Unknown tool: from_<name>"}
…polluting the conversation and wasting tokens. Today this is a known papercut that requires the human to nudge the agent ("these are not tools").
We do not currently know whether the offending tool calls originate from the LLM itself or from somewhere inside Anima.
Two hypotheses
- LLM hallucination. The model sees prior
from_* blocks in the conversation context and pattern-matches them as callable tools. Anthropic's endpoint returns them in tool_use blocks because the model decided to invoke them.
- Internal leak. Some part of Anima's pipeline (event subscriber, tool-execution dispatcher, registry assembly) generates these calls when it shouldn't — they never come from the LLM at all.
The fix differs significantly between the two:
- If hallucination: filter
from_* tool_use blocks out of the dispatch path (lib/events/subscribers/llm_response_handler.rb#normalize_tool_uses) and pretend the LLM never called them. No error round-trip.
- If internal leak: find the source and stop emitting them.
Research approach
Wire up debug logging for Aoide's main loop the same way the muses are instrumented today (lib/melete/runner.rb:132-133 logs system prompt and user message at debug level). For Aoide we additionally need:
- Raw response payload from the Anthropic endpoint, including
content blocks (text + tool_use)
- The list of tool_use blocks as returned by the API, before any normalization
- The tool name and ID of every dispatched tool execution, so we can correlate "what came in" vs "what got called"
Enable this in dev environment only.
Acceptance criteria
Suspected choke points (starting hints, not prescribed)
lib/events/subscribers/llm_response_handler.rb — normalize_tool_uses is where the API response gets turned into dispatchable tool calls. Logging the raw response just before this method runs would give the cleanest signal.
- The decorators in
app/decorators/pending_from_*.rb produce the from_* named blocks that appear in the agent's view. Worth checking whether the same names ever leak into the outgoing tool registry (they shouldn't, but if they do, the model sees them as legitimate tools).
Why this matters
Every spurious from_* call costs context (the failed tool result is persisted), wastes tokens, and erodes trust in the agent's apparent competence. It's one of the first weird behaviors a new agent encounters, and the human currently has to coach around it.
Problem to solve
Aoide's main loop occasionally surfaces tool calls named after sister/sub-agent message channels — e.g.
from_melete_goal,from_zero-width-sleuth,from_whitespace-scout. These names are not registered as tools; they are the prefixes the system uses when delivering messages to the agent. Calling them produces:…polluting the conversation and wasting tokens. Today this is a known papercut that requires the human to nudge the agent ("these are not tools").
We do not currently know whether the offending tool calls originate from the LLM itself or from somewhere inside Anima.
Two hypotheses
from_*blocks in the conversation context and pattern-matches them as callable tools. Anthropic's endpoint returns them intool_useblocks because the model decided to invoke them.The fix differs significantly between the two:
from_*tool_use blocks out of the dispatch path (lib/events/subscribers/llm_response_handler.rb#normalize_tool_uses) and pretend the LLM never called them. No error round-trip.Research approach
Wire up debug logging for Aoide's main loop the same way the muses are instrumented today (
lib/melete/runner.rb:132-133logs system prompt and user message at debug level). For Aoide we additionally need:contentblocks (text + tool_use)Enable this in dev environment only.
Acceptance criteria
from_*tool_use block was present in the API response or was synthesized later.Suspected choke points (starting hints, not prescribed)
lib/events/subscribers/llm_response_handler.rb—normalize_tool_usesis where the API response gets turned into dispatchable tool calls. Logging the raw response just before this method runs would give the cleanest signal.app/decorators/pending_from_*.rbproduce thefrom_*named blocks that appear in the agent's view. Worth checking whether the same names ever leak into the outgoing tool registry (they shouldn't, but if they do, the model sees them as legitimate tools).Why this matters
Every spurious
from_*call costs context (the failed tool result is persisted), wastes tokens, and erodes trust in the agent's apparent competence. It's one of the first weird behaviors a new agent encounters, and the human currently has to coach around it.