From 8963f3ca001173823dfca140417c67b28799f4f9 Mon Sep 17 00:00:00 2001 From: hzhang Date: Sat, 23 May 2026 22:21:03 +0100 Subject: [PATCH] feat(tools): fabric-guild-list returns serviceEndpoint for announce targets FabricSession.guilds type + fabric-guild-list response now carry the new serviceEndpoint field (added to GuildNode in Fabric.Backend.Center). The tool description teaches agents the distinction: 'endpoint' is the client-facing URL (which they themselves use to call the guild from plugin context), 'serviceEndpoint' is what to plumb into dialectic_propose_topic's announce_guild_base_url so the dialectic backend can dial it from inside the deployment. Fixes bug #2 from the first e2e debate run: agent-supplied 'http://server.t3:7002' wasn't backend-reachable; agent now supplies 'http://fabric-backend-guild:7002' via serviceEndpoint and broadcasts actually land. --- src/fabric-client.ts | 7 +++++++ src/tools.ts | 20 ++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/fabric-client.ts b/src/fabric-client.ts index e503182..8b59ccb 100644 --- a/src/fabric-client.ts +++ b/src/fabric-client.ts @@ -10,6 +10,13 @@ export type FabricSession = { nodeId: string; name: string; endpoint: string; + // Service-to-service endpoint (e.g. http://backend-guild:7002) — what + // an in-deployment backend uses to reach this guild. Distinct from + // `endpoint` which is the client/browser-facing URL. Plumb this into + // `announce_guild_base_url` on dialectic_propose_topic so the + // dialectic-backend can actually dial it. Null when the admin hasn't + // configured it. + serviceEndpoint?: string | null; status: string; // free-form description of this guild's role; admin-set on Center. // null when the admin hasn't filled it in yet. diff --git a/src/tools.ts b/src/tools.ts index d787018..0dc118e 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -367,10 +367,15 @@ export function registerFabricTools( name: 'fabric-guild-list', description: 'List guilds the calling agent is a member of. Returns ' + - '{nodeId, name, purpose, status} per row. `purpose` is a free-form ' + - "description of what each guild is for. Use this BEFORE " + - 'fabric-channel-list when a workflow asks you to pick the ' + - 'right guild by intent (no guild ids hardcoded into workflows).', + '{nodeId, name, purpose, status, serviceEndpoint} per row. ' + + "`purpose` is a free-form description of what each guild is for — " + + 'pick the guild whose purpose matches your intent. ' + + '`serviceEndpoint` is the internal URL another backend service ' + + 'uses to reach this guild (NOT the client-facing url): pass it as ' + + '`announce_guild_base_url` to dialectic_propose_topic when you ' + + 'want the dialectic-backend to broadcast topic lifecycle to a ' + + "channel in this guild. Use this tool BEFORE fabric-channel-list " + + 'when a workflow asks you to pick the right guild by intent.', parameters: { type: 'object', additionalProperties: false, @@ -411,6 +416,13 @@ export function registerFabricTools( name: g.name, status: g.status, purpose: g.purpose ?? null, + // serviceEndpoint is what an in-deployment service uses to + // reach this guild — pass this into announce_guild_base_url + // on dialectic_propose_topic, NOT the client-facing endpoint. + // Null when the admin hasn't configured it (broadcasts will + // fail until set; ask hangman to run + // `cli node set-service-endpoint --node-id --endpoint `). + serviceEndpoint: g.serviceEndpoint ?? null, })), }; },