Inbound `message.created` already carries `xType` (dm / triage / group /
broadcast / etc.) — record it in a per-channel cache so other plugins
can answer "is this channel a DM?" without poking the Center API.
New module src/channel-meta.ts:
- in-memory Map<channelId, xType>
- lazily loaded from ~/.openclaw/fabric-channel-meta.json on first
access (so first-ever DM after a fresh gateway start still hits
cache from the previous run)
- debounced 250ms flush on dirty; force-flush on gateway_stop
- recordChannelType(channelId, xType): called from inbound
- getChannelType(channelId): null if unknown — caller MUST treat null
as "don't know", NOT as "assume DM" (would re-introduce the false-
positive on group channels we're trying to eliminate)
Wiring:
- inbound.ts socket.on('message.created'): records xType BEFORE the
self-author / dedup gates (channel type is observer-agnostic)
- index.ts: installs globalThis.__fabric = { getChannelType } on
registerFull(); flushes on gateway_stop
Consumer: ClawPrompts' fabric-chat-injector will start gating its prompt
injection on getChannelType(channelId) === 'dm' (companion PR on
ClawPrompts). Removes the phase-1 "any fabric channel" false-positive.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>