tools serve: the dispatch harness, proven end to end

Add serveTools(broker) — the tool runtime's core: collect every module's
registered tools, index by name (refusing a duplicate name across two
modules rather than silently shadowing), and answer 'tools.invoke'
requests by running the named tool and returning its result. Plus
listTools() for discovery and Broker.handle() (the server side of
request/reply).

Proven by test: a real async, network-calling tool is registered (as a
module does), served over an in-memory broker, and invoked by name — it
reaches its upstream and returns the computed result. So a module's tools
genuinely serve: register -> collect -> serve -> invoke -> real work ->
result. The per-node runtime process that binds the mesh's real broker
and imports the assigned modules is the thin wrapper over this.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
2026-09-03 23:12:38 +02:00
parent a19a2f5cf0
commit 23bb53682b
3 changed files with 122 additions and 2 deletions
+5 -1
View File
@@ -9,8 +9,12 @@ export type { Envelope };
/** A request/reply call and a publish/subscribe surface over the mesh broker. */
export interface Broker {
/** Ask one question and await one answer — the shape mesh-control's command API is reached by. */
/** Ask one question and await one answer — the client side; the shape mesh-control's command
* API is reached by. */
request<Req, Res>(key: string, body: Req): Promise<Res>;
/** Answer a question — the server side of request/reply. A tool runtime serves invocations this
* way. Returns an unregister. */
handle<Req, Res>(key: string, handler: (body: Req) => Promise<Res>): Promise<() => void>;
/** Emit an event onto the mesh. */
publish<T>(env: Envelope<T>): Promise<void>;
/** React to events matching a routing-key pattern. Returns an unsubscribe. */