Summary
When a connector's channels/publish rejects (JSON-RPC error, 60 s request timeout, or transport drop mid-request), ChannelRegistry.routeSpeech throws instead of returning through its fail() helper. In locus mode every caller catches that throw and only writes to stderr. So:
- no
[discord-send-failed] marker reaches the agent's context,
- no
mcpl:speech-route-failed trace is emitted,
- the turn's
[delivered] receipt leaves the segment out without comment.
The agent's reply disappears with no sign in its context. The marker currently fires only for host-side refusals (no locus, unregistered channel, missing grant, server not found) and explicit delivered:false. It does not fire for the failures connectors actually report.
Where
Which connector failures take this path
Every in-tree connector returns delivered as a boolean on success and on empty text, and throws on everything else. Examples:
- discord-mcpl / slack-mcpl: any platform API error (missing permission, rate limit, 5xx).
- zulip_mcp: an upload failure, or a chunked long reply where chunk k > 1 fails after earlier chunks were already posted. That is a partial delivery, reported to the host as an error.
- Any connector that hangs past 60 s. The timeout text already says the outcome is unknown, but it only reaches stderr.
Related
Suggested direction
Catch the publish rejection inside routeSpeech and route it through fail() with a reason that tells two cases apart:
- definite: the server returned an error response and nothing was posted,
- uncertain: timeout, transport close, or a partial chunked send.
Then have both the marker and the >>> bounce use that distinction. A marker-per-failure test with a rejecting fake server would cover it.
Summary
When a connector's
channels/publishrejects (JSON-RPC error, 60 s request timeout, or transport drop mid-request),ChannelRegistry.routeSpeechthrows instead of returning through itsfail()helper. In locus mode every caller catches that throw and only writes to stderr. So:[discord-send-failed]marker reaches the agent's context,mcpl:speech-route-failedtrace is emitted,[delivered]receipt leaves the segment out without comment.The agent's reply disappears with no sign in its context. The marker currently fires only for host-side refusals (no locus, unregistered channel, missing grant, server not found) and explicit
delivered:false. It does not fire for the failures connectors actually report.Where
routeSpeechawaits the publish without a try: channel-registry.ts#L2921sendRequestrejects on error response, timeout, and close: server-connection.ts#L952-L963, #L1170-L1173Which connector failures take this path
Every in-tree connector returns
deliveredas a boolean on success and on empty text, and throws on everything else. Examples:Related
>>>mode the catch at framework.ts#L7853-L7857 bounces them as definitely failed and offers{{unsent}}, which can double-post.Suggested direction
Catch the publish rejection inside
routeSpeechand route it throughfail()with a reason that tells two cases apart:Then have both the marker and the
>>>bounce use that distinction. A marker-per-failure test with a rejecting fake server would cover it.