mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 13:50:49 +00:00
fix: resolve small triage issues
This commit is contained in:
@@ -65,6 +65,10 @@ export const discordChannelConfigUiHints = {
|
||||
label: "Discord Draft Tool Progress",
|
||||
help: "Show tool/progress activity in the live draft preview message (default: true). Set false to hide interim tool updates while the draft preview stays active.",
|
||||
},
|
||||
"streaming.preview.commandText": {
|
||||
label: "Discord Draft Command Text",
|
||||
help: 'Command/exec detail in preview tool-progress lines: "raw" preserves released behavior; "status" shows only the tool label.',
|
||||
},
|
||||
"streaming.progress.label": {
|
||||
label: "Discord Progress Label",
|
||||
help: 'Initial progress draft title. Use "auto" for built-in single-word labels, a custom string, or false to hide the title.',
|
||||
@@ -81,6 +85,10 @@ export const discordChannelConfigUiHints = {
|
||||
label: "Discord Progress Tool Lines",
|
||||
help: "Show compact tool/progress lines in progress draft mode (default: true). Set false to keep only the label until final delivery.",
|
||||
},
|
||||
"streaming.progress.commandText": {
|
||||
label: "Discord Progress Command Text",
|
||||
help: 'Command/exec detail in progress draft lines: "raw" preserves released behavior; "status" shows only the tool label.',
|
||||
},
|
||||
"retry.attempts": {
|
||||
label: "Discord Retry Attempts",
|
||||
help: "Max retry attempts for outbound Discord API calls (default: 3).",
|
||||
|
||||
@@ -1567,6 +1567,37 @@ describe("processDiscordMessage draft streaming", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("can hide raw command progress text in Discord progress drafts by config", async () => {
|
||||
const draftStream = createMockDraftStreamForTest();
|
||||
|
||||
dispatchInboundMessage.mockImplementationOnce(async (params?: DispatchInboundParams) => {
|
||||
await params?.replyOptions?.onToolStart?.({
|
||||
name: "exec",
|
||||
phase: "start",
|
||||
args: { command: "pnpm test -- --watch=false" },
|
||||
detailMode: "raw",
|
||||
});
|
||||
await params?.replyOptions?.onItemEvent?.({ progressText: "done" });
|
||||
return createNoQueuedDispatchResult();
|
||||
});
|
||||
|
||||
const ctx = await createAutomaticSourceDeliveryContext({
|
||||
discordConfig: {
|
||||
streaming: {
|
||||
mode: "progress",
|
||||
progress: {
|
||||
label: "Shelling",
|
||||
commandText: "status",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await runProcessDiscordMessage(ctx);
|
||||
|
||||
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n🛠️ Exec\n• done");
|
||||
});
|
||||
|
||||
it("keeps Discord progress lines across assistant boundaries", async () => {
|
||||
const draftStream = createMockDraftStreamForTest();
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from "openclaw/plugin-sdk/channel-reply-pipeline";
|
||||
import {
|
||||
formatChannelProgressDraftLine,
|
||||
formatChannelProgressDraftLineForEntry,
|
||||
resolveChannelStreamingBlockEnabled,
|
||||
} from "openclaw/plugin-sdk/channel-streaming";
|
||||
import { recordInboundSession } from "openclaw/plugin-sdk/conversation-runtime";
|
||||
@@ -669,7 +670,8 @@ export async function processDiscordMessage(
|
||||
await maybeBindStatusReactionsToToolReaction(payload);
|
||||
await statusReactions.setTool(payload.name);
|
||||
await draftPreview.pushToolProgress(
|
||||
formatChannelProgressDraftLine(
|
||||
formatChannelProgressDraftLineForEntry(
|
||||
discordConfig,
|
||||
{
|
||||
event: "tool",
|
||||
name: payload.name,
|
||||
@@ -683,7 +685,7 @@ export async function processDiscordMessage(
|
||||
},
|
||||
onItemEvent: async (payload) => {
|
||||
await draftPreview.pushToolProgress(
|
||||
formatChannelProgressDraftLine({
|
||||
formatChannelProgressDraftLineForEntry(discordConfig, {
|
||||
event: "item",
|
||||
itemKind: payload.kind,
|
||||
title: payload.title,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { logTypingFailure } from "openclaw/plugin-sdk/channel-feedback";
|
||||
import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pipeline";
|
||||
import {
|
||||
formatChannelProgressDraftLine,
|
||||
formatChannelProgressDraftLineForEntry,
|
||||
isChannelProgressDraftWorkToolName,
|
||||
} from "openclaw/plugin-sdk/channel-streaming";
|
||||
import {
|
||||
@@ -708,7 +708,8 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
|
||||
if (!isChannelProgressDraftWorkToolName(payload.name)) {
|
||||
return;
|
||||
}
|
||||
const statusLine = formatChannelProgressDraftLine(
|
||||
const statusLine = formatChannelProgressDraftLineForEntry(
|
||||
account.config,
|
||||
{
|
||||
event: "tool",
|
||||
name: payload.name,
|
||||
|
||||
@@ -17,4 +17,8 @@ export const matrixChannelConfigUiHints = {
|
||||
label: "Matrix Progress Tool Lines",
|
||||
help: "Show compact tool/progress lines in progress draft mode (default: true). Set false to keep only the label until final delivery.",
|
||||
},
|
||||
"streaming.progress.commandText": {
|
||||
label: "Matrix Progress Command Text",
|
||||
help: 'Command/exec detail in progress draft lines: "raw" preserves released behavior; "status" shows only the tool label.',
|
||||
},
|
||||
} satisfies Record<string, ChannelConfigUiHint>;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
createChannelProgressDraftGate,
|
||||
formatChannelProgressDraftLine,
|
||||
formatChannelProgressDraftLineForEntry,
|
||||
formatChannelProgressDraftText,
|
||||
isChannelProgressDraftWorkToolName,
|
||||
resolveChannelProgressDraftMaxLines,
|
||||
@@ -1580,7 +1581,8 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
||||
onToolStart: async (payload) => {
|
||||
const toolName = payload.name?.trim();
|
||||
await pushPreviewToolProgress(
|
||||
formatChannelProgressDraftLine(
|
||||
formatChannelProgressDraftLineForEntry(
|
||||
progressConfigEntry,
|
||||
{
|
||||
event: "tool",
|
||||
name: toolName,
|
||||
@@ -1594,7 +1596,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
||||
},
|
||||
onItemEvent: async (payload) => {
|
||||
await pushPreviewToolProgress(
|
||||
formatChannelProgressDraftLine({
|
||||
formatChannelProgressDraftLineForEntry(progressConfigEntry, {
|
||||
event: "item",
|
||||
itemKind: payload.kind,
|
||||
title: payload.title,
|
||||
|
||||
@@ -33,10 +33,18 @@ export const mattermostChannelConfigUiHints = {
|
||||
label: "Mattermost Progress Tool Lines",
|
||||
help: "Show compact tool/progress lines in progress draft mode (default: true). Set false to keep only the label until final delivery.",
|
||||
},
|
||||
"streaming.progress.commandText": {
|
||||
label: "Mattermost Progress Command Text",
|
||||
help: 'Command/exec detail in progress draft lines: "raw" preserves released behavior; "status" shows only the tool label.',
|
||||
},
|
||||
"streaming.preview.toolProgress": {
|
||||
label: "Mattermost Draft Tool Progress",
|
||||
help: "Show tool/progress activity in the live draft preview post (default: true). Set false to hide interim tool updates while the draft preview stays active.",
|
||||
},
|
||||
"streaming.preview.commandText": {
|
||||
label: "Mattermost Draft Command Text",
|
||||
help: 'Command/exec detail in preview tool-progress lines: "raw" preserves released behavior; "status" shows only the tool label.',
|
||||
},
|
||||
"streaming.block.enabled": {
|
||||
label: "Mattermost Block Streaming Enabled",
|
||||
help: 'Enable chunked block-style Mattermost preview delivery when channels.mattermost.streaming.mode="block".',
|
||||
|
||||
@@ -256,4 +256,15 @@ describe("buildMattermostToolStatusText", () => {
|
||||
}),
|
||||
).toBe("🛠️ Exec: run tests, `pnpm test -- --watch=false`");
|
||||
});
|
||||
|
||||
it("can hide raw exec detail from status text", () => {
|
||||
expect(
|
||||
buildMattermostToolStatusText({
|
||||
name: "exec",
|
||||
args: { command: "pnpm test -- --watch=false" },
|
||||
detailMode: "raw",
|
||||
config: { streaming: { preview: { commandText: "status" } } },
|
||||
}),
|
||||
).toBe("🛠️ Exec");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createFinalizableDraftLifecycle } from "openclaw/plugin-sdk/channel-lifecycle";
|
||||
import { formatChannelProgressDraftLine } from "openclaw/plugin-sdk/channel-streaming";
|
||||
import { formatChannelProgressDraftLineForEntry } from "openclaw/plugin-sdk/channel-streaming";
|
||||
import {
|
||||
createMattermostPost,
|
||||
deleteMattermostPost,
|
||||
@@ -37,9 +37,11 @@ export function buildMattermostToolStatusText(params: {
|
||||
phase?: string;
|
||||
args?: Record<string, unknown>;
|
||||
detailMode?: "explain" | "raw";
|
||||
config?: Parameters<typeof formatChannelProgressDraftLineForEntry>[0];
|
||||
}): string {
|
||||
return (
|
||||
formatChannelProgressDraftLine(
|
||||
formatChannelProgressDraftLineForEntry(
|
||||
params.config,
|
||||
{
|
||||
event: "tool",
|
||||
name: params.name,
|
||||
|
||||
@@ -1876,7 +1876,12 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
|
||||
if (!draftToolProgressEnabled) {
|
||||
return;
|
||||
}
|
||||
draftStream.update(buildMattermostToolStatusText(payload));
|
||||
draftStream.update(
|
||||
buildMattermostToolStatusText({
|
||||
...payload,
|
||||
config: account.config,
|
||||
}),
|
||||
);
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -29,4 +29,8 @@ export const msTeamsChannelConfigUiHints = {
|
||||
label: "MS Teams Progress Tool Lines",
|
||||
help: "Show compact tool/progress lines in progress mode (default: true). Set false to keep only the title until final delivery.",
|
||||
},
|
||||
"streaming.progress.commandText": {
|
||||
label: "MS Teams Progress Command Text",
|
||||
help: 'Command/exec detail in progress lines: "raw" preserves released behavior; "status" shows only the tool label.',
|
||||
},
|
||||
} satisfies Record<string, ChannelConfigUiHint>;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
formatChannelProgressDraftLine,
|
||||
formatChannelProgressDraftLineForEntry,
|
||||
resolveChannelPreviewStreamMode,
|
||||
resolveChannelStreamingBlockEnabled,
|
||||
} from "openclaw/plugin-sdk/channel-streaming";
|
||||
@@ -384,7 +385,8 @@ export function createMSTeamsReplyDispatcher(params: {
|
||||
detailMode?: "explain" | "raw";
|
||||
}) => {
|
||||
await streamController.pushProgressLine(
|
||||
formatChannelProgressDraftLine(
|
||||
formatChannelProgressDraftLineForEntry(
|
||||
msteamsCfg,
|
||||
{
|
||||
event: "tool",
|
||||
name: payload.name,
|
||||
@@ -407,7 +409,7 @@ export function createMSTeamsReplyDispatcher(params: {
|
||||
status?: string;
|
||||
}) => {
|
||||
await streamController.pushProgressLine(
|
||||
formatChannelProgressDraftLine({
|
||||
formatChannelProgressDraftLineForEntry(msteamsCfg, {
|
||||
event: "item",
|
||||
itemKind: payload.kind,
|
||||
title: payload.title,
|
||||
|
||||
@@ -117,6 +117,10 @@ export const slackChannelConfigUiHints = {
|
||||
label: "Slack Draft Tool Progress",
|
||||
help: "Show tool/progress activity in the live draft preview message (default: true). Set false to hide interim tool updates while the draft preview stays active.",
|
||||
},
|
||||
"streaming.preview.commandText": {
|
||||
label: "Slack Draft Command Text",
|
||||
help: 'Command/exec detail in preview tool-progress lines: "raw" preserves released behavior; "status" shows only the tool label.',
|
||||
},
|
||||
"streaming.progress.label": {
|
||||
label: "Slack Progress Label",
|
||||
help: 'Initial progress draft title. Use "auto" for built-in single-word labels, a custom string, or false to hide the title.',
|
||||
@@ -137,6 +141,10 @@ export const slackChannelConfigUiHints = {
|
||||
label: "Slack Progress Tool Lines",
|
||||
help: "Show compact tool/progress lines in progress draft mode (default: true). Set false to keep only the label until final delivery.",
|
||||
},
|
||||
"streaming.progress.commandText": {
|
||||
label: "Slack Progress Command Text",
|
||||
help: 'Command/exec detail in progress draft lines: "raw" preserves released behavior; "status" shows only the tool label.',
|
||||
},
|
||||
"thread.historyScope": {
|
||||
label: "Slack Thread History Scope",
|
||||
help: 'Scope for Slack thread history context ("thread" isolates per thread; "channel" reuses channel history).',
|
||||
|
||||
@@ -37,7 +37,16 @@ let capturedReplyOptions:
|
||||
| {
|
||||
disableBlockStreaming?: boolean;
|
||||
suppressDefaultToolProgressMessages?: boolean;
|
||||
onItemEvent?: (payload: { progressText: string }) => Promise<void> | void;
|
||||
onItemEvent?: (payload: {
|
||||
kind?: string;
|
||||
progressText?: string;
|
||||
summary?: string;
|
||||
title?: string;
|
||||
name?: string;
|
||||
phase?: string;
|
||||
status?: string;
|
||||
meta?: string;
|
||||
}) => Promise<void> | void;
|
||||
onPartialReply?: (payload: { text: string }) => Promise<void> | void;
|
||||
}
|
||||
| undefined;
|
||||
@@ -73,7 +82,18 @@ let mockedDispatchSequence: Array<{
|
||||
}> = [];
|
||||
let mockedProgressEvents: string[] = [];
|
||||
let mockedReplyOptionEvents: Array<
|
||||
{ kind: "item"; progressText: string } | { kind: "partial"; text: string }
|
||||
| {
|
||||
kind: "item";
|
||||
itemKind?: string;
|
||||
progressText?: string;
|
||||
summary?: string;
|
||||
title?: string;
|
||||
name?: string;
|
||||
phase?: string;
|
||||
status?: string;
|
||||
meta?: string;
|
||||
}
|
||||
| { kind: "partial"; text: string }
|
||||
> = [];
|
||||
|
||||
const noop = () => {};
|
||||
@@ -246,6 +266,41 @@ vi.mock("openclaw/plugin-sdk/channel-streaming", () => ({
|
||||
}
|
||||
: undefined;
|
||||
},
|
||||
buildChannelProgressDraftLineForEntry: (
|
||||
entry: {
|
||||
streaming?: {
|
||||
progress?: { commandText?: "raw" | "status" };
|
||||
preview?: { commandText?: "raw" | "status" };
|
||||
};
|
||||
},
|
||||
params: {
|
||||
itemKind?: string;
|
||||
progressText?: string;
|
||||
summary?: string;
|
||||
title?: string;
|
||||
name?: string;
|
||||
},
|
||||
) => {
|
||||
if (
|
||||
(entry.streaming?.progress?.commandText ?? entry.streaming?.preview?.commandText) ===
|
||||
"status" &&
|
||||
(params.itemKind === "command" || params.name === "exec")
|
||||
) {
|
||||
return {
|
||||
kind: "item",
|
||||
text: "🛠️ Exec",
|
||||
label: "Exec",
|
||||
};
|
||||
}
|
||||
const text = params.progressText ?? params.summary ?? params.title ?? params.name;
|
||||
return text
|
||||
? {
|
||||
kind: "item",
|
||||
text,
|
||||
label: params.title ?? params.name ?? "Update",
|
||||
}
|
||||
: undefined;
|
||||
},
|
||||
createChannelProgressDraftGate: (params: { onStart: () => void | Promise<void> }) => {
|
||||
let started = false;
|
||||
let workEvents = 0;
|
||||
@@ -290,6 +345,15 @@ vi.mock("openclaw/plugin-sdk/channel-streaming", () => ({
|
||||
title?: string;
|
||||
name?: string;
|
||||
}) => params.progressText ?? params.summary ?? params.title ?? params.name,
|
||||
formatChannelProgressDraftLineForEntry: (
|
||||
_entry: unknown,
|
||||
params: {
|
||||
progressText?: string;
|
||||
summary?: string;
|
||||
title?: string;
|
||||
name?: string;
|
||||
},
|
||||
) => params.progressText ?? params.summary ?? params.title ?? params.name,
|
||||
resolveChannelProgressDraftMaxLines: (entry?: {
|
||||
streaming?: { progress?: { maxLines?: number } };
|
||||
}) => entry?.streaming?.progress?.maxLines ?? 8,
|
||||
@@ -472,7 +536,16 @@ vi.mock("../reply.runtime.js", () => ({
|
||||
replyOptions?: {
|
||||
disableBlockStreaming?: boolean;
|
||||
suppressDefaultToolProgressMessages?: boolean;
|
||||
onItemEvent?: (payload: { progressText: string }) => Promise<void> | void;
|
||||
onItemEvent?: (payload: {
|
||||
kind?: string;
|
||||
progressText?: string;
|
||||
summary?: string;
|
||||
title?: string;
|
||||
name?: string;
|
||||
phase?: string;
|
||||
status?: string;
|
||||
meta?: string;
|
||||
}) => Promise<void> | void;
|
||||
onPartialReply?: (payload: { text: string }) => Promise<void> | void;
|
||||
};
|
||||
dispatcher: {
|
||||
@@ -492,7 +565,16 @@ vi.mock("../reply.runtime.js", () => ({
|
||||
if (mockedReplyOptionEvents.length > 0) {
|
||||
for (const entry of mockedReplyOptionEvents) {
|
||||
if (entry.kind === "item") {
|
||||
await params.replyOptions?.onItemEvent?.({ progressText: entry.progressText });
|
||||
await params.replyOptions?.onItemEvent?.({
|
||||
kind: entry.itemKind,
|
||||
progressText: entry.progressText,
|
||||
summary: entry.summary,
|
||||
title: entry.title,
|
||||
name: entry.name,
|
||||
phase: entry.phase,
|
||||
status: entry.status,
|
||||
meta: entry.meta,
|
||||
});
|
||||
} else {
|
||||
await params.replyOptions?.onPartialReply?.({ text: entry.text });
|
||||
}
|
||||
@@ -749,6 +831,34 @@ describe("dispatchPreparedSlackMessage preview fallback", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("can hide raw Slack command progress text by config", async () => {
|
||||
const draftStream = createDraftStreamStub();
|
||||
createSlackDraftStreamMock.mockReturnValueOnce(draftStream);
|
||||
mockedSlackStreamingMode = "progress";
|
||||
mockedSlackDraftMode = "status_final";
|
||||
mockedDispatchSequence = [];
|
||||
mockedReplyOptionEvents = [
|
||||
{
|
||||
kind: "item",
|
||||
itemKind: "command",
|
||||
name: "exec",
|
||||
progressText: "exec pnpm test -- --watch=false",
|
||||
},
|
||||
{ kind: "item", progressText: "done" },
|
||||
];
|
||||
|
||||
await dispatchPreparedSlackMessage(
|
||||
createPreparedSlackMessage({
|
||||
accountConfig: {
|
||||
streaming: { mode: "progress", progress: { label: "Shelling", commandText: "status" } },
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n• 🛠️ Exec\n• done");
|
||||
expect(draftStream.update.mock.calls.flat().join("\n")).not.toContain("pnpm test");
|
||||
});
|
||||
|
||||
it("suppresses standalone Slack tool progress when progress lines are disabled", async () => {
|
||||
mockedSlackStreamingMode = "progress";
|
||||
mockedSlackDraftMode = "status_final";
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from "openclaw/plugin-sdk/channel-reply-pipeline";
|
||||
import {
|
||||
buildChannelProgressDraftLine,
|
||||
buildChannelProgressDraftLineForEntry,
|
||||
createChannelProgressDraftGate,
|
||||
formatChannelProgressDraftText,
|
||||
isChannelProgressDraftWorkToolName,
|
||||
@@ -1108,7 +1109,8 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
await statusReactions.setTool(payload.name);
|
||||
}
|
||||
await pushPreviewToolProgress(
|
||||
buildChannelProgressDraftLine(
|
||||
buildChannelProgressDraftLineForEntry(
|
||||
account.config,
|
||||
{
|
||||
event: "tool",
|
||||
name: payload.name,
|
||||
@@ -1122,7 +1124,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
},
|
||||
onItemEvent: async (payload) => {
|
||||
await pushPreviewToolProgress(
|
||||
buildChannelProgressDraftLine({
|
||||
buildChannelProgressDraftLineForEntry(account.config, {
|
||||
event: "item",
|
||||
itemKind: payload.kind,
|
||||
title: payload.title,
|
||||
|
||||
@@ -780,14 +780,14 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps the Telegram progress draft across post-tool assistant boundaries", async () => {
|
||||
it("keeps non-command Telegram progress draft lines across post-tool assistant boundaries", async () => {
|
||||
const draftStream = createSequencedDraftStream(2001);
|
||||
createTelegramDraftStream.mockReturnValue(draftStream);
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(
|
||||
async ({ dispatcherOptions, replyOptions }) => {
|
||||
await replyOptions?.onReplyStart?.();
|
||||
await replyOptions?.onAssistantMessageStart?.();
|
||||
await replyOptions?.onItemEvent?.({ progressText: "exec ls ~/Desktop" });
|
||||
await replyOptions?.onItemEvent?.({ kind: "search", progressText: "docs lookup" });
|
||||
await replyOptions?.onItemEvent?.({ progressText: "tests passed" });
|
||||
await replyOptions?.onAssistantMessageStart?.();
|
||||
await dispatcherOptions.deliver({ text: "Final after tool" }, { kind: "final" });
|
||||
@@ -802,7 +802,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
});
|
||||
|
||||
expect(draftStream.update).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/^Shelling\n• `exec ls ~\/Desktop`\n• `tests passed`$/),
|
||||
expect.stringMatching(/^Shelling\n`🔎 Web Search: docs lookup`\n• `tests passed`$/),
|
||||
);
|
||||
expect(draftStream.forceNewMessage).not.toHaveBeenCalled();
|
||||
expect(draftStream.materialize).not.toHaveBeenCalled();
|
||||
@@ -815,19 +815,23 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
expect(draftStream.clear).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("streams Telegram tool progress by default when preview streaming is active", async () => {
|
||||
it("streams Telegram command progress text by default when preview streaming is active", async () => {
|
||||
const draftStream = createDraftStream();
|
||||
createTelegramDraftStream.mockReturnValue(draftStream);
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
|
||||
await replyOptions?.onToolStart?.({ name: "exec", phase: "start" });
|
||||
await replyOptions?.onItemEvent?.({ progressText: "exec ls ~/Desktop" });
|
||||
await replyOptions?.onItemEvent?.({
|
||||
kind: "command",
|
||||
name: "exec",
|
||||
progressText: "exec ls ~/Desktop",
|
||||
});
|
||||
return { queuedFinal: false };
|
||||
});
|
||||
|
||||
await dispatchWithContext({ context: createContext(), streamMode: "partial" });
|
||||
|
||||
expect(draftStream.update).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/\n`🛠️ Exec`\n• `exec ls ~\/Desktop`$/),
|
||||
expect.stringMatching(/\n`🛠️ Exec`\n`🛠️ Exec: exec ls ~\/Desktop`$/),
|
||||
);
|
||||
expect(dispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@@ -838,6 +842,36 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("can hide Telegram command progress text by config", async () => {
|
||||
const draftStream = createDraftStream();
|
||||
createTelegramDraftStream.mockReturnValue(draftStream);
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
|
||||
await replyOptions?.onToolStart?.({ name: "exec", phase: "start" });
|
||||
await replyOptions?.onItemEvent?.({
|
||||
kind: "command",
|
||||
name: "exec",
|
||||
progressText: "exec ls ~/Desktop",
|
||||
});
|
||||
return { queuedFinal: false };
|
||||
});
|
||||
|
||||
await dispatchWithContext({
|
||||
context: createContext(),
|
||||
streamMode: "partial",
|
||||
telegramCfg: { streaming: { mode: "partial", preview: { commandText: "status" } } },
|
||||
});
|
||||
|
||||
expect(draftStream.update).toHaveBeenCalledWith(expect.stringMatching(/\n`🛠️ Exec`$/));
|
||||
expect(draftStream.update.mock.calls.at(-1)?.[0]).not.toContain("exec ls");
|
||||
expect(dispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
replyOptions: expect.objectContaining({
|
||||
suppressDefaultToolProgressMessages: true,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("suppresses Telegram tool progress when explicitly disabled", async () => {
|
||||
const draftStream = createDraftStream();
|
||||
createTelegramDraftStream.mockReturnValue(draftStream);
|
||||
@@ -882,12 +916,15 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps Telegram tool progress links inside code formatting", async () => {
|
||||
it("keeps non-command Telegram tool progress links inside code formatting", async () => {
|
||||
const draftStream = createDraftStream();
|
||||
createTelegramDraftStream.mockReturnValue(draftStream);
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
|
||||
await replyOptions?.onToolStart?.({ name: "exec", phase: "start" });
|
||||
await replyOptions?.onItemEvent?.({ progressText: "read [label](tg://user?id=123)" });
|
||||
await replyOptions?.onItemEvent?.({
|
||||
kind: "search",
|
||||
progressText: "read [label](tg://user?id=123)",
|
||||
});
|
||||
return { queuedFinal: false };
|
||||
});
|
||||
|
||||
@@ -897,7 +934,9 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
});
|
||||
|
||||
const lastPreviewText = draftStream.update.mock.calls.at(-1)?.[0];
|
||||
expect(lastPreviewText).toMatch(/\n`🛠️ Exec`\n• `read \[label\]\(tg:\/\/user\?id=123\)`$/);
|
||||
expect(lastPreviewText).toMatch(
|
||||
/\n`🛠️ Exec`\n`🔎 Web Search: read \[label\]\(tg:\/\/user\?id=123\)`$/,
|
||||
);
|
||||
expect(renderTelegramHtmlText(lastPreviewText ?? "")).not.toContain("<a ");
|
||||
expect(dispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@@ -927,7 +966,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
const progressLine = lastPreviewText.split("\n").at(1) ?? "";
|
||||
|
||||
expect(lastPreviewText.length).toBeLessThan(340);
|
||||
expect(progressLine).toMatch(/^• `'{10}/);
|
||||
expect(progressLine).toMatch(/^• `.*…`$/);
|
||||
expect(progressLine).toContain("…");
|
||||
expect(renderTelegramHtmlText(lastPreviewText)).not.toContain("<a ");
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pi
|
||||
import {
|
||||
createChannelProgressDraftGate,
|
||||
formatChannelProgressDraftLine,
|
||||
formatChannelProgressDraftLineForEntry,
|
||||
formatChannelProgressDraftText,
|
||||
isChannelProgressDraftWorkToolName,
|
||||
resolveChannelProgressDraftMaxLines,
|
||||
@@ -1173,7 +1174,8 @@ export const dispatchTelegramMessage = async ({
|
||||
await statusReactionController.setTool(toolName);
|
||||
}
|
||||
await pushPreviewToolProgress(
|
||||
formatChannelProgressDraftLine(
|
||||
formatChannelProgressDraftLineForEntry(
|
||||
telegramCfg,
|
||||
{
|
||||
event: "tool",
|
||||
name: toolName,
|
||||
@@ -1187,7 +1189,7 @@ export const dispatchTelegramMessage = async ({
|
||||
},
|
||||
onItemEvent: async (payload) => {
|
||||
await pushPreviewToolProgress(
|
||||
formatChannelProgressDraftLine({
|
||||
formatChannelProgressDraftLineForEntry(telegramCfg, {
|
||||
event: "item",
|
||||
itemKind: payload.kind,
|
||||
title: payload.title,
|
||||
|
||||
@@ -73,6 +73,10 @@ export const telegramChannelConfigUiHints = {
|
||||
label: "Telegram Draft Tool Progress",
|
||||
help: "Show tool/progress activity in the live draft preview message (default: true when preview streaming is active). Set false to keep tool updates out of the edited Telegram preview.",
|
||||
},
|
||||
"streaming.preview.commandText": {
|
||||
label: "Telegram Draft Command Text",
|
||||
help: 'Command/exec detail in preview tool-progress lines: "raw" preserves released behavior; "status" shows only the tool label.',
|
||||
},
|
||||
"streaming.progress.label": {
|
||||
label: "Telegram Progress Label",
|
||||
help: 'Initial progress draft title. Use "auto" for built-in single-word labels, a custom string, or false to hide the title.',
|
||||
@@ -89,6 +93,10 @@ export const telegramChannelConfigUiHints = {
|
||||
label: "Telegram Progress Tool Lines",
|
||||
help: "Show compact tool/progress lines in progress draft mode (default: true). Set false to keep only the label until final delivery.",
|
||||
},
|
||||
"streaming.progress.commandText": {
|
||||
label: "Telegram Progress Command Text",
|
||||
help: 'Command/exec detail in progress draft lines: "raw" preserves released behavior; "status" shows only the tool label.',
|
||||
},
|
||||
"retry.attempts": {
|
||||
label: "Telegram Retry Attempts",
|
||||
help: "Max retry attempts for outbound Telegram API calls (default: 3).",
|
||||
|
||||
Reference in New Issue
Block a user