fix: recover telegram polling after runner stop (#26447) (thanks @theclawdaddy)

This commit is contained in:
Peter Steinberger
2026-02-26 02:24:45 +01:00
parent 61b9331c2d
commit d0694788c9
4 changed files with 29 additions and 5 deletions

View File

@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Agents/Subagents delivery: refactor subagent completion announce dispatch into an explicit queue/direct/fallback state machine, recover outbound channel-plugin resolution in cold/stale plugin-registry states across announce/message/gateway send paths, finalize cleanup bookkeeping when announce flow rejects, and treat Telegram sends without `message_id` as delivery failures (instead of false-success `"unknown"` IDs). (#26867, #25961, #26803, #25069, #26741) Thanks @SmithLabsLLC and @docaohieu2808.
- Telegram/Polling: keep the polling monitor alive when the grammY runner stops unexpectedly under an active gateway abort lifecycle, auto-restarting with backoff instead of exiting silently. (#26447) Thanks @theclawdaddy.
- Slack/Session threads: prevent oversized parent-session inheritance from silently bricking new thread sessions, surface embedded context-overflow empty-result failures to users, and add configurable `session.parentForkMaxTokens` (default `100000`, `0` disables). (#26912) Thanks @markshields-tl.
- Security/Signal: enforce DM/group authorization before reaction-only notification enqueue so unauthorized senders can no longer inject Signal reaction system events under `dmPolicy`/`groupPolicy`; reaction notifications now require channel access checks first. This ships in the next npm release (`2026.2.25`). Thanks @tdjackey for reporting.
- Security/Discord + Slack reactions: enforce DM policy/allowlist authorization before reaction-event system enqueue in direct messages; Discord reaction handling now also honors DM/group-DM enablement and guild `groupPolicy` channel gating to keep reaction ingress aligned with normal message preflight. This ships in the next npm release (`2026.2.25`). Thanks @tdjackey for reporting.

View File

@@ -45,6 +45,7 @@ vi.mock("../../commands/agent.js", () => ({
vi.mock("../../config/config.js", () => ({
loadConfig: () => mocks.loadConfigReturn,
STATE_DIR: "/tmp/openclaw-test-state",
}));
vi.mock("../../agents/agent-scope.js", () => ({

View File

@@ -229,6 +229,30 @@ describe("monitorTelegramProvider (grammY)", () => {
expect(runSpy).toHaveBeenCalledTimes(2);
});
it("restarts polling when runner stops unexpectedly under active abort signal", async () => {
const abort = new AbortController();
runSpy
.mockImplementationOnce(() => ({
task: () => Promise.resolve(),
stop: vi.fn(),
isRunning: (): boolean => false,
}))
.mockImplementationOnce(() => ({
task: () => {
abort.abort();
return Promise.resolve();
},
stop: vi.fn(),
isRunning: (): boolean => false,
}));
await monitorTelegramProvider({ token: "tok", abortSignal: abort.signal });
expect(computeBackoff).toHaveBeenCalled();
expect(sleepWithAbort).toHaveBeenCalled();
expect(runSpy).toHaveBeenCalledTimes(2);
});
it("deletes webhook before starting polling", async () => {
const order: string[] = [];
api.deleteWebhook.mockReset();

View File

@@ -45,10 +45,8 @@ export function createTelegramRunnerOptions(cfg: OpenClawConfig): RunOptions<unk
},
// Suppress grammY getUpdates stack traces; we log concise errors ourselves.
silent: true,
// Retry transient failures before surfacing errors. Use a generous
// window so the runner survives prolonged outages (e.g. scheduled
// internet downtime) without the outer loop needing to restart it.
maxRetryTime: 60 * 60 * 1000,
// Retry transient failures for a limited window before surfacing errors.
maxRetryTime: 5 * 60 * 1000,
retryInterval: "exponential",
},
};
@@ -279,7 +277,7 @@ export async function monitorTelegramProvider(opts: MonitorTelegramOpts = {}) {
try {
// runner.task() returns a promise that resolves when the runner stops
await runner.task();
if (opts.abortSignal?.aborted) {
if ((!opts.abortSignal || opts.abortSignal.aborted) && !forceRestarted) {
return;
}
// The runner stopped on its own. This can happen when grammY's