mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
fix(replies): preserve rich coalesced block replies (#81689)
This commit is contained in:
@@ -48,6 +48,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Codex startup: treat selectable configured OpenAI agent models as Codex runtime requirements during plugin auto-enable, startup planning, and doctor install repair, so Anthropic-primary configs can still switch to OpenAI/Codex cleanly.
|
||||
- Agents: preserve source-reply delivery metadata when merging tool-returned media into the final reply, keeping message-tool-only replies deliverable and mirrored. Thanks @pashpashpash and @vincentkoc.
|
||||
- Replies: treat rich presentation, interactive controls, and channel-native payload data as outbound content across follow-up, heartbeat, cron, ACP, and block-streaming delivery paths, preventing card/button-only replies from being dropped as empty.
|
||||
- Replies: deliver rich-only block replies even when block-streaming coalescing is enabled, keeping card and button payloads from being dropped by the text coalescer. Thanks @pashpashpash.
|
||||
- macOS/companion: require system TLS trust before pinning a first-use direct `wss://` gateway certificate and honor `gateway.remote.tlsFingerprint` as the explicit pin for remote node-mode sessions, so fresh endpoints fail closed when macOS cannot trust the certificate unless configured out of band. Fixes #50642. Thanks @BunsDev.
|
||||
- Update: snapshot config before update-time repair and restart writes, preserve plugin install records through doctor cleanup, and keep update-time config size drops from blocking the update while pointing users to the pre-update backup. Fixes #80077. (#80257) Thanks @Jerry-Xin and @vincentkoc.
|
||||
- WebChat/TUI: route Codex `tools.message` source replies to the active internal UI turn and mirror them to session history, so message-tool-only harness replies, including rich presentation and button-only replies, no longer disappear while WebChat and TUI remain non-targetable outbound channels. (#81586) Thanks @pashpashpash.
|
||||
|
||||
@@ -167,6 +167,31 @@ describe("createBlockReplyPipeline dedup with threading", () => {
|
||||
expect(sent).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("bypasses text coalescing for rich-only payloads", async () => {
|
||||
const sent: Array<{ presentation?: unknown }> = [];
|
||||
const pipeline = createBlockReplyPipeline({
|
||||
onBlockReply: async (payload) => {
|
||||
sent.push({ presentation: payload.presentation });
|
||||
},
|
||||
timeoutMs: 5000,
|
||||
coalescing: {
|
||||
minChars: 1,
|
||||
maxChars: 200,
|
||||
idleMs: 0,
|
||||
joiner: "\n\n",
|
||||
},
|
||||
});
|
||||
|
||||
const presentation = {
|
||||
blocks: [{ type: "buttons" as const, buttons: [{ label: "Open", value: "open" }] }],
|
||||
};
|
||||
|
||||
pipeline.enqueue({ presentation });
|
||||
await pipeline.flush({ force: true });
|
||||
|
||||
expect(sent).toEqual([{ presentation }]);
|
||||
});
|
||||
|
||||
it("does not track media when text-only blocks are delivered", async () => {
|
||||
const pipeline = createBlockReplyPipeline({
|
||||
onBlockReply: async () => {},
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
||||
import {
|
||||
hasOutboundReplyContent,
|
||||
resolveSendableOutboundReplyParts,
|
||||
} from "openclaw/plugin-sdk/reply-payload";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { getReplyPayloadMetadata } from "../reply-payload.js";
|
||||
import type { ReplyPayload } from "../types.js";
|
||||
@@ -233,8 +236,12 @@ export function createBlockReplyPipeline(params: {
|
||||
if (bufferPayload(payload)) {
|
||||
return;
|
||||
}
|
||||
const hasMedia = resolveSendableOutboundReplyParts(payload).hasMedia;
|
||||
if (hasMedia) {
|
||||
const reply = resolveSendableOutboundReplyParts(payload);
|
||||
const hasNonTextContent = hasOutboundReplyContent(
|
||||
{ ...payload, text: undefined },
|
||||
{ trimText: true },
|
||||
);
|
||||
if (reply.hasMedia || hasNonTextContent) {
|
||||
void coalescer?.flush({ force: true });
|
||||
sendPayload(payload, /* bypassSeenCheck */ false);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user