feat(fabric): coalesce a split agent turn into ONE message (deterministic)
OpenClaw delivers an agent turn whose blocks are text -> thinking/tool -> text via multiple inbound deliver() calls (a non-text block is a delivery boundary), so one turn became N Fabric messages. Fix: buffer deliver() segments per channel (src/coalesce.ts) and flush them as ONE postMessage at a deterministic boundary — the finally after dispatchInboundReplyWithBase() resolves, which provably runs only after every deliver() of the turn (verified: deliver,deliver -> dispatch returned -> flush). No hooks, no timers, no idle guessing. The agent_end hook was rejected: it fires BEFORE deliver(). gateway_stop flushes any leftover; a long safety timeout is a leak-guard only. channels.fabric.coalesce=false restores raw per-segment posting. Verified on local openclaw + Fabric with a fake text/thinking/text model: single trigger -> exactly one merged message. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,11 @@ export type FabricChannelConfig = {
|
||||
// (Guild C-2). Required by the channel config schema; sourced from config
|
||||
// only — never from the environment.
|
||||
commandsSyncKey?: string;
|
||||
// Coalesce an agent turn that OpenClaw split into multiple deliveries
|
||||
// (text → thinking/tool → text => N sendText calls) into ONE Fabric
|
||||
// message. The flush boundary is the deterministic `agent_end` hook (not
|
||||
// a timer). Default true; set false for raw per-segment posting.
|
||||
coalesce?: boolean;
|
||||
accounts?: Record<string, FabricAccountConfig>;
|
||||
defaultAccount?: string;
|
||||
} & FabricAccountConfig;
|
||||
@@ -46,6 +51,12 @@ export function resolveCommandsSyncKey(cfg: Cfg): string {
|
||||
return (section(cfg).commandsSyncKey ?? '').trim();
|
||||
}
|
||||
|
||||
// Whether to coalesce a split agent turn into one Fabric message
|
||||
// (channel-level). Default true.
|
||||
export function resolveCoalesce(cfg: Cfg): boolean {
|
||||
return (cfg.channels?.fabric ?? {}).coalesce !== false;
|
||||
}
|
||||
|
||||
export function listFabricAccountIds(cfg: Cfg): string[] {
|
||||
const accts = section(cfg).accounts ?? {};
|
||||
const ids = Object.keys(accts);
|
||||
|
||||
Reference in New Issue
Block a user