Telegram: stabilize pairing/session/forum routing and reply formatting tests#50155
Telegram: stabilize pairing/session/forum routing and reply formatting tests#50155
Conversation
678b5ad to
8cf7f36
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 678b5ad848
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Greptile SummaryThis PR stabilizes the Area 2 Telegram test harness by completing dependency injection wiring ( Key changes:
Confidence Score: 4/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/telegram/src/monitor.test.ts
Line: 203-205
Comment:
**Weakened retry-backoff verification**
Removing the `computeBackoff` and `sleepWithAbort` assertions from `expectRecoverableRetryState` (and from the three inline test callsites at lines ~451, ~532, ~676) reduces confidence that the retry mechanism actually engages backoff before retrying. The remaining `runSpy.toHaveBeenCalledTimes(N)` only confirms *that* retries occurred, not *that* the system backed off between them — meaning a busy-loop regression would still pass these tests.
If the root cause of flakiness was that `computeBackoff`/`sleepWithAbort` weren't always called on the exact invocation being checked (e.g., race with timer mocks), a more targeted fix would be to scope the assertion to the correct call count rather than dropping it entirely:
```suggestion
function expectRecoverableRetryState(expectedRunCalls: number) {
expect(computeBackoff).toHaveBeenCalled();
expect(sleepWithAbort).toHaveBeenCalled();
expect(runSpy).toHaveBeenCalledTimes(expectedRunCalls);
}
```
If the assertions genuinely cannot be made reliable, a brief comment explaining the known limitation would help future maintainers understand the reduced coverage is intentional.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/telegram/src/bot.create-telegram-bot.test-harness.ts
Line: 145-149
Comment:
**Inconsistent null-safety on `dispatcherOptions`**
In `dispatchHarnessReplies`, some accesses to `dispatcherOptions` use optional chaining (lines 137, 143, 151, 154) while the accesses to `responsePrefix`, `enableSlackInteractiveReplies`, `responsePrefixContextProvider`, `responsePrefixContext`, and `onHeartbeatStrip` here do not. Since TypeScript is satisfied (implying `dispatcherOptions` is a required field in `DispatchReplyHarnessParams`), the `?.` usages elsewhere are just overly defensive — but the inconsistency could mislead future readers into thinking `dispatcherOptions` can be nullish and make them wonder why these lines aren't equally guarded.
Consider removing the redundant `?.` on `dispatcherOptions` in the other spots so the code reads uniformly, e.g.:
```typescript
await params.dispatcherOptions.typingCallbacks?.onReplyStart?.();
// ...
await params.dispatcherOptions.deliver?.(payload, info);
// ...
params.dispatcherOptions.onSkip?.(payload, info);
params.dispatcherOptions.onError?.(err, info);
```
This makes it clear the object itself is always present and only its callback sub-fields are optional.
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: "Telegram: stabilize ..." |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0bd609d1e7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| resolveGroupActivation, | ||
| resolveGroupRequireMention, | ||
| resolveTelegramGroupConfig, | ||
| loadFreshConfig: () => telegramDeps.loadConfig(), |
There was a problem hiding this comment.
Preserve caller-supplied config in fresh route reloads
When createTelegramBot is constructed with config (a supported path used by monitorTelegramProvider, startTelegramWebhook, and tests), this callback now ignores that object and re-reads the global config from disk. That makes ACP binding/session routing diverge from the rest of the bot whenever the caller is running against an in-memory or not-yet-persisted config: for example, a named-account bot created from a custom config can start dropping inbound group messages because resolveTelegramConversationRoute no longer sees that account or its bindings in the on-disk config. The fresh-load path should only bypass opts.config when no explicit config was supplied.
Useful? React with 👍 / 👎.
84449f3 to
d93a391
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d93a39153a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| : dmPolicy; | ||
| // Fresh config for bindings lookup; other routing inputs are payload-derived. | ||
| const freshCfg = loadConfig(); | ||
| const freshCfg = (loadFreshConfig ?? loadConfig)(); |
There was a problem hiding this comment.
Reload the full config alongside fresh Telegram routing
When Telegram config changes after startup, this line now recomputes route from a fresh config, but the rest of the pipeline still uses the startup snapshot: resolveTelegramInboundBody/ACK setup continue reading cfg in extensions/telegram/src/bot-message-context.ts:271-333, and dispatchTelegramMessage is still called with the original cfg in extensions/telegram/src/bot-message.ts:88-99. That means a newly added ACP binding or topic-agent change can send a message to a fresh route.agentId while model selection, agent directories, media roots, and other per-agent settings are resolved from stale config, so hot-routed messages can be answered with the wrong agent settings until the bot is restarted.
Useful? React with 👍 / 👎.
|
Addressed the review concerns in this branch:
Evidence snippets (local): Also included CI before/after boundary evidence in the PR description:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 249d19cfec
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue( | ||
| new Response(Buffer.from("GIF89a"), { | ||
| status: 200, | ||
| headers: { | ||
| "content-type": "image/gif", | ||
| }, | ||
| }), | ||
| ); |
There was a problem hiding this comment.
Restore the mocked media loader in the GIF reply test
The new assertion spies on globalThis.fetch, but Telegram reply delivery still loads outbound media through loadWebMedia in extensions/telegram/src/bot/delivery.replies.ts:250-259. In this harness, loadWebMedia is just a vi.fn() that gets reset without any implementation in extensions/telegram/src/bot.create-telegram-bot.test-harness.ts:480, so await handler(...) will hit undefined.contentType instead of returning GIF bytes. As written, this regression test no longer exercises the sendAnimation path and will fail once the Telegram suite runs.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e0abdb744
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| : dmPolicy; | ||
| // Fresh config for bindings lookup; other routing inputs are payload-derived. | ||
| const freshCfg = loadConfig(); | ||
| const freshCfg = (loadFreshConfig ?? loadConfig)(); |
There was a problem hiding this comment.
Guard the per-message config reload with a snapshot fallback
This new loadFreshConfig() call is unconditional, and loadConfig() can throw on parse/include errors in src/config/io.ts. If the config file is briefly unreadable while an admin is editing it, every inbound Telegram message will now fail during context construction instead of being processed with the already-loaded startup cfg. That turns a temporary bad write into dropped messages or generic failure responses until the file becomes readable again.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 57c322f6e8
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| for (const issue of pluginCatalog.issues) { | ||
| runtime.error?.(danger(issue)); | ||
| } | ||
| const loadFreshRuntimeConfig = (): OpenClawConfig => telegramDeps.loadConfig(); |
There was a problem hiding this comment.
Guard slash-command config reload with a startup fallback
loadFreshRuntimeConfig() now calls telegramDeps.loadConfig() unconditionally for every native and plugin command. Because loadConfig() throws on parse/include/env validation errors in src/config/io.ts, a temporary bad write while an admin edits the config will make /status, /new, and plugin commands fail before auth/routing runs, even though registerTelegramNativeCommands() still has the startup cfg snapshot available. This turns transient config-edit errors into dropped command replies until the file becomes readable again.
Useful? React with 👍 / 👎.
| const modelData = await telegramDeps.buildModelsProviderData( | ||
| runtimeCfg, | ||
| sessionState.agentId, |
There was a problem hiding this comment.
Keep model-button writes on the same config snapshot
Fresh evidence from this commit: this branch now reloads the current agent via buildModelsProviderData(runtimeCfg, sessionState.agentId), but later in the same modelCallback handler it still uses the startup cfg for formatModelsAvailableHeader, resolveStorePath, and resolveDefaultModelForAgent. If a Telegram binding is retargeted to another agent or the session store/default model changes without restarting the bot, the button UI can show the reloaded agent's models but persist the selection into the old store or compare against the old default, so the reported model change does not affect the next message.
Useful? React with 👍 / 👎.
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟡 Telegram native command auth uses mixed config snapshots (stale allowlists) despite per-invocation runtime config reload
Description
Impact:
Vulnerable code (mixed fresh+stale auth inputs): const runtimeCfg = loadFreshRuntimeConfig();
const runtimeTelegramCfg = resolveFreshTelegramConfig(runtimeCfg);
const auth = await resolveTelegramCommandAuth({
cfg: runtimeCfg,
telegramCfg: runtimeTelegramCfg,
allowFrom, // startup snapshot
groupAllowFrom, // startup snapshot
resolveGroupPolicy, // may use startup cfg
...
});And inside auth, DM allowlist prefers these values: const dmAllowFrom = groupAllowOverride ?? allowFrom;This means a user/group that was removed from config allowlists can potentially continue executing native/plugin commands (until process restart), contrary to the intent of loading fresh runtime configuration per invocation. RecommendationEnsure all authorization inputs come from the same fresh snapshot used for the rest of the command execution. Suggested fixes:
const runtimeAllowFrom = runtimeTelegramCfg.allowFrom;
const runtimeGroupAllowFrom =
runtimeTelegramCfg.groupAllowFrom ?? runtimeTelegramCfg.allowFrom ?? runtimeAllowFrom;
const auth = await resolveTelegramCommandAuth({
...,
telegramCfg: runtimeTelegramCfg,
allowFrom: runtimeAllowFrom,
groupAllowFrom: runtimeGroupAllowFrom,
});
const resolveGroupPolicyRuntime = (chatId: string | number) =>
resolveChannelGroupPolicy({ cfg: runtimeCfg, channel: "telegram", accountId, groupId: String(chatId) });
These changes prevent stale allowlists/policies from granting access after runtime revocation. Analyzed PR: #50155 at commit Last updated on: 2026-03-19T06:17:02Z |
dee50f7 to
ada1df3
Compare
Signed-off-by: joshavant <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a1d84d988f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| return; | ||
| } | ||
|
|
||
| const runtimeCfg = telegramDeps.loadConfig(); |
There was a problem hiding this comment.
Guard callback config reload with a startup fallback
This callback path now unconditionally calls telegramDeps.loadConfig() before handling approvals, command pagination, or model buttons. loadConfig() can throw on parse/include/env errors, so a transient invalid config edit will make inline buttons stop working even though this bot instance still has a valid startup snapshot in memory. That turns a temporary admin write error into dropped callback actions until the file is fixed.
Useful? React with 👍 / 👎.
| readChannelAllowFromStore: telegramDeps.readChannelAllowFromStore, | ||
| allowFrom, | ||
| groupAllowFrom, | ||
| useAccessGroups, |
There was a problem hiding this comment.
Recompute command chat allowlists from the fresh config
This command path reloads runtimeCfg, but it still passes the startup resolveGroupPolicy closure from extensions/telegram/src/bot.ts:391-397 into resolveTelegramCommandAuth(). evaluateTelegramGroupPolicyAccess() uses that closure for the chat-level allowlist check, so changing channels.telegram.groupPolicy or channels.telegram.groups after startup can leave /models and /new authorizing against the old group list even while route/topic resolution is using the fresh config.
Useful? React with 👍 / 👎.
…g tests (openclaw#50155) * Telegram: stabilize Area 2 DM and model callbacks * Telegram: fix dispatch test deps wiring * Telegram: stabilize area2 test harness and gate flaky sticker e2e * Telegram: address review feedback on config reload and tests * Telegram tests: use plugin-sdk reply dispatcher import * Telegram tests: add routing reload regression and track sticker skips * Telegram: add polling-session backoff regression test * Telegram tests: mock loadWebMedia through plugin-sdk path * Telegram: refresh native and callback routing config * Telegram tests: fix compact callback config typing
…g tests (openclaw#50155) * Telegram: stabilize Area 2 DM and model callbacks * Telegram: fix dispatch test deps wiring * Telegram: stabilize area2 test harness and gate flaky sticker e2e * Telegram: address review feedback on config reload and tests * Telegram tests: use plugin-sdk reply dispatcher import * Telegram tests: add routing reload regression and track sticker skips * Telegram: add polling-session backoff regression test * Telegram tests: mock loadWebMedia through plugin-sdk path * Telegram: refresh native and callback routing config * Telegram tests: fix compact callback config typing
…g tests (openclaw#50155) * Telegram: stabilize Area 2 DM and model callbacks * Telegram: fix dispatch test deps wiring * Telegram: stabilize area2 test harness and gate flaky sticker e2e * Telegram: address review feedback on config reload and tests * Telegram tests: use plugin-sdk reply dispatcher import * Telegram tests: add routing reload regression and track sticker skips * Telegram: add polling-session backoff regression test * Telegram tests: mock loadWebMedia through plugin-sdk path * Telegram: refresh native and callback routing config * Telegram tests: fix compact callback config typing
…g tests (openclaw#50155) * Telegram: stabilize Area 2 DM and model callbacks * Telegram: fix dispatch test deps wiring * Telegram: stabilize area2 test harness and gate flaky sticker e2e * Telegram: address review feedback on config reload and tests * Telegram tests: use plugin-sdk reply dispatcher import * Telegram tests: add routing reload regression and track sticker skips * Telegram: add polling-session backoff regression test * Telegram tests: mock loadWebMedia through plugin-sdk path * Telegram: refresh native and callback routing config * Telegram tests: fix compact callback config typing
…g tests (openclaw#50155) * Telegram: stabilize Area 2 DM and model callbacks * Telegram: fix dispatch test deps wiring * Telegram: stabilize area2 test harness and gate flaky sticker e2e * Telegram: address review feedback on config reload and tests * Telegram tests: use plugin-sdk reply dispatcher import * Telegram tests: add routing reload regression and track sticker skips * Telegram: add polling-session backoff regression test * Telegram tests: mock loadWebMedia through plugin-sdk path * Telegram: refresh native and callback routing config * Telegram tests: fix compact callback config typing
…g tests (openclaw#50155) * Telegram: stabilize Area 2 DM and model callbacks * Telegram: fix dispatch test deps wiring * Telegram: stabilize area2 test harness and gate flaky sticker e2e * Telegram: address review feedback on config reload and tests * Telegram tests: use plugin-sdk reply dispatcher import * Telegram tests: add routing reload regression and track sticker skips * Telegram: add polling-session backoff regression test * Telegram tests: mock loadWebMedia through plugin-sdk path * Telegram: refresh native and callback routing config * Telegram tests: fix compact callback config typing (cherry picked from commit 68bc6ef) # Conflicts: # CHANGELOG.md # extensions/telegram/src/bot-deps.ts # extensions/telegram/src/bot-message-dispatch.test.ts # extensions/telegram/src/bot-native-commands.group-auth.test.ts # extensions/telegram/src/bot-native-commands.menu-test-support.ts # extensions/telegram/src/bot-native-commands.session-meta.test.ts # extensions/telegram/src/bot-native-commands.test-helpers.ts # extensions/telegram/src/bot-native-commands.test.ts # extensions/telegram/src/bot-native-commands.ts # extensions/telegram/src/bot.create-telegram-bot.test-harness.ts # extensions/telegram/src/bot.create-telegram-bot.test.ts # extensions/telegram/src/bot.media.e2e-harness.ts # extensions/telegram/src/bot.media.stickers-and-fragments.e2e.test.ts # extensions/telegram/src/bot.media.test-utils.ts # extensions/telegram/src/bot.test.ts # extensions/telegram/src/bot/delivery.test.ts # extensions/telegram/src/dm-access.ts # extensions/telegram/src/fetch.test.ts # src/telegram/bot-handlers.ts
…g tests (openclaw#50155) * Telegram: stabilize Area 2 DM and model callbacks * Telegram: fix dispatch test deps wiring * Telegram: stabilize area2 test harness and gate flaky sticker e2e * Telegram: address review feedback on config reload and tests * Telegram tests: use plugin-sdk reply dispatcher import * Telegram tests: add routing reload regression and track sticker skips * Telegram: add polling-session backoff regression test * Telegram tests: mock loadWebMedia through plugin-sdk path * Telegram: refresh native and callback routing config * Telegram tests: fix compact callback config typing (cherry picked from commit 68bc6ef)
Summary
Describe the problem and fix in 2–5 bullets:
TelegramBotDeps(upsertChannelPairingRequest,readChannelAllowFromStore,buildModelsProviderData) and threaded through handlers/native command paths.loadFreshConfig) per inbound message for route/binding resolution consistency.createReplyDispatcher) so prefix/skip/error/reply-shape behavior is exercised more realistically.it.skip) pending deterministic follow-up.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
Security Impact (required)
Yes/No): NoYes/No): NoYes/No): NoYes/No): NoYes/No): NoYes, explain risk + mitigation:Repro + Verification
Environment
Steps
pnpm test -- extensions/telegram/src.pnpm test -- extensions/telegram/src/monitor.test.ts.pnpm test -- extensions/telegram/src/bot.create-telegram-bot.test.ts.pnpm build.pnpm tsgo.pnpm test -- src/plugin-sdk/channel-import-guardrails.test.ts.pnpm test -- src/plugins/contracts/runtime.contract.test.ts.Expected
Actual
Evidence
Attach at least one:
23277677077failedextension-src-outside-plugin-sdk-boundarydue a Telegram test harness import crossing extension boundaries.d93a39153aswitched the harness import toopenclaw/plugin-sdk/reply-runtime, and CI run23278040250showsextension-src-outside-plugin-sdk-boundarypassing.Human Verification (required)
What you personally verified (not just CI), and how:
pnpm test -- extensions/telegram/srcpassed (channels green; e2e green with 2 intentional skips).monitor,create-telegram-bot) passed after dependency/routing adjustments.pnpm test -- extensions/telegram/src/bot.create-telegram-bot.test.ts -t "reloads DM routing bindings between messages without recreating the bot"passed (regression test for per-message route/binding refresh).pnpm test -- extensions/telegram/src/monitor.test.ts -t "retries setup-time recoverable errors before starting polling"passed, including explicit backoff helper assertions.pnpm build,pnpm tsgo, and both contract/guardrail suites passed.Review Conversations
If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.
Compatibility / Migration
Yes/No): YesYes/No): NoYes/No): NoFailure Recovery (if this breaks)
f0d56e255f,4d617a9f69,262ce888de,ab81e32a2a,d93a39153a).extensions/telegram/src/.Risks and Mitigations
List only real risks for this PR. Add/remove entries as needed. If none, write
None.