mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 11:10:45 +00:00
fix(discord): collapse cron announce text
This commit is contained in:
@@ -80,6 +80,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Discord/cron: deliver text-only isolated cron and heartbeat announce output from the canonical final assistant text once, avoiding duplicate Discord posts when streamed block payloads and the final answer contain the same content. Fixes #71406. Thanks @alexgross21.
|
||||
- Control UI/WebChat: hide heartbeat prompts, `HEARTBEAT_OK` acknowledgments, and internal-only runtime context turns from visible chat history while leaving the underlying transcript intact. Fixes #71381. Thanks @gerald1950ggg-ai.
|
||||
- Control UI/chat: keep optimistic user and assistant tail messages visible when a final history refresh briefly returns an older snapshot, preventing message cards from flash-disappearing until the next refresh. Fixes #71371. Thanks @WolvenRA.
|
||||
- Talk/TTS: resolve configured extension speech providers from the active runtime registry before provider-list discovery, so Talk mode no longer rejects valid plugin speech providers as unsupported.
|
||||
|
||||
@@ -94,6 +94,11 @@ When isolated cron runs orchestrate subagents, delivery also prefers the final
|
||||
descendant output over stale parent interim text. If descendants are still
|
||||
running, OpenClaw suppresses that partial parent update instead of announcing it.
|
||||
|
||||
For text-only Discord announce targets, OpenClaw sends the canonical final
|
||||
assistant text once instead of replaying both streamed/intermediate text payloads
|
||||
and the final answer. Media and structured Discord payloads are still delivered
|
||||
as separate payloads so attachments and components are not dropped.
|
||||
|
||||
### Payload options for isolated jobs
|
||||
|
||||
- `--message`: prompt text (required for isolated)
|
||||
|
||||
@@ -267,6 +267,9 @@ Now create some channels on your Discord server and start chatting. Your agent c
|
||||
- Guild channels are isolated session keys (`agent:<agentId>:discord:channel:<channelId>`).
|
||||
- Group DMs are ignored by default (`channels.discord.dm.groupEnabled=false`).
|
||||
- Native slash commands run in isolated command sessions (`agent:<agentId>:discord:slash:<userId>`), while still carrying `CommandTargetSessionKey` to the routed conversation session.
|
||||
- Text-only cron/heartbeat announce delivery to Discord uses the final
|
||||
assistant-visible answer once. Media and structured component payloads remain
|
||||
multi-message when the agent emits multiple deliverable payloads.
|
||||
|
||||
## Forum channels
|
||||
|
||||
|
||||
@@ -115,6 +115,10 @@ describe("discordPlugin outbound", () => {
|
||||
expect(source).not.toContain('require("./channel-actions.js")');
|
||||
});
|
||||
|
||||
it("prefers final assistant text for text-only cron announce delivery", () => {
|
||||
expect(discordPlugin.outbound?.preferFinalAssistantVisibleText).toBe(true);
|
||||
});
|
||||
|
||||
it("honors per-account replyToMode overrides", () => {
|
||||
const resolveReplyToMode = discordPlugin.threading?.resolveReplyToMode;
|
||||
if (!resolveReplyToMode) {
|
||||
|
||||
@@ -789,6 +789,7 @@ export const discordPlugin: ChannelPlugin<ResolvedDiscordAccount, DiscordProbe>
|
||||
},
|
||||
outbound: {
|
||||
...discordOutbound,
|
||||
preferFinalAssistantVisibleText: true,
|
||||
shouldTreatDeliveredTextAsVisible: shouldTreatDiscordDeliveredTextAsVisible,
|
||||
shouldSuppressLocalPayloadPrompt: ({ cfg, accountId, payload }) =>
|
||||
shouldSuppressLocalDiscordExecApprovalPrompt({
|
||||
|
||||
@@ -157,10 +157,14 @@ function resolveCoreChannelSender(
|
||||
function createCliDelegatingOutbound(params: {
|
||||
channel: CoreChannel;
|
||||
deliveryMode?: ChannelOutboundAdapter["deliveryMode"];
|
||||
preferFinalAssistantVisibleText?: boolean;
|
||||
resolveTarget?: ChannelOutboundAdapter["resolveTarget"];
|
||||
}): ChannelOutboundAdapter {
|
||||
return {
|
||||
deliveryMode: params.deliveryMode ?? "direct",
|
||||
...(params.preferFinalAssistantVisibleText !== undefined
|
||||
? { preferFinalAssistantVisibleText: params.preferFinalAssistantVisibleText }
|
||||
: {}),
|
||||
...(params.resolveTarget ? { resolveTarget: params.resolveTarget } : {}),
|
||||
sendText: async ({ cfg, to, text, accountId, deps }) =>
|
||||
withRequiredMessageId(
|
||||
@@ -239,7 +243,10 @@ describe("runCronIsolatedAgentTurn core-channel direct delivery", () => {
|
||||
pluginId: "discord",
|
||||
plugin: createOutboundTestPlugin({
|
||||
id: "discord",
|
||||
outbound: createCliDelegatingOutbound({ channel: "discord" }),
|
||||
outbound: createCliDelegatingOutbound({
|
||||
channel: "discord",
|
||||
preferFinalAssistantVisibleText: true,
|
||||
}),
|
||||
}),
|
||||
source: "test",
|
||||
},
|
||||
@@ -283,6 +290,31 @@ describe("runCronIsolatedAgentTurn core-channel direct delivery", () => {
|
||||
});
|
||||
});
|
||||
|
||||
if (testCase.channel === "discord") {
|
||||
it("collapses Discord text-only announce delivery to the final assistant text", async () => {
|
||||
await expectCoreChannelAnnounceDelivery({
|
||||
testCase,
|
||||
payloads: [{ text: "Working on it..." }, { text: "Final weather summary" }],
|
||||
meta: {
|
||||
meta: {
|
||||
durationMs: 5,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
finalAssistantVisibleText: "Final weather summary",
|
||||
},
|
||||
},
|
||||
assertSend: (sendFn) => {
|
||||
expect(sendFn).toHaveBeenCalledTimes(1);
|
||||
expect(sendFn).toHaveBeenCalledWith(
|
||||
testCase.expectedTo,
|
||||
"Final weather summary",
|
||||
expect.any(Object),
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
it(`preserves multi-payload text-only announce delivery for ${testCase.name} even when final assistant text exists`, async () => {
|
||||
await expectCoreChannelAnnounceDelivery({
|
||||
testCase,
|
||||
|
||||
Reference in New Issue
Block a user