fix(telegram): migrate retired native draft config

This commit is contained in:
Ayaan Zaidi
2026-06-13 17:59:58 +05:30
parent 93318050e1
commit a3bc0097c8
3 changed files with 128 additions and 1 deletions

View File

@@ -319,7 +319,7 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
- `streaming.preview.toolProgress` controls whether tool/progress updates reuse the same edited preview message (default: `true` when preview streaming is active)
- `streaming.preview.commandText` controls command/exec detail inside those tool-progress lines: `raw` (default, preserves released behavior) or `status` (tool label only)
- `streaming.progress.commentary` (default: `false`) opts into assistant commentary/preamble text in the temporary progress draft
- legacy `channels.telegram.streamMode` and boolean `streaming` values are detected; run `openclaw doctor --fix` to migrate them to `channels.telegram.streaming.mode`
- legacy `channels.telegram.streamMode`, boolean `streaming` values, and retired native draft preview keys are detected; run `openclaw doctor --fix` to migrate them to current streaming config
Tool-progress preview updates are the short status lines shown while tools run, for example command execution, file reads, planning updates, patch summaries, or Codex preamble/commentary text in Codex app-server mode. Telegram keeps these enabled by default to match released OpenClaw behavior from `v2026.4.22` and later.

View File

@@ -37,6 +37,23 @@ function hasRetiredTelegramAccountDmConfig(value: unknown): boolean {
return Object.values(accounts).some((account) => hasRetiredTelegramDmConfig(account));
}
function hasRetiredTelegramNativeDraftConfig(value: unknown): boolean {
const entry = asObjectRecord(value);
const streaming = asObjectRecord(entry?.streaming);
const preview = asObjectRecord(streaming?.preview);
return (
preview?.nativeToolProgress !== undefined || preview?.nativeToolProgressAllowFrom !== undefined
);
}
function hasRetiredTelegramAccountNativeDraftConfig(value: unknown): boolean {
const accounts = asObjectRecord(value);
if (!accounts) {
return false;
}
return Object.values(accounts).some((account) => hasRetiredTelegramNativeDraftConfig(account));
}
function removeRetiredTelegramDmConfig(params: {
entry: Record<string, unknown>;
pathPrefix: string;
@@ -82,6 +99,41 @@ function removeRetiredTelegramDmConfig(params: {
return { entry: updated, changed };
}
function removeRetiredTelegramNativeDraftConfig(params: {
entry: Record<string, unknown>;
pathPrefix: string;
changes: string[];
}): { entry: Record<string, unknown>; changed: boolean } {
const streaming = asObjectRecord(params.entry.streaming);
const preview = asObjectRecord(streaming?.preview);
if (
!streaming ||
!preview ||
(preview.nativeToolProgress === undefined && preview.nativeToolProgressAllowFrom === undefined)
) {
return { entry: params.entry, changed: false };
}
const nextPreview = { ...preview };
delete nextPreview.nativeToolProgress;
delete nextPreview.nativeToolProgressAllowFrom;
const nextStreaming = { ...streaming };
if (Object.keys(nextPreview).length > 0) {
nextStreaming.preview = nextPreview;
} else {
delete nextStreaming.preview;
}
const updated =
Object.keys(nextStreaming).length > 0
? { ...params.entry, streaming: nextStreaming }
: Object.fromEntries(Object.entries(params.entry).filter(([key]) => key !== "streaming"));
params.changes.push(
`Removed ${params.pathPrefix}.streaming.preview native draft keys; Telegram previews now use rich send/edit messages.`,
);
return { entry: updated, changed: true };
}
function resolveCompatibleDefaultGroupEntry(section: Record<string, unknown>): {
groups: Record<string, unknown>;
entry: Record<string, unknown>;
@@ -118,6 +170,18 @@ export const legacyConfigRules: ChannelDoctorLegacyConfigRule[] = [
'channels.telegram.accounts.<id>.dm and direct.<chatId>.threadReplies were removed; DM topic sessions now follow Telegram getMe.has_topics_enabled, so topics-enabled bots may use thread-scoped DM sessions. Run "openclaw doctor --fix".',
match: hasRetiredTelegramAccountDmConfig,
},
{
path: ["channels", "telegram"],
message:
'channels.telegram.streaming.preview.nativeToolProgress and nativeToolProgressAllowFrom were removed; Telegram previews now use rich send/edit messages. Run "openclaw doctor --fix".',
match: hasRetiredTelegramNativeDraftConfig,
},
{
path: ["channels", "telegram", "accounts"],
message:
'channels.telegram.accounts.<id>.streaming.preview.nativeToolProgress and nativeToolProgressAllowFrom were removed; Telegram previews now use rich send/edit messages. Run "openclaw doctor --fix".',
match: hasRetiredTelegramAccountNativeDraftConfig,
},
{
path: ["channels", "telegram"],
message:
@@ -154,6 +218,14 @@ export function normalizeCompatibilityConfig({
updated = removedThreadReplies.entry;
changed = changed || removedThreadReplies.changed;
const removedNativeDraft = removeRetiredTelegramNativeDraftConfig({
entry: updated,
pathPrefix: "channels.telegram",
changes,
});
updated = removedNativeDraft.entry;
changed = changed || removedNativeDraft.changed;
if (updated.groupMentionsOnly !== undefined) {
const defaultGroupEntry = resolveCompatibleDefaultGroupEntry(updated);
if (!defaultGroupEntry) {
@@ -210,6 +282,15 @@ export function normalizeCompatibilityConfig({
nextAccounts[accountId] = accountRemovedThreadReplies.entry;
accountsChanged = true;
}
const accountRemovedNativeDraft = removeRetiredTelegramNativeDraftConfig({
entry: nextAccounts[accountId] as Record<string, unknown>,
pathPrefix: `channels.telegram.accounts.${accountId}`,
changes,
});
if (accountRemovedNativeDraft.changed) {
nextAccounts[accountId] = accountRemovedNativeDraft.entry;
accountsChanged = true;
}
}
if (accountsChanged) {
updated = { ...updated, accounts: nextAccounts };

View File

@@ -229,6 +229,52 @@ describe("telegram doctor", () => {
]);
});
it("removes retired native draft preview keys", () => {
const normalize = telegramDoctor.normalizeCompatibilityConfig;
if (!normalize) {
throw new Error("expected telegram compatibility normalizer");
}
const result = normalize({
cfg: {
channels: {
telegram: {
streaming: {
mode: "partial",
preview: {
toolProgress: true,
nativeToolProgress: true,
nativeToolProgressAllowFrom: ["123"],
},
},
accounts: {
work: {
streaming: {
preview: {
nativeToolProgress: true,
},
},
},
},
},
},
} as never,
});
const telegram = result.config.channels?.telegram;
expect(telegram?.streaming).toEqual({
mode: "partial",
preview: {
toolProgress: true,
},
});
expect(telegram?.accounts?.work?.streaming).toBeUndefined();
expect(result.changes).toEqual([
"Removed channels.telegram.streaming.preview native draft keys; Telegram previews now use rich send/edit messages.",
"Removed channels.telegram.accounts.work.streaming.preview native draft keys; Telegram previews now use rich send/edit messages.",
]);
});
it("finds invalid allowFrom entries across scopes", () => {
const hits = scanTelegramInvalidAllowFromEntries({
channels: {