Commit Graph

872 Commits

Author SHA1 Message Date
poisk
32e8bca02c fix(telegram): honor removeAckAfterReply for status reactions (#68067)
Thanks @poiskgit.
2026-04-21 01:47:20 +01:00
Amine Harch el korane
8c05043eca fix(telegram): tune polling stall threshold
Raise the Telegram polling watchdog default from 90s to 120s and add bounded channels.telegram.pollingStallThresholdMs overrides, including per-account config.\n\nThanks @Vitalcheffe.
2026-04-21 01:03:04 +01:00
Peter Steinberger
77a6187a70 fix(telegram): bound offset confirmation timeout (#50368) (thanks @boticlaw) 2026-04-21 00:04:15 +01:00
Daniel
45ffb6cc25 fix(telegram): add client-side timeout to #confirmPersistedOffset getUpdates 2026-04-21 00:04:15 +01:00
Peter Steinberger
8b7418b127 refactor: share channel doctor alias normalization 2026-04-20 23:34:19 +01:00
Peter Steinberger
3eb48ec3e7 refactor(telegram): split polling liveness tracking 2026-04-20 23:16:55 +01:00
Peter Steinberger
431e33b567 test: share channel directory id assertions 2026-04-20 23:15:58 +01:00
Peter Steinberger
72571f0d38 test: decouple outbound target tests from bundled plugins 2026-04-20 23:14:50 +01:00
Peter Steinberger
60fea81cf1 fix(telegram): harden polling transport liveness (#69476)
* fix(telegram): release undici dispatchers via TelegramTransport.close()

TelegramTransport now exposes an explicit close() that destroys every
owned undici dispatcher (default Agent plus lazily-created IPv4 and
IP-pinned fallback Agents) and the TCP sockets they hold. Dispatcher
constructors are also given bounded keep-alive defaults
(keepAliveTimeout, keepAliveMaxTimeout, connections, pipelining) as a
defence-in-depth layer so the pool cannot grow unbounded even if a
caller forgets to call close().

Without this, every transport that went through a fallback retry left
its fallback Agents anchored forever in a closure; long-running polling
sessions accumulated hundreds of ESTABLISHED keep-alive sockets to
api.telegram.org, saturating the per-IP quota on upstream forward
proxies and making the currently-active outbound node time out while
every other node still tested healthy.

Mock dispatchers in fetch.test.ts gain destroy() spies so the close()
chain is assertable. Call sites that built caller-owned transports from
globalThis.fetch (delivery.resolve-media, test helpers) return an async
no-op close(), matching the new required surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(telegram): dispose polling transport on shutdown and dirty rebuild

Every recoverable network error and stall-watchdog trip sets
TelegramPollingTransportState.#transportDirty so the next polling
cycle rebuilds the transport inside acquireForNextCycle(). Previously
the rebuild simply overwrote the field, leaving the old transport's
keep-alive sockets anchored in the now-unreferenced dispatcher — the
polling loop has no natural GC point for these resources, and Node's
object GC never touches OS-level sockets.

acquireForNextCycle() now closes the previous transport (fire-and-
forget so the polling cycle is not blocked by a slow destroy) before
swapping in the rebuilt one. dispose() is a new method that the owning
TelegramPollingSession calls from the finally block of runUntilAbort(),
so a single transport is always tied to a single polling session
lifetime. After dispose(), acquireForNextCycle() returns undefined to
prevent zombie rebuilds.

Under high sustained polling traffic over long-lived sessions, this is
what stops the per-gateway connection count to api.telegram.org from
growing indefinitely and saturating upstream proxy quotas.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(changelog): note Telegram undici dispatcher lifecycle fix

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(telegram): disable HTTP/2 for all Telegram polling dispatchers

Undici 8 enables HTTP/2 ALPN by default, but Telegram's long-polling
connections stall on Windows due to IPv6 + H2 multiplexing issues. The
core fetch-guard already sets allowH2:false for guarded paths, but the
Telegram extension creates its own Agent/ProxyAgent/EnvHttpProxyAgent
instances directly from undici without this flag.

Apply allowH2:false to all dispatcher constructors in the Telegram
transport layer, matching the approach used in src/infra/net/undici-runtime.ts.

Fixes #66885

* fix: avoid false telegram polling stall restarts

* fix(telegram): publish polling health liveness

---------

Co-authored-by: Ethan Chen <ethanbit@qq.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Magicray1217 <magicray1217@users.noreply.github.com>
Co-authored-by: aoao <aoao@openclaw>
2026-04-20 23:03:57 +01:00
Peter Steinberger
8f6cf2afdd test(telegram): move ingest schema coverage 2026-04-20 21:59:41 +01:00
Peter Steinberger
8134fe737c test(extensions): move legacy schema assertions 2026-04-20 21:58:34 +01:00
Peter Steinberger
3a7a1f156d test(extensions): move remaining channel schema tests 2026-04-20 21:54:49 +01:00
Peter Steinberger
49b2ec1e2e test(extensions): move config regression coverage 2026-04-20 21:51:34 +01:00
Peter Steinberger
2f4cf2d67d test(extensions): move channel config schema coverage 2026-04-20 21:47:13 +01:00
Peter Steinberger
958ca2ebec test(extensions): move registry channel contracts 2026-04-20 20:55:39 +01:00
Peter Steinberger
aa0957c4dd test: share messaging plugin fixtures 2026-04-20 20:52:31 +01:00
Peter Steinberger
5f94c2592d test: stabilize directory id sorting 2026-04-20 20:28:48 +01:00
Peter Steinberger
911cfe2adc refactor: use structured clone for local copies 2026-04-20 20:28:47 +01:00
Peter Steinberger
4edc64037c test(extensions): stabilize ci test assertions 2026-04-20 18:09:01 +01:00
Peter Steinberger
ca2d89bc4d test(extensions): move channel contracts out of core 2026-04-20 17:59:51 +01:00
Peter Steinberger
78cf0e95ad test: share telegram thread binding fixture 2026-04-20 17:51:49 +01:00
Peter Steinberger
873ef6cf45 test: share telegram inbound body fixture 2026-04-20 17:50:36 +01:00
Peter Steinberger
e7befec3ff test: share telegram command menu fixture 2026-04-20 17:49:04 +01:00
Peter Steinberger
a73bbe4bdd test(extensions): move channel security coverage 2026-04-20 17:38:20 +01:00
Peter Steinberger
c0490aa418 test: share telegram dm access fixture 2026-04-20 17:15:07 +01:00
Peter Steinberger
68fbe9fab1 test: share telegram api base mock 2026-04-20 17:06:57 +01:00
Peter Steinberger
1148f245c8 build(deps): declare extension runtime dependencies 2026-04-20 16:07:14 +01:00
Peter Steinberger
fc56cd135f refactor: reuse telegram command keyboard helper 2026-04-20 15:38:10 +01:00
Peter Steinberger
8116e638f3 chore: release 2026.4.20 2026-04-20 13:16:40 +01:00
Ayaan Zaidi
df3aa90a20 fix(telegram): require numeric allowFrom ids in setup 2026-04-20 10:03:25 +05:30
Peter Steinberger
ac8f0c9c0d chore: prepare 2026.4.19-beta.1 release 2026-04-19 02:09:43 +01:00
Peter Steinberger
155162a8cd chore(lint): enable additional cleanup rules 2026-04-18 20:37:13 +01:00
Peter Steinberger
4fa961d4f1 refactor(lint): enable map spread rule 2026-04-18 20:37:12 +01:00
Peter Steinberger
73e497f9be refactor: cache hot channel imports 2026-04-18 20:19:53 +01:00
Lucenx9
90b8f3fba2 fix(telegram): tighten permanent edit error match 2026-04-18 19:52:31 +01:00
Lucenx9
d8b18f1d96 fix(telegram): avoid wedging callback updates on permanent edit errors 2026-04-18 19:52:31 +01:00
Peter Steinberger
1f1ff0567a refactor(lint): reduce map spread patterns 2026-04-18 19:27:43 +01:00
Peter Steinberger
3f3bc97cd3 chore(lint): enable warning comments rule 2026-04-18 18:55:18 +01:00
Peter Steinberger
e7d33b4870 refactor: finish dynamic import cleanup 2026-04-18 17:54:38 +01:00
Peter Steinberger
3f2e73b723 chore(release): bump version to 2026.4.18 2026-04-18 15:46:33 +01:00
Ayaan Zaidi
dc3b10285d fix(telegram): require authorized abort supersede 2026-04-18 10:14:08 +05:30
Rubén Cuevas
996eb9a024 fix: fence Telegram stale reply delivery after abort (#68100) (thanks @rubencu)
* fix(telegram): fence stale reply delivery after abort

* refactor(telegram): narrow abort fence scope

* fix(telegram): ignore stale reply finalization after abort

* fix(telegram): close abort supersession races

* fix(telegram): release abort fences on setup errors

* fix(telegram): discard superseded draft cleanup

* refactor(telegram): distill abort fence cleanup

* fix: fence Telegram stale reply delivery after abort (#68100) (thanks @rubencu)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-04-18 10:02:38 +05:30
Peter Steinberger
a22b789547 test: stabilize telegram status lane test 2026-04-18 02:13:11 +01:00
Peter Steinberger
569247cff8 test: speed channel contract hotspots 2026-04-18 01:36:15 +01:00
Peter Steinberger
ed65e8017d test: slim channel directory contracts 2026-04-18 01:36:15 +01:00
Gustavo Madeira Santana
5ae059db16 test: speed legacy state migration discovery
Keep bundled legacy migration discovery on narrow setup-entry surfaces so
state-migration tests and doctor cold paths avoid unrelated channel runtime
loads. Add targeted setup feature metadata, narrow Telegram/WhatsApp legacy
contracts, and a path-only pairing SDK helper.
2026-04-17 16:41:43 -04:00
Gustavo Madeira Santana
8b76bcba90 test: avoid real Telegram config writes in retry tests 2026-04-17 15:50:41 -04:00
Gustavo Madeira Santana
54e4e16844 Tests: narrow session binding contract setup 2026-04-17 03:36:13 -04:00
Ayaan Zaidi
5aad79571e fix(telegram): clear compaction replay after visible boundaries 2026-04-17 11:18:22 +05:30
Chinar Amrutkar
8205de84a9 fix: clear stale telegram ACP bindings on startup (#67822) (thanks @chinar-amrutkar)
* fix(telegram): clean up thread bindings to stale/failed ACP sessions on startup

When loading persisted thread bindings on manager creation, validate each
ACP session against the session store. Remove bindings where:
- Session entry doesn't exist (deleted externally)
- Session status is failed/killed/timeout
- ACP runtime state is 'error'

This addresses issue #60102 where Telegram DMs remained routed to stale
ACP sessions even after restart, because the binding file persisted
across restarts without validating the target session was still valid.

* fix(telegram): guard against null session entry and transient store read failures

Address review comments on PR #67822:

1. Skip bindings when readAcpSessionEntry returns null or when
   session store is temporarily unreadable (storeReadFailed: true).
   Without this, a transient I/O error would mark all ACP bindings
   as stale and delete them on every startup.

2. Only set needsPersist when bindings were actually removed.
   Previously, stale session keys from OTHER accounts could set
   needsPersist=true even when zero bindings were removed for
   the current account — causing spurious disk writes.

Also clean up redundant optional chaining on entry.status now
that we guard against undefined/nullable sessionEntry.

* perf(telegram): dedupe ACP session reads in startup cleanup

Cache readAcpSessionEntry calls by targetSessionKey. Multiple bindings
to the same ACP session now result in a single session store read instead
of one read per binding.

Addresses chatgpt-codex-connector P2 review comment on PR #67822.

* fix(telegram): skip non-ACP session keys in stale binding cleanup

Address chatgpt-codex-connector P1 review comment on PR #67822:

Plugin-bound Telegram conversations use "plugin-binding:*" keys
with targetKind === "acp", but these are NOT ACP runtime sessions.
readAcpSessionEntry() returns no entry for them, so !sessionEntry.entry
would classify them as stale and delete them on every startup.

Now checks isAcpSessionKey(binding.targetSessionKey) to skip plugin-bound
sessions from the stale session cleanup scan.

Also clarifies the comment to explain why we use targetKind === "acp"
// together with isAcpSessionKey() check.

* fix(telegram): import isAcpSessionKey from sessions/session-key-utils

isAcpSessionKey is not re-exported from openclaw/plugin-sdk/routing.
Fix import to use the correct subpath: openclaw/sessions/session-key-utils.

Addresses chatgpt-codex-connector P1 review comment on PR #67822.

* fix(telegram): import from relative path, remove unused variable

- Import isAcpSessionKey from relative path ../../sessions/session-key-utils.js
  (not openclaw/sessions/session-key-utils which doesn't exist)
- Remove unused 'bindings' variable in for-of loop

Addresses CI failures on PR #67822.

* fix(telegram): export isAcpSessionKey from plugin-sdk/routing

isAcpSessionKey lives in src/routing/session-key.ts, which is already
exported via openclaw/plugin-sdk/routing. Re-export it from routing.ts
so extensions can import via the public plugin-sdk path.

Fixes chatgpt-codex-connector P1: relative path ../../sessions/session-key-utils.js
doesn't exist in the build output, making the Telegram extension fail
module resolution before startup cleanup can run.

* test(telegram): cover startup ACP binding cleanup

* fix: clear stale telegram ACP bindings on startup (#67822) (thanks @chinar-amrutkar)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-04-17 11:03:36 +05:30