Inbound was hardcoding `peer: { kind: 'group' }` and `ChatType: 'group'`
for every fabric channel regardless of xType. As a result:
- sessionKey for a DM was `agent:<id>:fabric:group:<chan>` instead of
`agent:<id>:fabric:direct:<chan>`
- ctx.ChatType='group' caused user-prompt metadata to render
`is_group_chat: true` on a DM
- openclaw's `isDirectMessage()` check (ChatType==='direct') returned
false, so DM-specific prompt and turn behavior never engaged
Caught by recruiter test in session 40c51de2: the model's thinking trace
acknowledged "fabric DM channel" (from the ClawPrompts chat-injector
hook) but the surrounding user-prompt metadata contradicted it with
`is_group_chat: true`, and the model reasoned its way out of running
`workflow_start`.
Fix factors a small helper `fabricPeerRoutingForXType` (and a cache-
backed `fabricPeerRoutingForChannel` for outbound) in channel.ts that
maps:
- 'dm' → { peerKind: 'direct', chatType: 'direct' }
- rest → { peerKind: 'group', chatType: 'group' } (no change)
Inbound uses m.xType directly (live, authoritative). Outbound has no
xType in its call signature, so it consults the channel-meta cache
populated by inbound (same `getChannelType` already exposed via
__fabric). Cache miss falls back to 'group' — the pre-fix default, no
regression. The proactive-DM-without-prior-inbound edge case still
routes that one outbound as 'group'; the next round agrees on 'direct'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>