Files
Fabric.OpenclawPlugin/dist/fabric/src/sub-discussion-hook.js
hzhang f8c8c21727 feat(gateway): register fabric.register gateway method for live no-restart registration
Recruitment's register-agent step is a plain shell step (no LLM turn), so it
cannot invoke the `fabric-register` TOOL (tool only fires inside an agent
turn) and there is no `openclaw tools call` CLI. It previously fell back to
the standalone bootstrap binary, which writes fabric-identity.json but cannot
notify the running plugin -> the new agent's inbound socket only came up after
a gateway restart.

This adds an in-process gateway method `fabric.register` (scope:
operator.write) whose handler runs inbound.addAccount: validates the key,
persists identity, and brings the inbound socket up immediately. The script
now calls `openclaw gateway call fabric.register --params ...` and only falls
back to the bootstrap binary if the method is unavailable.

Also resyncs committed dist/ to source (sub-discussion-hook/store + tools/
inbound were source-committed but their dist artifacts were stale).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 21:04:38 +01:00

60 lines
2.6 KiB
JavaScript

// Plugin-local before_prompt_build hook that injects per-(agent, channel)
// guides for sub-discussion channels created via the `create-sub-discussion`
// tool. Mirrors the pattern used by ClawPrompts' fabric-chat-injector
// (channelId-aware injection) but with content dynamically supplied at
// channel-creation time instead of read from static files via PrismFacet's
// router/rule registry.
//
// Match logic per turn:
// ctx.channelId → store.find() → sub-discussion entry
// ctx.agentId → identity.findByAgentId().fabricUserId
// ─ matches entry.hostUserId → inject hostGuide
// ─ matches entry.guestUserIds → inject guestGuide
// ─ neither → no injection
//
// Fail-closed on unknown agentId/channelId — we never inject "the wrong"
// guide, only the right one or nothing.
const _G = globalThis;
const DEDUP_KEY = '_fabricSubDiscussionHookDedup';
export function registerSubDiscussionHook(api, store, identity) {
if (!(_G[DEDUP_KEY] instanceof WeakSet))
_G[DEDUP_KEY] = new WeakSet();
const dedup = _G[DEDUP_KEY];
api.on('before_prompt_build', async (...args) => {
const event = args[0];
const ctx = (args[1] ?? {});
// The hook fires both for fabric-driven turns (channelId set) and
// for other triggers (HF wake, exec-event, etc.) — drop those.
if (typeof event === 'object' && event !== null) {
if (dedup.has(event))
return undefined;
dedup.add(event);
}
const agentId = (ctx.agentId ?? '').trim();
const channelId = (ctx.channelId ?? '').trim();
if (!agentId || !channelId)
return undefined;
const provider = (ctx.messageProvider ?? '').toLowerCase();
if (provider && provider !== 'fabric')
return undefined;
const entry = store.find(channelId);
if (!entry)
return undefined;
const ident = identity.findByAgentId(agentId);
const myUserId = (ident?.fabricUserId ?? '').trim();
if (!myUserId) {
// identity registry caches fabricUserId after the first agentLogin
// in inbound.ts. If it's missing here, the agent likely hasn't
// completed login yet — skip rather than guess.
return undefined;
}
if (myUserId === entry.hostUserId) {
return { appendSystemContext: entry.hostGuide };
}
if (entry.guestUserIds.includes(myUserId)) {
return { appendSystemContext: entry.guestGuide };
}
return undefined;
});
}