mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 21:00:44 +00:00
fix(channels): harden tool progress previews
This commit is contained in:
@@ -101,7 +101,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/Voice Call: start provider STT after Telnyx outbound conversation greetings and pass configured Telnyx voice IDs through to the speak action. Fixes #56091. Thanks @Roshan.
|
||||
- Skills: honor legacy `metadata.clawdbot` requirements and installer hints when `metadata.openclaw` is absent, so older skills no longer appear ready when required binaries are missing. Fixes #71323. Thanks @chen-zhang-cs-code.
|
||||
- Browser/config: expand `~` in `browser.executablePath` before Chromium launch, so home-relative custom browser paths no longer fail with `ENOENT`. Fixes #67264. Thanks @Quratulain-bilal.
|
||||
- Telegram/streaming: hide tool-progress status updates by default while keeping explicit `streaming.preview.toolProgress` opt-in support for edited preview messages. Fixes #71320. Thanks @neeravmakwana.
|
||||
- Channels/streaming: keep Telegram tool-progress preview updates enabled by default to match released behavior, document `streaming.preview.toolProgress: false` for disabling only those status lines, and prevent preview progress text from triggering Telegram Markdown links, Discord mentions, or Slack mrkdwn mentions. Fixes #71320. Thanks @neeravmakwana.
|
||||
- Gateway/sessions: copy the oversized `sessions.json` to a rotation backup before the atomic rewrite instead of renaming the live store away, so a crash during rotation keeps the existing session-to-transcript mapping authoritative. Fixes #68229. Thanks @jjjojoj.
|
||||
- Providers/OpenAI-compatible: strip OpenAI-only Completions `store` from proxy payloads and allow `extra_body`/`extraBody` passthrough params for provider-specific request fields. Fixes #61826 and #69717.
|
||||
- Discord/subagents: preserve thread-bound completion delivery by keeping the requester-agent announce path primary and falling back to direct thread sends only when the announce produces no visible output. (#71064) Thanks @DolencLuka.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
5c7709e1686f6ad90beaa8e34ba45e6445e34c48d598407bd837361b58c365ab config-baseline.json
|
||||
b6d1e53947fcdfbff1b99f8ec79d3814d243385a1750b7fb40b40bb30f2e2975 config-baseline.json
|
||||
98c83ce8af9ec4703726d7d673add95279be008a801b1d298982cbd9c1785747 config-baseline.core.json
|
||||
22d7cd6d8279146b2d79c9531a55b80b52a2c99c81338c508104729154fdd02d config-baseline.channel.json
|
||||
d72032762ab46b99480b57deb81130a0ab5b1401189cfbaf4f7fef4a063a7f6c config-baseline.channel.json
|
||||
86f615b7d267b03888af0af7ccb3f8232a6b636f8a741d522ff425e46729ba81 config-baseline.plugin.json
|
||||
|
||||
@@ -273,9 +273,28 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
|
||||
|
||||
- `channels.telegram.streaming` is `off | partial | block | progress` (default: `partial`)
|
||||
- `progress` maps to `partial` on Telegram (compat with cross-channel naming)
|
||||
- `streaming.preview.toolProgress` controls whether tool/progress updates reuse the same edited preview message (default: `false`). Set `true` only when visible Telegram progress updates are desired.
|
||||
- `streaming.preview.toolProgress` controls whether tool/progress updates reuse the same edited preview message (default: `true` when preview streaming is active)
|
||||
- legacy `channels.telegram.streamMode` and boolean `streaming` values are auto-mapped
|
||||
|
||||
Tool-progress preview updates are the short "Working..." lines shown while tools run, for example command execution, file reads, planning updates, or patch summaries. Telegram keeps these enabled by default to match released OpenClaw behavior from `v2026.4.22` and later. To keep the edited preview for answer text but hide tool-progress lines, set:
|
||||
|
||||
```json
|
||||
{
|
||||
"channels": {
|
||||
"telegram": {
|
||||
"streaming": {
|
||||
"mode": "partial",
|
||||
"preview": {
|
||||
"toolProgress": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use `streaming.mode: "off"` only when you want to disable Telegram preview edits entirely. Use `streaming.preview.toolProgress: false` when you only want to disable the tool-progress status lines.
|
||||
|
||||
For text-only replies:
|
||||
|
||||
- DM: OpenClaw keeps the same preview message and performs a final edit in place (no second message)
|
||||
|
||||
@@ -176,10 +176,28 @@ Preview streaming can also include **tool-progress** updates — short status li
|
||||
|
||||
Supported surfaces:
|
||||
|
||||
- **Discord** and **Slack** stream tool-progress into the live preview edit by default.
|
||||
- **Telegram** only streams tool-progress into the live preview edit when `streaming.preview.toolProgress` is explicitly enabled.
|
||||
- **Discord**, **Slack**, and **Telegram** stream tool-progress into the live preview edit by default when preview streaming is active.
|
||||
- Telegram has shipped with tool-progress preview updates enabled since `v2026.4.22`; keeping them enabled preserves that released behavior.
|
||||
- **Mattermost** already folds tool activity into its single draft preview post (see above).
|
||||
- Tool-progress edits follow the active preview streaming mode; they are skipped when preview streaming is `off` or when block streaming has taken over the message.
|
||||
- To keep preview streaming but hide tool-progress lines, set `streaming.preview.toolProgress` to `false` for that channel. To disable preview edits entirely, set `streaming.mode` to `off`.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"channels": {
|
||||
"telegram": {
|
||||
"streaming": {
|
||||
"mode": "partial",
|
||||
"preview": {
|
||||
"toolProgress": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ describe("createDiscordDraftStream", () => {
|
||||
expect(rest.post).toHaveBeenCalledWith(Routes.channelMessages("c1"), {
|
||||
body: {
|
||||
content: "first draft",
|
||||
allowed_mentions: { parse: [] },
|
||||
message_reference: {
|
||||
message_id: "parent-1",
|
||||
fail_if_not_exists: false,
|
||||
@@ -51,11 +52,42 @@ describe("createDiscordDraftStream", () => {
|
||||
},
|
||||
});
|
||||
expect(rest.patch).toHaveBeenCalledWith(Routes.channelMessage("c1", "m1"), {
|
||||
body: { content: "second draft" },
|
||||
body: { content: "second draft", allowed_mentions: { parse: [] } },
|
||||
});
|
||||
expect(stream.messageId()).toBe("m1");
|
||||
});
|
||||
|
||||
it("suppresses mentions in preview creates and edits", async () => {
|
||||
const rest = {
|
||||
post: vi.fn(async () => ({ id: "m1" })),
|
||||
patch: vi.fn(async () => undefined),
|
||||
delete: vi.fn(async () => undefined),
|
||||
};
|
||||
const stream = createDiscordDraftStream({
|
||||
rest: rest as never,
|
||||
channelId: "c1",
|
||||
throttleMs: 250,
|
||||
});
|
||||
|
||||
stream.update("working @everyone <@123>");
|
||||
await stream.flush();
|
||||
stream.update("still working @here");
|
||||
await stream.flush();
|
||||
|
||||
expect(rest.post).toHaveBeenCalledWith(Routes.channelMessages("c1"), {
|
||||
body: {
|
||||
content: "working @everyone <@123>",
|
||||
allowed_mentions: { parse: [] },
|
||||
},
|
||||
});
|
||||
expect(rest.patch).toHaveBeenCalledWith(Routes.channelMessage("c1", "m1"), {
|
||||
body: {
|
||||
content: "still working @here",
|
||||
allowed_mentions: { parse: [] },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("stops previewing and warns once text exceeds the configured limit", async () => {
|
||||
const rest = {
|
||||
post: vi.fn(async () => ({ id: "m1" })),
|
||||
|
||||
@@ -6,6 +6,7 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
/** Discord messages cap at 2000 characters. */
|
||||
const DISCORD_STREAM_MAX_CHARS = 2000;
|
||||
const DEFAULT_THROTTLE_MS = 1200;
|
||||
const DISCORD_PREVIEW_ALLOWED_MENTIONS = { parse: [] };
|
||||
|
||||
export type DiscordDraftStream = {
|
||||
update: (text: string) => void;
|
||||
@@ -76,7 +77,7 @@ export function createDiscordDraftStream(params: {
|
||||
if (streamMessageId !== undefined) {
|
||||
// Edit existing message
|
||||
await rest.patch(Routes.channelMessage(channelId, streamMessageId), {
|
||||
body: { content: trimmed },
|
||||
body: { content: trimmed, allowed_mentions: DISCORD_PREVIEW_ALLOWED_MENTIONS },
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -88,6 +89,7 @@ export function createDiscordDraftStream(params: {
|
||||
const sent = (await rest.post(Routes.channelMessages(channelId), {
|
||||
body: {
|
||||
content: trimmed,
|
||||
allowed_mentions: DISCORD_PREVIEW_ALLOWED_MENTIONS,
|
||||
...(messageReference ? { message_reference: messageReference } : {}),
|
||||
},
|
||||
})) as { id?: string } | undefined;
|
||||
|
||||
@@ -42,13 +42,14 @@ let mockedDispatchSequence: Array<{
|
||||
mediaUrls?: string[];
|
||||
};
|
||||
}> = [];
|
||||
let mockedProgressEvents: string[] = [];
|
||||
|
||||
const noop = () => {};
|
||||
const noopAsync = async () => {};
|
||||
|
||||
function createDraftStreamStub() {
|
||||
return {
|
||||
update: noop,
|
||||
update: vi.fn(),
|
||||
flush: noopAsync,
|
||||
clear: noopAsync,
|
||||
discardPending: noopAsync,
|
||||
@@ -292,7 +293,10 @@ vi.mock("../reply.runtime.js", () => ({
|
||||
markDispatchIdle: () => {},
|
||||
}),
|
||||
dispatchInboundMessage: async (params: {
|
||||
replyOptions?: { disableBlockStreaming?: boolean };
|
||||
replyOptions?: {
|
||||
disableBlockStreaming?: boolean;
|
||||
onItemEvent?: (payload: { progressText: string }) => Promise<void> | void;
|
||||
};
|
||||
dispatcher: {
|
||||
deliver: (
|
||||
payload: {
|
||||
@@ -307,6 +311,9 @@ vi.mock("../reply.runtime.js", () => ({
|
||||
};
|
||||
}) => {
|
||||
capturedReplyOptions = params.replyOptions;
|
||||
for (const progressText of mockedProgressEvents) {
|
||||
await params.replyOptions?.onItemEvent?.({ progressText });
|
||||
}
|
||||
for (const entry of mockedDispatchSequence) {
|
||||
await params.dispatcher.deliver(entry.payload, { kind: entry.kind });
|
||||
}
|
||||
@@ -344,6 +351,7 @@ describe("dispatchPreparedSlackMessage preview fallback", () => {
|
||||
mockedReplyThreadTs = THREAD_TS;
|
||||
mockedReplyThreadTsSequence = undefined;
|
||||
mockedDispatchSequence = [{ kind: "final", payload: { text: FINAL_REPLY_TEXT } }];
|
||||
mockedProgressEvents = [];
|
||||
|
||||
createSlackDraftStreamMock.mockReturnValue(createDraftStreamStub());
|
||||
finalizeSlackPreviewEditMock.mockRejectedValue(new Error("socket closed"));
|
||||
@@ -406,6 +414,19 @@ describe("dispatchPreparedSlackMessage preview fallback", () => {
|
||||
expect(capturedReplyOptions?.disableBlockStreaming).toBe(true);
|
||||
});
|
||||
|
||||
it("escapes Slack mrkdwn in tool progress preview labels", async () => {
|
||||
const draftStream = createDraftStreamStub();
|
||||
createSlackDraftStreamMock.mockReturnValueOnce(draftStream);
|
||||
mockedDispatchSequence = [];
|
||||
mockedProgressEvents = ["ran <!here> <@U123> *bold* `code` & done"];
|
||||
|
||||
await dispatchPreparedSlackMessage(createPreparedSlackMessage());
|
||||
|
||||
expect(draftStream.update).toHaveBeenCalledWith(
|
||||
"Working…\n• ran <!here> <@U123> \\*bold\\* \\`code\\` & done",
|
||||
);
|
||||
});
|
||||
|
||||
it("starts native streams in the first-reply thread for top-level channel messages", async () => {
|
||||
mockedNativeStreaming = true;
|
||||
mockedReplyThreadTs = "171234.111";
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
import { resolveSlackThreadTargets } from "../../threading.js";
|
||||
import { normalizeSlackAllowOwnerEntry } from "../allow-list.js";
|
||||
import { resolveStorePath, updateLastRoute } from "../config.runtime.js";
|
||||
import { escapeSlackMrkdwn } from "../mrkdwn.js";
|
||||
import {
|
||||
createSlackReplyDeliveryPlan,
|
||||
deliverReplies,
|
||||
@@ -891,11 +892,12 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
||||
if (!normalized) {
|
||||
return;
|
||||
}
|
||||
const escaped = escapeSlackMrkdwn(normalized);
|
||||
const previous = previewToolProgressLines.at(-1);
|
||||
if (previous === normalized) {
|
||||
if (previous === escaped) {
|
||||
return;
|
||||
}
|
||||
previewToolProgressLines = [...previewToolProgressLines, normalized].slice(-8);
|
||||
previewToolProgressLines = [...previewToolProgressLines, escaped].slice(-8);
|
||||
draftStream.update(
|
||||
["Working…", ...previewToolProgressLines.map((entry) => `• ${entry}`)].join("\n"),
|
||||
);
|
||||
|
||||
@@ -499,7 +499,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("suppresses Telegram tool progress by default", async () => {
|
||||
it("streams Telegram tool progress by default when preview streaming is active", async () => {
|
||||
const draftStream = createDraftStream();
|
||||
createTelegramDraftStream.mockReturnValue(draftStream);
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
|
||||
@@ -510,6 +510,33 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
|
||||
await dispatchWithContext({ context: createContext(), streamMode: "partial" });
|
||||
|
||||
expect(draftStream.update).toHaveBeenCalledWith(
|
||||
"Working…\n• `tool: exec`\n• `exec ls ~/Desktop`",
|
||||
);
|
||||
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);
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
|
||||
await replyOptions?.onToolStart?.({ name: "exec", phase: "start" });
|
||||
await replyOptions?.onItemEvent?.({ progressText: "exec ls ~/Desktop" });
|
||||
return { queuedFinal: false };
|
||||
});
|
||||
|
||||
await dispatchWithContext({
|
||||
context: createContext(),
|
||||
streamMode: "partial",
|
||||
telegramCfg: { streaming: { preview: { toolProgress: false } } },
|
||||
});
|
||||
|
||||
expect(draftStream.update).not.toHaveBeenCalled();
|
||||
expect(dispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@@ -520,7 +547,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("streams Telegram tool progress only when explicitly enabled", async () => {
|
||||
it("keeps Telegram tool progress links inside code formatting", async () => {
|
||||
const draftStream = createDraftStream();
|
||||
createTelegramDraftStream.mockReturnValue(draftStream);
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
|
||||
@@ -532,7 +559,6 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
await dispatchWithContext({
|
||||
context: createContext(),
|
||||
streamMode: "partial",
|
||||
telegramCfg: { streaming: { preview: { toolProgress: true } } },
|
||||
});
|
||||
|
||||
const lastPreviewText = draftStream.update.mock.calls.at(-1)?.[0];
|
||||
@@ -566,11 +592,32 @@ describe("dispatchTelegramMessage draft streaming", () => {
|
||||
const progressLine = lastPreviewText.split("\n").at(1) ?? "";
|
||||
|
||||
expect(lastPreviewText.length).toBeLessThan(340);
|
||||
expect(progressLine).toMatch(/^• `{10}/);
|
||||
expect(progressLine).toMatch(/^• `'{10}/);
|
||||
expect(progressLine).toContain("…");
|
||||
expect(renderTelegramHtmlText(lastPreviewText)).not.toContain("<a ");
|
||||
});
|
||||
|
||||
it("does not let Telegram tool progress backticks break out of code formatting", async () => {
|
||||
const draftStream = createDraftStream();
|
||||
createTelegramDraftStream.mockReturnValue(draftStream);
|
||||
const breakoutProgress = `${"`".repeat(10)} [label](tg://user?id=123)`;
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
|
||||
await replyOptions?.onItemEvent?.({ progressText: breakoutProgress });
|
||||
return { queuedFinal: false };
|
||||
});
|
||||
|
||||
await dispatchWithContext({
|
||||
context: createContext(),
|
||||
streamMode: "partial",
|
||||
telegramCfg: { streaming: { preview: { toolProgress: true } } },
|
||||
});
|
||||
|
||||
const lastPreviewText = draftStream.update.mock.calls.at(-1)?.[0] ?? "";
|
||||
|
||||
expect(lastPreviewText).toContain(`• \`'''''''''' [label](tg://user?id=123)\``);
|
||||
expect(renderTelegramHtmlText(lastPreviewText)).not.toContain("<a ");
|
||||
});
|
||||
|
||||
it("keeps block streaming enabled when account config enables it", async () => {
|
||||
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => {
|
||||
await dispatcherOptions.deliver({ text: "Hello" }, { kind: "final" });
|
||||
|
||||
@@ -208,7 +208,6 @@ function resolveTelegramReasoningLevel(params: {
|
||||
}
|
||||
|
||||
const MAX_PROGRESS_MARKDOWN_TEXT_CHARS = 300;
|
||||
const MAX_PROGRESS_MARKDOWN_FENCE_CHARS = 10;
|
||||
|
||||
function clipProgressMarkdownText(text: string): string {
|
||||
if (text.length <= MAX_PROGRESS_MARKDOWN_TEXT_CHARS) {
|
||||
@@ -219,12 +218,8 @@ function clipProgressMarkdownText(text: string): string {
|
||||
|
||||
function formatProgressAsMarkdownCode(text: string): string {
|
||||
const clipped = clipProgressMarkdownText(text);
|
||||
const maxBacktickRun = Math.max(
|
||||
0,
|
||||
...Array.from(clipped.matchAll(/`+/g), (match) => match[0].length),
|
||||
);
|
||||
const fence = "`".repeat(Math.min(maxBacktickRun + 1, MAX_PROGRESS_MARKDOWN_FENCE_CHARS));
|
||||
return `${fence}${clipped}${fence}`;
|
||||
const safe = clipped.replaceAll("`", "'");
|
||||
return `\`${safe}\``;
|
||||
}
|
||||
|
||||
export const dispatchTelegramMessage = async ({
|
||||
@@ -408,7 +403,7 @@ export const dispatchTelegramMessage = async ({
|
||||
const answerLane = lanes.answer;
|
||||
const reasoningLane = lanes.reasoning;
|
||||
const previewToolProgressEnabled =
|
||||
Boolean(answerLane.stream) && resolveChannelStreamingPreviewToolProgress(telegramCfg, false);
|
||||
Boolean(answerLane.stream) && resolveChannelStreamingPreviewToolProgress(telegramCfg);
|
||||
let previewToolProgressSuppressed = false;
|
||||
let previewToolProgressLines: string[] = [];
|
||||
const pushPreviewToolProgress = (line?: string) => {
|
||||
|
||||
@@ -63,7 +63,7 @@ export const telegramChannelConfigUiHints = {
|
||||
},
|
||||
"streaming.preview.toolProgress": {
|
||||
label: "Telegram Draft Tool Progress",
|
||||
help: "Show tool/progress activity in the live draft preview message (default: false). Enable only when visible Telegram progress updates are desired.",
|
||||
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.",
|
||||
},
|
||||
"retry.attempts": {
|
||||
label: "Telegram Retry Attempts",
|
||||
|
||||
@@ -893,6 +893,9 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
toolProgress: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
@@ -2066,6 +2069,9 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
toolProgress: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
@@ -3081,6 +3087,10 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
label: "Discord Draft Chunk Break Preference",
|
||||
help: "Preferred breakpoints for Discord draft chunks (paragraph | newline | sentence). Default: paragraph.",
|
||||
},
|
||||
"streaming.preview.toolProgress": {
|
||||
label: "Discord Draft Tool Progress",
|
||||
help: "Show tool/progress activity in the live draft preview message (default: true). Set false to keep tool updates as separate messages.",
|
||||
},
|
||||
"retry.attempts": {
|
||||
label: "Discord Retry Attempts",
|
||||
help: "Max retry attempts for outbound Discord API calls (default: 3).",
|
||||
@@ -9417,6 +9427,27 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
],
|
||||
},
|
||||
},
|
||||
groupAllowFrom: {
|
||||
type: "array",
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
type: "number",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
dmPolicy: {
|
||||
type: "string",
|
||||
enum: ["open", "allowlist", "disabled"],
|
||||
},
|
||||
groupPolicy: {
|
||||
type: "string",
|
||||
enum: ["open", "allowlist", "disabled"],
|
||||
},
|
||||
systemPrompt: {
|
||||
type: "string",
|
||||
},
|
||||
@@ -9479,42 +9510,41 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
},
|
||||
],
|
||||
},
|
||||
tts: {
|
||||
execApprovals: {
|
||||
type: "object",
|
||||
properties: {
|
||||
enabled: {
|
||||
type: "boolean",
|
||||
anyOf: [
|
||||
{
|
||||
type: "boolean",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
const: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
provider: {
|
||||
type: "string",
|
||||
},
|
||||
baseUrl: {
|
||||
type: "string",
|
||||
},
|
||||
apiKey: {
|
||||
type: "string",
|
||||
},
|
||||
model: {
|
||||
type: "string",
|
||||
},
|
||||
voice: {
|
||||
type: "string",
|
||||
},
|
||||
authStyle: {
|
||||
type: "string",
|
||||
enum: ["bearer", "api-key"],
|
||||
},
|
||||
queryParams: {
|
||||
type: "object",
|
||||
propertyNames: {
|
||||
type: "string",
|
||||
},
|
||||
additionalProperties: {
|
||||
approvers: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
speed: {
|
||||
type: "number",
|
||||
agentFilter: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
sessionFilter: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
target: {
|
||||
type: "string",
|
||||
enum: ["dm", "channel", "both"],
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
@@ -9637,6 +9667,27 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
],
|
||||
},
|
||||
},
|
||||
groupAllowFrom: {
|
||||
type: "array",
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "string",
|
||||
},
|
||||
{
|
||||
type: "number",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
dmPolicy: {
|
||||
type: "string",
|
||||
enum: ["open", "allowlist", "disabled"],
|
||||
},
|
||||
groupPolicy: {
|
||||
type: "string",
|
||||
enum: ["open", "allowlist", "disabled"],
|
||||
},
|
||||
systemPrompt: {
|
||||
type: "string",
|
||||
},
|
||||
@@ -9699,6 +9750,45 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
},
|
||||
],
|
||||
},
|
||||
execApprovals: {
|
||||
type: "object",
|
||||
properties: {
|
||||
enabled: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "boolean",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
const: "auto",
|
||||
},
|
||||
],
|
||||
},
|
||||
approvers: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
agentFilter: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
sessionFilter: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
target: {
|
||||
type: "string",
|
||||
enum: ["dm", "channel", "both"],
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
additionalProperties: {},
|
||||
},
|
||||
@@ -10866,6 +10956,9 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
toolProgress: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
@@ -11775,6 +11868,9 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
toolProgress: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
@@ -12311,6 +12407,10 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
label: "Slack Native Streaming",
|
||||
help: "Enable native Slack text streaming (chat.startStream/chat.appendStream/chat.stopStream) when channels.slack.streaming.mode is partial (default: true). Requires a reply thread target; top-level DMs stay on the non-thread fallback path.",
|
||||
},
|
||||
"streaming.preview.toolProgress": {
|
||||
label: "Slack Draft Tool Progress",
|
||||
help: "Show tool/progress activity in the live draft preview message (default: true). Set false to keep tool updates as separate messages.",
|
||||
},
|
||||
"thread.historyScope": {
|
||||
label: "Slack Thread History Scope",
|
||||
help: 'Scope for Slack thread history context ("thread" isolates per thread; "channel" reuses channel history).',
|
||||
@@ -13058,6 +13158,9 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
toolProgress: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
@@ -14096,6 +14199,9 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
toolProgress: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
@@ -14498,6 +14604,10 @@ export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = [
|
||||
label: "Telegram Draft Chunk Break Preference",
|
||||
help: "Preferred breakpoints for Telegram draft chunks (paragraph | newline | sentence).",
|
||||
},
|
||||
"streaming.preview.toolProgress": {
|
||||
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.",
|
||||
},
|
||||
"retry.attempts": {
|
||||
label: "Telegram Retry Attempts",
|
||||
help: "Max retry attempts for outbound Telegram API calls (default: 3).",
|
||||
|
||||
Reference in New Issue
Block a user