Skip to content

[2/2] feat(eve): observe channel deliveries as session history - #3023

Open
Lukavyi wants to merge 3 commits into
vercel:mainfrom
Lukavyi:telegram-observe-group-messages
Open

[2/2] feat(eve): observe channel deliveries as session history#3023
Lukavyi wants to merge 3 commits into
vercel:mainfrom
Lukavyi:telegram-observe-group-messages

Conversation

@Lukavyi

@Lukavyi Lukavyi commented Sep 4, 2026

Copy link
Copy Markdown

Stacked on #3020. Review the last commit only (feat(eve): observe channel deliveries as session history); the first two commits are #3020 and will disappear on rebase once it lands.

Summary

A group bot only ever sees the messages that address it. Even with one session per chat (#3020), it cannot follow the conversation that happens between mentions, so "@bot, what do you think about what U1 just said?" has nothing to work with. Slack has threadContext for this; Telegram had no equivalent, and the dispatch gate drops unaddressed messages before they reach a session (#874, second half).

This adds observe: true to channel deliveries, a channel-agnostic primitive:

  • An observed delivery appends its message and context to the session as history without running a turn. eve buffers it on the session, parked or mid-turn, and folds the buffered messages in arrival order into the next delivery that does run a turn, so the model sees them as part of that turn's input (this rides on the coalescing fixed in coalesceDeliverPayloads silently drops earlier messages in a batched delivery (last-key-wins for message/context) #897).
  • It never steers an active turn (turnPolicy is not applied) and never starts a session: with no owner for the address it throws RuntimeNoActiveSessionError, which the Telegram channel logs at debug and drops.
  • The observed backlog is capped at 256 payloads, oldest dropped first. Task deliveries settle on their own and leave observed history waiting for a channel turn, so a child result never carries chat history it did not ask for.
  • Telegram exposes it as observe on the onMessage result. isTelegramBotMentioned is now exported so a custom onMessage can gate on it; the docs carry the four-line example. Telegram only delivers unaddressed group messages with privacy mode off or the bot as admin, noted in the docs rather than enforced.

SendPayload gains a field, so the channel extension contract moves to epoch 18 (structurally backward compatible, epoch 17 retained).

We have run this shape as a patchedDependencies patch in production on two group bots since July; the acceptance scenario from #874 (untagged question, later a mention, answer with that context) passes there.

Related to #874, #897, #223 (Slack: the same observe gate would let a Slack onMessage follow a thread without mentions).

Validation

pnpm --filter eve exec vitest run --config vitest.unit.config.ts src/execution src/channel src/public/channels/telegram   # 124 files, 1109 passed
pnpm --filter eve exec tsc -p tsconfig.json --noEmit
pnpm run guard:invariants
pnpm run docs:check
pnpm update:extension-contracts --update channel

New coverage: coalesceDeliverPayloads keeps observe only when every payload was observed; bufferObservedDelivery buffers observe-only deliveries, leaves addressed ones to the caller, and drops the oldest past the cap; a parked session stays parked on observed deliveries and coalesces them ahead of the next addressed one; buffered observed deliveries alone do not wake it; a task delivery runs without them; the turn receiver buffers observed deliveries without steering or forwarding; the Telegram channel delivers an observe result as history with the <telegram_context> block and a mention without it.

Checklist

🤖 Generated with Claude Code

https://claude.ai/code/session_015P6RBuagWTMzBmJLCiYKW9

@vercel

vercel Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@Lukavyi is attempting to deploy a commit to the Vercel Team on Vercel.

A member of the Team first needs to authorize it.

@Lukavyi
Lukavyi force-pushed the telegram-observe-group-messages branch from 5a78fd5 to 330c225 Compare September 4, 2026 08:09
Lukavyi and others added 3 commits September 4, 2026 10:10
Telegram group and supergroup inbound messages keyed the session to the
bot message being replied to, or to the mention itself. Every fresh
mention therefore started a new session with one message of context,
while private chats already keep one continuous session per chat.

Groups now key to the chat plus forum topic, the same way private chats
do. Mentions, replies to bot messages, callback queries, and outbound
sends all resume that session. The message_thread_id that non-forum
supergroups stamp on replies is a reply chain, not a topic, and does not
split the session. Proactive targets can still pin a thread through
conversationId.

Related to vercel#874

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015P6RBuagWTMzBmJLCiYKW9
Signed-off-by: Taras Lukavyi <lukavyi@me.com>
…orm prompt is pending

A reply to a bot message went through respond() with only the synthetic
telegram_reply:<id> response. When that id was not a registered freeform
prompt, the adapter resolved nothing, returned undefined, and the session
re-parked without a turn, so the reply was dropped. The initial channel
sent the text as message on the same delivery; vercel#1597 replaced that with
respond() and lost it. With chat-wide group sessions a reply to the bot
is the natural follow-up, so carry the text as message again: the adapter
still resolves a pending freeform answer first and otherwise runs the
text as a normal turn. Pass state and title so a reply can start a session
with a usable chat id when none owns the address.

Also correct the TelegramReceiveTarget docstring: conversationId pins a
caller-selected id that inbound messages never route to, and
initialMessage no longer starts a separate thread in groups.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015P6RBuagWTMzBmJLCiYKW9
Signed-off-by: Taras Lukavyi <lukavyi@me.com>
A group bot only ever sees the messages that address it, so even with one
session per chat it cannot follow the conversation that happens between
mentions. Slack has threadContext for this; Telegram had nothing.

Add observe: true to channel deliveries. An observed delivery appends its
message and context to the session as history without running a turn:
eve buffers it on the parked or active session, never steers, never
starts a session, and folds the buffered messages in arrival order into
the next delivery that does run a turn. The observed backlog is capped at
256 payloads, oldest dropped first. Task deliveries settle on their own
and leave observed history waiting for a channel turn.

The Telegram channel exposes it as observe on the onMessage result and
exports isTelegramBotMentioned so hosts can gate on it.

Related to vercel#874

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015P6RBuagWTMzBmJLCiYKW9
Signed-off-by: Taras Lukavyi <lukavyi@me.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant