






















@@ -13,6 +13,7 @@ import { DEFAULT_QA_LIVE_PROVIDER_MODE } from "../../providers/index.js";
1313import {
1414defaultQaModelForMode,
1515normalizeQaProviderMode,
16+type QaProviderMode,
1617type QaProviderModeInput,
1718} from "../../run-config.js";
1819import {
@@ -46,11 +47,14 @@ type TelegramQaScenarioId =
4647| "telegram-commands-command"
4748| "telegram-tools-compact-command"
4849| "telegram-whoami-command"
50+| "telegram-status-command"
51+| "telegram-other-bot-command-gating"
4952| "telegram-context-command"
5053| "telegram-current-session-status-tool"
5154| "telegram-stream-final-single-message"
5255| "telegram-long-final-three-chunks"
5356| "telegram-long-final-reuses-preview"
57+| "telegram-reply-chain-exact-marker"
5458| "telegram-mentioned-message-reply"
5559| "telegram-mention-gating";
5660@@ -69,6 +73,9 @@ type TelegramQaScenarioRun = {
6973type TelegramQaScenarioDefinition = LiveTransportScenarioDefinition<TelegramQaScenarioId> & {
7074buildRun: (sutUsername: string) => TelegramQaScenarioRun;
7175defaultEnabled?: boolean;
76+defaultProviderModes?: readonly QaProviderMode[];
77+regressionRefs?: readonly string[];
78+rationale: string;
7279};
73807481type TelegramObservedMessage = {
@@ -231,6 +238,7 @@ const TELEGRAM_QA_SCENARIOS: TelegramQaScenarioDefinition[] = [
231238id: "telegram-help-command",
232239standardId: "help-command",
233240title: "Telegram help command reply",
241+rationale: "Canary-grade native command reply path.",
234242timeoutMs: 45_000,
235243buildRun: (sutUsername) => ({
236244expectReply: true,
@@ -241,6 +249,7 @@ const TELEGRAM_QA_SCENARIOS: TelegramQaScenarioDefinition[] = [
241249{
242250id: "telegram-commands-command",
243251title: "Telegram commands list reply",
252+rationale: "Native command catalog must render in Telegram group replies.",
244253timeoutMs: 45_000,
245254buildRun: (sutUsername) => ({
246255expectReply: true,
@@ -251,6 +260,7 @@ const TELEGRAM_QA_SCENARIOS: TelegramQaScenarioDefinition[] = [
251260{
252261id: "telegram-tools-compact-command",
253262title: "Telegram tools compact reply",
263+rationale: "Tool catalog rendering catches command dispatch plus model-tool inventory drift.",
254264timeoutMs: 45_000,
255265buildRun: (sutUsername) => ({
256266expectReply: true,
@@ -261,16 +271,40 @@ const TELEGRAM_QA_SCENARIOS: TelegramQaScenarioDefinition[] = [
261271{
262272id: "telegram-whoami-command",
263273title: "Telegram whoami reply",
274+rationale: "Identity command proves Telegram channel context is attached to native commands.",
264275timeoutMs: 45_000,
265276buildRun: (sutUsername) => ({
266277expectReply: true,
267278input: `/whoami@${sutUsername}`,
268279expectedTextIncludes: ["🧭 Identity", "Channel: telegram"],
269280}),
270281},
282+{
283+id: "telegram-status-command",
284+title: "Telegram status command reply",
285+rationale: "Recent Telegram group regressions broke /status while normal chat still worked.",
286+regressionRefs: ["openclaw/openclaw#74698"],
287+timeoutMs: 45_000,
288+buildRun: (sutUsername) => ({
289+expectReply: true,
290+input: `/status@${sutUsername}`,
291+expectedTextIncludes: ["OpenClaw", "Model:", "Session:", "Activation:"],
292+}),
293+},
294+{
295+id: "telegram-other-bot-command-gating",
296+title: "Telegram command addressed to another bot is ignored",
297+rationale: "Bot-to-bot groups must not let commands addressed to another bot wake the SUT.",
298+timeoutMs: 8_000,
299+buildRun: () => ({
300+expectReply: false,
301+input: "/status@OpenClawQaOtherBot",
302+}),
303+},
271304{
272305id: "telegram-context-command",
273306title: "Telegram context reply",
307+rationale: "Context command exercises native command routing into Telegram-specific help text.",
274308timeoutMs: 45_000,
275309buildRun: (sutUsername) => ({
276310expectReply: true,
@@ -282,29 +316,49 @@ const TELEGRAM_QA_SCENARIOS: TelegramQaScenarioDefinition[] = [
282316id: "telegram-current-session-status-tool",
283317title: "Telegram current session_status tool call",
284318defaultEnabled: false,
319+rationale:
320+"Opt-in threaded probe for current Telegram group session resolution through model tools.",
285321timeoutMs: 60_000,
286322buildRun: (sutUsername) => ({
287323expectReply: true,
288324input: `@${sutUsername} Telegram current session_status QA check. Call session_status with sessionKey set to current, then reply with the exact QA marker and resolved session key.`,
289325expectedTextIncludes: ["QA-TELEGRAM-CURRENT-SESSION-OK", ":telegram:group:"],
326+replyToLatestSutMessage: true,
290327}),
291328},
292329{
293330id: "telegram-mentioned-message-reply",
294331title: "Telegram mentioned message gets a reply",
295-defaultEnabled: false,
332+rationale: "Bot-to-bot group mention routing must produce a threaded SUT reply.",
296333timeoutMs: 45_000,
297334buildRun: (sutUsername) => ({
298-allowAnySutReply: true,
299335expectReply: true,
300336input: `@${sutUsername} Telegram QA mention routing check. Reply with a short acknowledgement.`,
301337replyToLatestSutMessage: true,
302338}),
303339},
340+{
341+id: "telegram-reply-chain-exact-marker",
342+title: "Telegram reply-chain exact marker",
343+defaultProviderModes: ["mock-openai"],
344+rationale: "Mock-backed reply-chain check proves quoted bot-to-bot follow-ups keep threading.",
345+timeoutMs: 45_000,
346+buildRun: (sutUsername) => ({
347+expectReply: true,
348+input: `@${sutUsername} Telegram reply-chain marker QA. Reply exactly: QA-TELEGRAM-REPLY-CHAIN-OK`,
349+expectedTextIncludes: ["QA-TELEGRAM-REPLY-CHAIN-OK"],
350+expectedJoinedSutTextIncludes: ["QA-TELEGRAM-REPLY-CHAIN-OK"],
351+expectedSutMessageCount: 1,
352+replyToLatestSutMessage: true,
353+settleMs: 4_000,
354+}),
355+},
304356{
305357id: "telegram-stream-final-single-message",
306358title: "Telegram streamed final stays one message",
307-defaultEnabled: false,
359+defaultProviderModes: ["mock-openai"],
360+rationale: "Regression guard for duplicate final replies from Telegram streaming paths.",
361+regressionRefs: ["openclaw/openclaw#39905"],
308362timeoutMs: 45_000,
309363buildRun: (sutUsername) => ({
310364allowAnySutReply: true,
@@ -320,7 +374,9 @@ const TELEGRAM_QA_SCENARIOS: TelegramQaScenarioDefinition[] = [
320374{
321375id: "telegram-long-final-reuses-preview",
322376title: "Telegram long final reuses the preview message",
323-defaultEnabled: false,
377+defaultProviderModes: ["mock-openai"],
378+rationale: "Regression guard for long streamed finals leaving stale preview messages behind.",
379+regressionRefs: ["openclaw/openclaw#39905"],
324380timeoutMs: 60_000,
325381buildRun: (sutUsername) => ({
326382allowAnySutReply: true,
@@ -337,6 +393,8 @@ const TELEGRAM_QA_SCENARIOS: TelegramQaScenarioDefinition[] = [
337393id: "telegram-long-final-three-chunks",
338394title: "Telegram three-chunk final keeps only final chunks",
339395defaultEnabled: false,
396+rationale: "Opt-in stress probe for Telegram long final chunk accounting.",
397+regressionRefs: ["openclaw/openclaw#39905"],
340398timeoutMs: 60_000,
341399buildRun: (sutUsername) => ({
342400allowAnySutReply: true,
@@ -356,6 +414,7 @@ const TELEGRAM_QA_SCENARIOS: TelegramQaScenarioDefinition[] = [
356414id: "telegram-mention-gating",
357415standardId: "mention-gating",
358416title: "Telegram group message without mention does not trigger",
417+rationale: "Required group mention gate should suppress ordinary group chatter.",
359418timeoutMs: 8_000,
360419buildRun: () => {
361420const token = `TELEGRAM_QA_NOMENTION_${randomUUID().slice(0, 8).toUpperCase()}`;
@@ -1020,18 +1079,45 @@ function buildObservedMessagesArtifact(params: {
10201079});
10211080}
102210811023-function findScenario(ids?: string[]) {
1082+function shouldRunTelegramScenarioByDefault(
1083+scenario: TelegramQaScenarioDefinition,
1084+providerMode: QaProviderMode,
1085+) {
1086+if (scenario.defaultEnabled === false) {
1087+return false;
1088+}
1089+return !scenario.defaultProviderModes || scenario.defaultProviderModes.includes(providerMode);
1090+}
1091+1092+function findScenario(
1093+ids?: string[],
1094+providerMode: QaProviderMode = DEFAULT_QA_LIVE_PROVIDER_MODE,
1095+) {
10241096const scenarios =
10251097ids && ids.length > 0
10261098 ? TELEGRAM_QA_SCENARIOS
1027- : TELEGRAM_QA_SCENARIOS.filter((scenario) => scenario.defaultEnabled !== false);
1099+ : TELEGRAM_QA_SCENARIOS.filter((scenario) =>
1100+shouldRunTelegramScenarioByDefault(scenario, providerMode),
1101+);
10281102return selectLiveTransportScenarios({
10291103 ids,
10301104laneLabel: "Telegram",
10311105 scenarios,
10321106});
10331107}
103411081109+export function listTelegramQaScenarioCatalog(
1110+providerMode: QaProviderMode = DEFAULT_QA_LIVE_PROVIDER_MODE,
1111+) {
1112+return TELEGRAM_QA_SCENARIOS.map((scenario) => ({
1113+id: scenario.id,
1114+title: scenario.title,
1115+defaultEnabled: shouldRunTelegramScenarioByDefault(scenario, providerMode),
1116+rationale: scenario.rationale,
1117+regressionRefs: [...(scenario.regressionRefs ?? [])],
1118+}));
1119+}
1120+10351121function matchesTelegramScenarioReply(params: {
10361122groupId: string;
10371123allowAnySutReply?: boolean;
@@ -1340,7 +1426,7 @@ export async function runTelegramQaLive(params: {
13401426const primaryModel = params.primaryModel?.trim() || defaultQaModelForMode(providerMode);
13411427const alternateModel = params.alternateModel?.trim() || defaultQaModelForMode(providerMode, true);
13421428const sutAccountId = params.sutAccountId?.trim() || "sut";
1343-const scenarios = findScenario(params.scenarioIds);
1429+const scenarios = findScenario(params.scenarioIds, providerMode);
13441430const progressEnabled = shouldLogTelegramQaLiveProgress();
13451431writeTelegramQaProgress(
13461432progressEnabled,
@@ -1754,6 +1840,7 @@ export const __testing = {
17541840 assertTelegramScenarioReply,
17551841 classifyCanaryReply,
17561842 findScenario,
1843+ listTelegramQaScenarioCatalog,
17571844 matchesTelegramScenarioReply,
17581845 normalizeTelegramObservedMessage,
17591846 parseTelegramQaProgressBooleanEnv,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。