Fix cross-app agent delegation misreporting resolution failures as outages - #5024
builder-io-integration[bot] wants to merge 1 commit into
Conversation
…udly when unresolvable findAgent matched an agent handle exactly, so agent="plans" missed the plan app even though the Plan app labels itself "Plans" in its own sidebar, nav state, and skills. Twelve of thirteen first-party apps had the same latent miss in one grammatical number or the other. call-agent then returned the miss as a plain "Error: ..." string. The agent loop scores a returned string as a successful tool call, and the early return happened before the tracked call began, so the failure produced no isError, no loop-breaker entry, and no $a2a_invocation row. Handed a successful result containing prose, the model retold it as "The Plans app is temporarily unavailable" and fell back to writing a knowledge doc.
There was a problem hiding this comment.
Builder reviewed your changes and has a few items to flag 🟡
Review Details
Code Review Summary
PR #5024 updates shared cross-app delegation in @agent-native/core: findAgent now resolves a singular/plural handle variant after exact matching, while refusing ambiguous trailing-s candidates, and unresolved call-agent targets now throw a typed agent_not_found error with logging and $a2a_invocation telemetry. The approach is sound and addresses the reported false-outage behavior; the added discovery and delegation regressions cover the primary paths, and both review agents found no blocking correctness or compatibility issue.
Risk assessment: Standard, because this changes shared business logic and telemetry used across first-party apps. One telemetry accuracy issue remains: the unresolved-target helper hardcodes mode: "message", even though the same branch can be reached for direct actions and task polling. This is non-blocking but will misclassify those failures in analytics and omit task correlation.
✅ Exact-match precedence and ambiguity refusal are preserved.
✅ Errors now propagate as tool failures instead of successful-looking prose.
✅ Focused regression suites passed according to the review agents.
🧪 Browser testing: Attempted full verification; 0/16 cases could be verified because Chrome automation tools were unavailable, although the dev server and affected routes returned HTTP 200.
Summary
Fixes
findAgentto resolve singular/plural handle variants (e.g.plans→plan) and makes unresolvable delegation targets throw a typed, logged, telemetry-tracked error instead of returning a plainError: ...string.Problem
Factory item
4ea92872812afebae7b3c90322155307cd37b64d8a34cb67c6c15f458c803a56(Slack thread) reported that when the Brain app tried to delegate toagent:plans, it failed and the model told the user "The Plans app is temporarily unavailable," then silently fell back to writing a knowledge doc.Root cause:
findAgentonly matched handles exactly, soagent="plans"missed the Plan app, which is registered with idplaneven though its own sidebar, nav state, and skills all label it "Plans." When resolution failed,call-agentreturned a plainError: ...string rather than throwing. Because the agent loop scores a returned string as a successful tool call (noisError), this failure was invisible in both logs and telemetry, and the model was free to invent a plausible-sounding excuse ("temporarily unavailable") for what was actually a naming/registration mismatch. This is a systemic issue: twelve of thirteen first-party apps had the same latent singular/plural mismatch.Solution
Error: ...string return with a thrown, typedagent_not_founderror that is logged and reported via$a2a_invocationtelemetry, and whose message explicitly tells the model this is a target-resolution failure, not an outage.Key Changes
agentHandleNumberVariantinagent-discovery.tsto compute the singular/plural counterpart of a handle, returningnullwhen the swap would be meaningless (empty string or already ending in "ss").findAgentto fall back to the number-variant match when no exact match is found, only returning a result if exactly one agent matches the variant.unresolvableAgentTargetErrorincall-agent.tsto build anA2AInvocationError(agent_not_found) that logs the unresolved target and connected agents, emits$a2a_invocationtelemetry withstatus: "error", and instructs the model not to describe the target as unavailable/down.call-agent'srunnow throws this error instead of returning a string when the delegation target can't be found.plans→plan, resolving every built-in agent from its other grammatical number, leaving genuinely unknown handles unresolved, refusing to guess between ambiguousreport/reports-style agents, and asserting the new thrown error is logged and tracked correctly instead of being scored as a successful call.To clone this PR locally use the Github CLI with command
gh pr checkout 5024You can tag me at @BuilderIO for anything you want me to fix or change