




















@@ -12,6 +12,7 @@ import {
1212normalizeOptionalLowercaseString,
1313normalizeOptionalString,
1414} from "../../shared/string-coerce.js";
15+import { markReplyPayloadForSourceSuppressionDelivery } from "../reply-payload.js";
1516import {
1617listReservedChatSlashCommandNames,
1718resolveSkillCommandInvocation,
@@ -127,6 +128,23 @@ export type InlineActionResult =
127128cleanedBody: string;
128129};
129130131+// Command / skill-dispatch handlers ("/compact", "/status", tool-not-available
132+// errors, etc.) emit system-meta feedback for an explicit user action; they
133+// are not assistant source content. Mark them so dispatch-from-config does
134+// not silently drop them when sourceReplyDeliveryMode === "message_tool_only"
135+// (the default for many channels and group chats). See #87107.
136+function markCommandReplyForDelivery(
137+reply: ReplyPayload | ReplyPayload[] | undefined,
138+): ReplyPayload | ReplyPayload[] | undefined {
139+if (!reply) {
140+return reply;
141+}
142+if (Array.isArray(reply)) {
143+return reply.map((payload) => markReplyPayloadForSourceSuppressionDelivery(payload));
144+}
145+return markReplyPayloadForSourceSuppressionDelivery(reply);
146+}
147+130148function extractTextFromToolResult(result: unknown): string | null {
131149if (!result || typeof result !== "object") {
132150return null;
@@ -307,7 +325,12 @@ export async function handleInlineActions(params: {
307325const tool = authorizedTools.find((candidate) => candidate.name === dispatch.toolName);
308326if (!tool) {
309327typing.cleanup();
310-return { kind: "reply", reply: { text: `❌ Tool not available: ${dispatch.toolName}` } };
328+return {
329+kind: "reply",
330+reply: markCommandReplyForDelivery({
331+text: `❌ Tool not available: ${dispatch.toolName}`,
332+}),
333+};
311334}
312335313336const toolCallId = `cmd_${generateSecureToken(8)}`;
@@ -323,16 +346,19 @@ export async function handleInlineActions(params: {
323346typing.cleanup();
324347return {
325348kind: "reply",
326-reply: { text: `❌ Tool call blocked: ${blockedReason}` },
349+reply: markCommandReplyForDelivery({ text: `❌ Tool call blocked: ${blockedReason}` }),
327350};
328351}
329352const text = extractTextFromToolResult(result) ?? "✅ Done.";
330353typing.cleanup();
331-return { kind: "reply", reply: { text } };
354+return { kind: "reply", reply: markCommandReplyForDelivery({ text }) };
332355} catch (err) {
333356const message = formatErrorMessage(err);
334357typing.cleanup();
335-return { kind: "reply", reply: { text: `❌ ${message}` } };
358+return {
359+kind: "reply",
360+reply: markCommandReplyForDelivery({ text: `❌ ${message}` }),
361+};
336362}
337363}
338364@@ -494,7 +520,7 @@ export async function handleInlineActions(params: {
494520if (inlineResult.reply) {
495521if (!inlineCommand.cleaned) {
496522typing.cleanup();
497-return { kind: "reply", reply: inlineResult.reply };
523+return { kind: "reply", reply: markCommandReplyForDelivery(inlineResult.reply) };
498524}
499525await sendInlineReply(inlineResult.reply);
500526}
@@ -558,7 +584,7 @@ export async function handleInlineActions(params: {
558584const commandResult = await runCommands(command);
559585if (!commandResult.shouldContinue) {
560586typing.cleanup();
561-return { kind: "reply", reply: commandResult.reply };
587+return { kind: "reply", reply: markCommandReplyForDelivery(commandResult.reply) };
562588}
563589if (command.commandBodyNormalized !== commandBodyBeforeRun) {
564590cleanedBody = command.commandBodyNormalized;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。