diff --git a/src/inbound.ts b/src/inbound.ts index 1ce77f4..bbeefb8 100644 --- a/src/inbound.ts +++ b/src/inbound.ts @@ -51,6 +51,30 @@ type FabricMessage = { xType?: string; }; +// Walk cfg.bindings for the entry that ties `agentId` to a fabric account. +// Returns the binding's match.accountId (the slot label routing keys on); +// returns undefined when the agent has no explicit fabric binding so the +// caller can fall back to agentId without changing pre-existing semantics +// for agents whose binding accountId == agent_id anyway. +function findFabricBindingAccountId(cfg: unknown, agentId: string): string | undefined { + const bindings = (cfg as { bindings?: Array<{ + agentId?: string; + match?: { channel?: string; accountId?: string }; + }> })?.bindings; + if (!Array.isArray(bindings)) return undefined; + for (const b of bindings) { + if ( + b?.agentId === agentId && + b?.match?.channel === 'fabric' && + typeof b?.match?.accountId === 'string' && + b.match.accountId.length > 0 + ) { + return b.match.accountId; + } + } + return undefined; +} + export class FabricInbound { private sockets: Socket[] = []; private seen = new Set(); @@ -541,10 +565,22 @@ export class FabricInbound { // (commands-handlers `isDirectMessage` checks ChatType==='direct') // misclassifies the turn. const { peerKind, chatType } = fabricPeerRoutingForXType(m.xType); + // resolveAgentRoute needs the *binding* accountId (the channel-side + // slot name) — not the openclaw agentId. For most agents the binding + // is `{agentId: X, match: {channel: fabric, accountId: X}}` so the + // two coincide; but for shared-placeholder cases (e.g. the recruitment + // `interviewee` slot bound to multiple agents over its lifetime) the + // binding accountId is the slot label ("interviewee", "Neon", …) not + // the agent_id. Passing agentId there returned bindings=0 and silently + // fell back to `main`, hijacking sub-discussion turns. Look up the + // agent's fabric binding accountId here; fall back to agentId when no + // explicit binding exists (preserves prior behavior for agents with + // no fabric binding declared). + const bindingAccountId = findFabricBindingAccountId(this.cfg, agentId) ?? agentId; const route = core.channel.routing.resolveAgentRoute({ cfg: this.cfg, channel: 'fabric', - accountId: agentId, + accountId: bindingAccountId, peer: { kind: peerKind, id: channelId }, }); const storePath = core.channel.session.resolveStorePath(cfg.session?.store, {