Agent tools issue their requests off the mesh router
The hub an agent plugin is handed is not necessarily a hub it may post work from.
ChatClientAgentFactory builds its built-in plugins — ContentCollectionPlugin,
CollaborationPlugin, MeshPlugin, VersionPlugin, … — with its own DI-injected
IMessageHub. On a portal that is the root mesh hub, which is the mesh's ROUTER. A tool that
calls hub.Observe(request, o => o.WithTarget(owner)) on it makes the router an END of the delivery
in both directions:
- the request leaves stamped
sender: mesh/{id}— theROUTER_TRAFFIC ORIGIN … as senderline; - the owner answers with
ResponseFor(delivery), which is correct, and the reply is therefore addressed straight back atmesh/{id}— the… as targetline, logged at the OWNER's call site.
The second line names the innocent answering half. Nothing there changes: moving the requester removes both lines.
The rule
A plugin method that issues request/response work issues it on
hub.NodeOperationIssuingHub() (core, MeshExtensions), never on the bare injected hub. The
seam is the identity function for every hub that is not the router, so a plugin built on a
session or portal hub keeps its identity byte for byte; on the router it returns the mesh's
off-router execution hub, which is routing-registered (replies land on it cross-silo) and shares
the mesh's type registry, so a response type registered there by AddAITypes — such as
SaveContentResponse — is read exactly as before.
Reads that go through ChunkNavigation or MeshOperations already pick their own seam; the rule
is about a plugin that posts a request itself.
Where it was met
ContentCollectionPlugin.UploadContent posted its SaveContentRequest on the injected hub, and
production showed the pair — request with the router as sender, SaveContentResponse from
AIExtensions.HandleSaveContent with the router as target
(Systemorph/MeshWeaver#6647, #6646). CollaborationPlugin.PostAndReport (comment and
suggested-edit requests) had the identical shape and moved with it.
The detector prints its line once per (role, message type) per hub lifetime, so one production sighting says nothing about how often a tool posted this way.
The test
MeshWeaver.AI.Test.ContentUploadIssuedOffTheRouterTest builds the plugin on Mesh — the
factory's portal shape — runs UploadContent, requires the owner's own verdict to come back (so
the exchange really happened), and asserts the origin detector recorded neither half. Its positive
control observes the same SaveContentRequest on the root hub and requires the capture to record
it; without that control, "no record" and "the capture never saw the detector" read the same.
With the fix reverted, the measurement fails on the SaveContentRequest … sender record.
Related
- Router Traffic Detection — the detector, its two sites and the role taxonomy.
- Agent tool cancellation — the other contract every plugin tool keeps.