965 lines
24 KiB
Markdown
965 lines
24 KiB
Markdown
# Messenger Connectors - Uncertainty Pass 35
|
|
|
|
Date: 2026-04-29
|
|
Scope: conversation history, Telegram topic projection, teammate-visible messages, backfill policy, canonical local store, and anti-duplication rules
|
|
|
|
## Executive Delta
|
|
|
|
The next weakest area is:
|
|
|
|
```text
|
|
local app messages
|
|
-> canonical messenger conversation history
|
|
-> Telegram topic projection
|
|
-> provider message links
|
|
-> reply-to routing
|
|
```
|
|
|
|
This looks like a UX problem, but it is actually a data model problem.
|
|
|
|
If we simply mirror the existing app feed into Telegram, we risk:
|
|
|
|
```text
|
|
1. Sending internal lead thoughts or slash command output to Telegram.
|
|
2. Mixing unrelated teammate replies from inboxes/user.json into the wrong topic.
|
|
3. Duplicating the same answer because the local UI feed dedupes differently from provider delivery.
|
|
4. Losing reply-to routing because local messages have no provider message link.
|
|
5. Creating a Telegram topic that looks like history, but is missing context from before connection.
|
|
6. Backfilling old history and accidentally exposing private/internal messages.
|
|
```
|
|
|
|
The safest rule:
|
|
|
|
```text
|
|
Telegram topic is a projection, not the source of truth.
|
|
```
|
|
|
|
Canonical history must be a new provider-neutral store:
|
|
|
|
```text
|
|
MessengerConversationStore
|
|
accepted inbound provider messages
|
|
external-safe local replies
|
|
provider delivery links
|
|
route/team/member references
|
|
projection state
|
|
```
|
|
|
|
The existing `TeamMessageFeedService` is useful as an input, but it is not safe to use as the Telegram projection source directly.
|
|
|
|
## Source Facts Rechecked
|
|
|
|
Telegram official facts checked on 2026-04-29:
|
|
|
|
- Bot API exposes update delivery through `getUpdates` or webhooks. Updates are stored on Telegram servers until the bot receives them, but not longer than 24 hours.
|
|
- `Update.update_id` helps ignore repeated updates or restore order if webhook updates arrive out of order.
|
|
- `Message.message_id` is unique inside a chat. In some scheduled-message cases it can be `0` and unusable until actually sent.
|
|
- `Message.message_thread_id` identifies a message thread or forum topic for supergroups and private chats.
|
|
- `createForumTopic` can create a topic in a forum supergroup or a private chat with a user. It returns a `ForumTopic`.
|
|
- `editForumTopic` can change topic name/icon in a forum supergroup or private chat with a user.
|
|
- `copyMessages` supports `message_thread_id`, copies 1-100 known messages, and returns `MessageId[]`.
|
|
- `sendMessage` and media methods return the sent `Message` on success. This returned provider message id is required for future reply-to routing.
|
|
- `sendChatAction` supports `message_thread_id` and lasts 5 seconds or less. Telegram recommends it only when a response will take noticeable time.
|
|
- `sendMessageDraft` can stream a partial message to a user while it is being generated, with optional `message_thread_id`.
|
|
- `editMessageText` can edit messages, but it is primarily for changing existing message history and has 48-hour limits for certain business messages not sent by the bot.
|
|
- `deleteMessage` has important limits, including a 48-hour deletion window for normal messages and service-message exceptions.
|
|
- Telegram FAQ says bots can see messages sent to them, and group privacy mode changes what group messages they can see. Treat bots as third-party participants.
|
|
|
|
Sources:
|
|
|
|
- https://core.telegram.org/bots/api#getting-updates
|
|
- https://core.telegram.org/bots/api#update
|
|
- https://core.telegram.org/bots/api#message
|
|
- https://core.telegram.org/bots/api#createforumtopic
|
|
- https://core.telegram.org/bots/api#editforumtopic
|
|
- https://core.telegram.org/bots/api#copymessages
|
|
- https://core.telegram.org/bots/api#sendmessage
|
|
- https://core.telegram.org/bots/api#sendchataction
|
|
- https://core.telegram.org/bots/api#sendmessagedraft
|
|
- https://core.telegram.org/bots/api#editmessagetext
|
|
- https://core.telegram.org/bots/api#deletemessage
|
|
- https://telegram.org/faq
|
|
|
|
Inference from the Bot API docs:
|
|
|
|
```text
|
|
The Bot API is update-driven and method-driven.
|
|
It does not document a general "read arbitrary private chat history" method for bots.
|
|
Therefore Agent Teams must persist the history it needs at acceptance/projection time.
|
|
```
|
|
|
|
Local code facts checked:
|
|
|
|
- `TeamInboxReader` merges all `inboxes/*.json`, assigns `to` from the filename when absent, and creates deterministic message ids for rows without `messageId`.
|
|
- `TeamSentMessagesStore` keeps only the newest 200 messages in `sentMessages.json`. This is a UI/local persistence cap, not a long-term external conversation history.
|
|
- `TeamMessageFeedService` merges inbox messages, lead session messages, and sent messages, then dedupes, links passive summaries, attaches lead session ids, and annotates slash command responses.
|
|
- `TeamMessageFeedService` is optimized for UI display, not for provider delivery or privacy policy.
|
|
- `InboxMessage.source` already has multiple categories: `inbox`, `lead_session`, `lead_process`, `runtime_delivery`, `user_sent`, `system_notification`, `cross_team`, `cross_team_sent`.
|
|
- Existing `conversationId` and `replyToConversationId` are used for cross-team routing and can inspire messenger conversation identity, but they are not enough by themselves for Telegram provider links.
|
|
- `inboxes/user.json` can contain teammate replies to the user without stable provider thread context.
|
|
|
|
Implication:
|
|
|
|
```text
|
|
Messenger history must not be derived lazily from the renderer feed.
|
|
It must be committed as a conversation ledger when an external route is involved.
|
|
```
|
|
|
|
## Top 3 History Models
|
|
|
|
### 1. Canonical MessengerConversationStore plus Telegram projection ledger
|
|
|
|
🎯 9 🛡️ 9 🧠 7 Approx change size: 4000-9000 LOC
|
|
|
|
Shape:
|
|
|
|
```text
|
|
provider inbound committed locally
|
|
-> MessengerConversationStore append inbound
|
|
-> local delivery to lead/team
|
|
-> safe local replies appended to same conversation
|
|
-> TelegramProjectionLedger sends only eligible projection events
|
|
-> provider message ids stored as ProviderMessageLink
|
|
```
|
|
|
|
Why this is best:
|
|
|
|
- Telegram topic is a view of an external conversation, not the data source.
|
|
- Existing UI feed remains untouched for local app semantics.
|
|
- Provider delivery idempotency and reply-to mapping have a durable home.
|
|
- Future WhatsApp/Discord adapters can reuse the same core model.
|
|
- Privacy policy can be enforced before a row becomes externally projectable.
|
|
|
|
Weaknesses:
|
|
|
|
- More code.
|
|
- Needs migration/UI integration to show messenger conversations.
|
|
- Requires careful linking from existing team replies to the correct conversation.
|
|
|
|
Verdict:
|
|
|
|
```text
|
|
Use this.
|
|
```
|
|
|
|
### 2. Reuse existing TeamMessageFeedService as canonical history
|
|
|
|
🎯 5 🛡️ 4 🧠 3 Approx change size: 900-2200 LOC
|
|
|
|
Shape:
|
|
|
|
```text
|
|
watch TeamMessageFeedService
|
|
filter messages
|
|
send eligible messages to Telegram topic
|
|
store provider links separately
|
|
```
|
|
|
|
Why it is tempting:
|
|
|
|
- Much less new architecture.
|
|
- UI already displays this feed.
|
|
- Existing refresh/invalidation paths exist.
|
|
|
|
Why it is risky:
|
|
|
|
- Feed is display-oriented and merges many sources.
|
|
- It can annotate slash command responses.
|
|
- It dedupes and links passive summaries for UI purposes.
|
|
- It includes local-only concepts that should never leave the app by default.
|
|
- It has no long-term guarantee because `sentMessages.json` caps at 200 rows.
|
|
|
|
Verdict:
|
|
|
|
```text
|
|
Do not use as provider source of truth.
|
|
Can be an input to a projection gate only.
|
|
```
|
|
|
|
### 3. Telegram topic as the canonical history
|
|
|
|
🎯 4 🛡️ 5 🧠 5 Approx change size: 1800-4500 LOC
|
|
|
|
Shape:
|
|
|
|
```text
|
|
send everything important to Telegram
|
|
use Telegram topic message ids as history
|
|
local app reads/links only provider ids
|
|
```
|
|
|
|
Why it is attractive:
|
|
|
|
- User sees history in Telegram.
|
|
- Less local history UI work.
|
|
|
|
Why it fails:
|
|
|
|
- Bot API does not provide a general documented way to read arbitrary private chat history later.
|
|
- If delivery to Telegram is ambiguous, local source of truth is unclear.
|
|
- If user deletes messages or blocks bot, local product history degrades.
|
|
- Provider-specific semantics leak into core.
|
|
- WhatsApp/Discord will not match exactly.
|
|
|
|
Verdict:
|
|
|
|
```text
|
|
Reject for core architecture.
|
|
Telegram is projection only.
|
|
```
|
|
|
|
## Recommended Canonical Model
|
|
|
|
Use two related ledgers:
|
|
|
|
```text
|
|
MessengerConversationStore
|
|
what happened in the external-user conversation
|
|
|
|
MessengerProviderProjectionLedger
|
|
what was attempted/sent/linked in Telegram
|
|
```
|
|
|
|
Conversation row:
|
|
|
|
```ts
|
|
interface MessengerConversationMessage {
|
|
id: string;
|
|
conversationId: string;
|
|
routeId: string;
|
|
bindingId: string;
|
|
teamId: string;
|
|
direction: 'inbound_from_user' | 'outbound_to_user' | 'internal_note';
|
|
author: {
|
|
kind: 'external_user' | 'team_member' | 'team_lead' | 'system';
|
|
memberId?: string;
|
|
displayName: string;
|
|
};
|
|
text: string;
|
|
createdAt: string;
|
|
externalVisibility:
|
|
| 'projectable'
|
|
| 'local_only'
|
|
| 'blocked_by_policy'
|
|
| 'requires_manual_approval';
|
|
source: {
|
|
kind:
|
|
| 'telegram_update'
|
|
| 'team_inbox'
|
|
| 'lead_session'
|
|
| 'runtime_delivery'
|
|
| 'manual_ui'
|
|
| 'system';
|
|
localMessageId?: string;
|
|
providerUpdateId?: string;
|
|
providerMessageId?: string;
|
|
leadSessionId?: string;
|
|
};
|
|
replyTo?: {
|
|
conversationMessageId?: string;
|
|
providerMessageLink?: ProviderMessageLink;
|
|
localMessageId?: string;
|
|
};
|
|
policy: {
|
|
sanitized: boolean;
|
|
strippedInternalBlocks: boolean;
|
|
reasonCodes: string[];
|
|
};
|
|
}
|
|
```
|
|
|
|
Projection row:
|
|
|
|
```ts
|
|
interface MessengerProviderProjectionRecord {
|
|
id: string;
|
|
conversationMessageId: string;
|
|
provider: 'telegram';
|
|
routeId: string;
|
|
providerTarget: {
|
|
chatIdHash: string;
|
|
messageThreadId: string;
|
|
};
|
|
status:
|
|
| 'pending'
|
|
| 'sending'
|
|
| 'sent'
|
|
| 'ambiguous'
|
|
| 'failed_retryable'
|
|
| 'failed_terminal'
|
|
| 'suppressed';
|
|
payloadHash: string;
|
|
providerMessageLink?: ProviderMessageLink;
|
|
attempts: number;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
lastError?: string;
|
|
}
|
|
```
|
|
|
|
Important:
|
|
|
|
```text
|
|
The conversation store can contain local-only rows.
|
|
The projection ledger can only contain rows that passed external visibility policy.
|
|
```
|
|
|
|
## What Counts As Conversation History
|
|
|
|
For Telegram user-facing history, include:
|
|
|
|
```text
|
|
1. User inbound messages accepted from Telegram.
|
|
2. Lead replies explicitly addressed to user.
|
|
3. Teammate replies explicitly addressed to user.
|
|
4. User manual messages from local UI that are intentionally sent to the team under this route.
|
|
5. Short system status messages that are explicitly external-facing, for example "desktop offline".
|
|
```
|
|
|
|
Do not include by default:
|
|
|
|
```text
|
|
lead thoughts
|
|
tool summaries
|
|
slash command outputs
|
|
task status notifications
|
|
cross-team internal messages
|
|
teammate-to-teammate chat
|
|
permission_request JSON
|
|
idle heartbeats
|
|
bootstrap check-ins
|
|
raw XML/agent blocks
|
|
attachments until media policy is implemented
|
|
```
|
|
|
|
This must be enforced before a message is appended as `projectable`.
|
|
|
|
## How To Handle Teammate Messages To User
|
|
|
|
The user asked for this:
|
|
|
|
```text
|
|
Messages from teammates to the user should appear in Telegram,
|
|
with each teammate clearly signed.
|
|
```
|
|
|
|
Recommended rule:
|
|
|
|
```text
|
|
Any known team member message with to == "user" can be appended to the conversation
|
|
only if it is linked to an active messenger route/conversation.
|
|
```
|
|
|
|
Rendering:
|
|
|
|
```text
|
|
[Frontend] Alice
|
|
I found the failing test. The callback resolves before token refresh.
|
|
```
|
|
|
|
```text
|
|
[QA] Mark
|
|
Reproduced on the latest build. Only happens after session restore.
|
|
```
|
|
|
|
Why prefix instead of separate bots:
|
|
|
|
- One bot per team member is much harder to manage.
|
|
- Multiple bots do not solve core routing.
|
|
- Prefix keeps the topic readable.
|
|
- It works across providers later.
|
|
|
|
Routing requirement:
|
|
|
|
```text
|
|
Do not send every message to user globally.
|
|
Send only messages whose conversationId or relay link ties them to the active messenger conversation.
|
|
```
|
|
|
|
## Conversation Identity
|
|
|
|
Use one active user-facing conversation per team route in MVP:
|
|
|
|
```text
|
|
conversationId = routeId + currentConversationSeq
|
|
```
|
|
|
|
MVP can start with:
|
|
|
|
```text
|
|
one open conversation per team topic
|
|
```
|
|
|
|
Later:
|
|
|
|
```text
|
|
multiple conversations per team topic with task/thread labels
|
|
```
|
|
|
|
Why not one conversation per message:
|
|
|
|
- Too noisy.
|
|
- Hard for the lead to maintain context.
|
|
- Telegram topic already groups by team.
|
|
|
|
Why not only one global conversation for all teams:
|
|
|
|
- Reply routing becomes ambiguous.
|
|
- User needs team-level separation.
|
|
- Topics per team become mostly cosmetic.
|
|
|
|
## Backfill Policy
|
|
|
|
Backfill is risky because old local history may contain private/internal context.
|
|
|
|
Top 3 backfill options:
|
|
|
|
### A. No automatic backfill, send a compact connection marker
|
|
|
|
🎯 9 🛡️ 9 🧠 3 Approx change size: 500-1200 LOC
|
|
|
|
On topic creation:
|
|
|
|
```text
|
|
Connected to Agent Teams.
|
|
Team: Frontend
|
|
New messages will appear here.
|
|
```
|
|
|
|
Optional local-only UI shows older app history, but Telegram starts clean.
|
|
|
|
Verdict:
|
|
|
|
```text
|
|
Use for MVP.
|
|
```
|
|
|
|
### B. User-approved summary backfill
|
|
|
|
🎯 8 🛡️ 8 🧠 6 Approx change size: 1800-4000 LOC
|
|
|
|
Desktop prepares a summary:
|
|
|
|
```text
|
|
Recent context:
|
|
- Alice is debugging auth callback tests.
|
|
- Mark is checking session restore.
|
|
- Open question: should refresh happen before redirect?
|
|
```
|
|
|
|
User explicitly approves before sending.
|
|
|
|
Verdict:
|
|
|
|
```text
|
|
Good Phase 2.
|
|
```
|
|
|
|
### C. Raw transcript backfill
|
|
|
|
🎯 4 🛡️ 3 🧠 5 Approx change size: 1600-3600 LOC
|
|
|
|
Desktop sends last N messages from local feed into Telegram.
|
|
|
|
Problems:
|
|
|
|
- High privacy leak risk.
|
|
- Rate-limit/noise risk.
|
|
- Duplicates provider projection.
|
|
- Old messages may lack clean source/route links.
|
|
- Telegram message timestamps become send time, not original time.
|
|
|
|
Verdict:
|
|
|
|
```text
|
|
Reject by default.
|
|
Only allow export/manual paste workflows later.
|
|
```
|
|
|
|
## History Display In Telegram
|
|
|
|
Telegram topic should show:
|
|
|
|
```text
|
|
inbound user message
|
|
team reply with member prefix
|
|
short status markers
|
|
optional typing/draft/progress indicator
|
|
```
|
|
|
|
It should not try to reproduce the full local app timeline.
|
|
|
|
Recommended topic message examples:
|
|
|
|
```text
|
|
You
|
|
Can you check why login redirects loop?
|
|
```
|
|
|
|
```text
|
|
[Lead] Agent Teams
|
|
I routed this to Frontend. Alice is checking the auth callback.
|
|
```
|
|
|
|
```text
|
|
[Frontend] Alice
|
|
Found the loop. The callback reads a stale refresh token after restore.
|
|
```
|
|
|
|
```text
|
|
[System]
|
|
Desktop went offline. Open Agent Teams and resend if this still matters.
|
|
```
|
|
|
|
Avoid:
|
|
|
|
```text
|
|
tool call summaries
|
|
stdout chunks
|
|
agent chain-of-thought style text
|
|
raw task board mutations
|
|
every idle/status heartbeat
|
|
```
|
|
|
|
## Progress Indicators
|
|
|
|
Top 3 options:
|
|
|
|
### 1. `sendChatAction(typing)` heartbeat while a route-linked answer is pending
|
|
|
|
🎯 8 🛡️ 8 🧠 4 Approx change size: 700-1500 LOC
|
|
|
|
Pros:
|
|
|
|
- Official method.
|
|
- Supports `message_thread_id`.
|
|
- Lasts 5 seconds or less, so it naturally expires.
|
|
- Does not create message history clutter.
|
|
|
|
Cons:
|
|
|
|
- Needs throttling.
|
|
- Can imply active work even if the lead is blocked.
|
|
|
|
Verdict:
|
|
|
|
```text
|
|
Use carefully after inbound commit, while local delivery is pending or agent turn is active.
|
|
```
|
|
|
|
### 2. `sendMessageDraft`
|
|
|
|
🎯 6 🛡️ 6 🧠 7 Approx change size: 1200-3000 LOC
|
|
|
|
Pros:
|
|
|
|
- New Bot API method for partial generated messages.
|
|
- Supports `message_thread_id`.
|
|
- Could feel impressive.
|
|
|
|
Cons:
|
|
|
|
- Draft lifecycle/id semantics need real-world testing.
|
|
- It might leak partial agent output before safety/projection filtering.
|
|
- Harder to reconcile if final answer is suppressed.
|
|
|
|
Verdict:
|
|
|
|
```text
|
|
Do not use in MVP.
|
|
Only consider for final-answer generation after projection gate is mature.
|
|
```
|
|
|
|
### 3. Explicit status messages like "Alice is working"
|
|
|
|
🎯 7 🛡️ 6 🧠 3 Approx change size: 500-1200 LOC
|
|
|
|
Pros:
|
|
|
|
- Simple.
|
|
- Durable and visible.
|
|
|
|
Cons:
|
|
|
|
- Adds clutter.
|
|
- Can become spammy.
|
|
- Hard to keep accurate.
|
|
|
|
Verdict:
|
|
|
|
```text
|
|
Use only for major state changes, not continuous progress.
|
|
```
|
|
|
|
## Reply-To Routing
|
|
|
|
Incoming Telegram reply should route by priority:
|
|
|
|
```text
|
|
1. reply_to_message.message_id maps to ProviderMessageLink
|
|
2. message_thread_id maps to team route
|
|
3. slash command selects member or action
|
|
4. fallback to lead
|
|
```
|
|
|
|
Provider message link:
|
|
|
|
```ts
|
|
interface ProviderMessageLink {
|
|
provider: 'telegram';
|
|
routeId: string;
|
|
providerChatIdHash: string;
|
|
providerMessageThreadId: string;
|
|
providerMessageId: string;
|
|
conversationMessageId: string;
|
|
authorKind: 'external_user' | 'team_member' | 'team_lead' | 'system';
|
|
authorMemberId?: string;
|
|
sentAt: string;
|
|
}
|
|
```
|
|
|
|
Examples:
|
|
|
|
```text
|
|
User replies to Alice message
|
|
-> route to team topic
|
|
-> include reply target "Alice" in lead/team prompt
|
|
-> if direct teammate reply mode is enabled, deliver to Alice inbox
|
|
```
|
|
|
|
```text
|
|
User sends a new message in team topic without reply
|
|
-> route to lead by default
|
|
```
|
|
|
|
MVP decision:
|
|
|
|
```text
|
|
Do not DM arbitrary teammate automatically from reply-to.
|
|
Route to lead with reply context first.
|
|
```
|
|
|
|
Why:
|
|
|
|
- Lead can coordinate.
|
|
- Teammate may be offline or mid-turn.
|
|
- Direct teammate routing can be added after route policy is proven.
|
|
|
|
## Commands In Topic
|
|
|
|
Keep commands minimal in MVP:
|
|
|
|
```text
|
|
/teams
|
|
/status
|
|
/help
|
|
/disconnect
|
|
```
|
|
|
|
Do not overload the topic with rich command grammar early.
|
|
|
|
Team selection:
|
|
|
|
```text
|
|
Primary selection is topic.
|
|
Commands are fallback and diagnostics.
|
|
```
|
|
|
|
If message arrives outside a topic:
|
|
|
|
```text
|
|
show active teams
|
|
ask user to pick a topic
|
|
do not infer from recent activity unless exactly one team is active
|
|
```
|
|
|
|
## Projection State Machine
|
|
|
|
```text
|
|
local_message_seen
|
|
-> policy_checked
|
|
-> conversation_appended
|
|
-> projection_pending
|
|
-> provider_sending
|
|
-> provider_sent
|
|
-> linked
|
|
```
|
|
|
|
Failure states:
|
|
|
|
```text
|
|
suppressed_by_policy
|
|
requires_manual_approval
|
|
provider_ambiguous
|
|
provider_failed_retryable
|
|
provider_failed_terminal
|
|
route_disabled
|
|
topic_needs_repair
|
|
```
|
|
|
|
Important invariant:
|
|
|
|
```text
|
|
Provider projection cannot start before the message is appended to MessengerConversationStore.
|
|
```
|
|
|
|
This ensures Telegram never has a message that the local conversation store cannot explain.
|
|
|
|
## Duplicate Prevention
|
|
|
|
Use three layers:
|
|
|
|
```text
|
|
1. Conversation idempotency key
|
|
2. Projection payload hash
|
|
3. Provider message link
|
|
```
|
|
|
|
Conversation idempotency:
|
|
|
|
```text
|
|
source.kind + source.localMessageId/providerUpdateId + routeId
|
|
```
|
|
|
|
Projection idempotency:
|
|
|
|
```text
|
|
conversationMessageId + provider + routeId + payloadHash
|
|
```
|
|
|
|
Provider link:
|
|
|
|
```text
|
|
stored only after sendMessage returns Message
|
|
```
|
|
|
|
If Telegram send times out:
|
|
|
|
```text
|
|
mark projection ambiguous
|
|
do not retry automatically with the same text unless policy accepts duplicate risk
|
|
surface "delivery uncertain" in local UI
|
|
```
|
|
|
|
This matches earlier outbound delivery research.
|
|
|
|
## Edit And Delete Policy
|
|
|
|
Do not use Telegram edit/delete as the normal sync mechanism.
|
|
|
|
Reasons:
|
|
|
|
- `deleteMessage` has a 48-hour limit and service-message exceptions.
|
|
- `editMessageText` has constraints and can return different shapes.
|
|
- Edits are provider-specific and hard to reconcile across adapters.
|
|
|
|
MVP:
|
|
|
|
```text
|
|
append-only Telegram topic
|
|
append-only local conversation ledger
|
|
corrections are new messages
|
|
```
|
|
|
|
Later:
|
|
|
|
```text
|
|
support explicit "correct last bot message" for bot-authored messages only
|
|
```
|
|
|
|
## Storage And Retention
|
|
|
|
Do not rely on:
|
|
|
|
```text
|
|
sentMessages.json cap of 200
|
|
inboxes/user.json as long-term canonical external history
|
|
Telegram topic as recoverable history
|
|
```
|
|
|
|
Use:
|
|
|
|
```text
|
|
getAppDataPath()/messenger-conversations/
|
|
bindings/
|
|
routes/
|
|
conversations/
|
|
projections/
|
|
```
|
|
|
|
Retention tiers:
|
|
|
|
```text
|
|
MVP:
|
|
keep text conversation rows locally until user deletes route/binding
|
|
|
|
Later:
|
|
per-route retention setting
|
|
export/delete controls
|
|
encrypted local store option
|
|
encrypted backend queue option
|
|
```
|
|
|
|
## UI Implications
|
|
|
|
Desktop should show:
|
|
|
|
```text
|
|
Connected Telegram account
|
|
team topics/routes
|
|
last projected message status
|
|
delivery uncertain warnings
|
|
local-only vs sent-to-Telegram marker
|
|
reconnect/repair action
|
|
```
|
|
|
|
Message row badges:
|
|
|
|
```text
|
|
local only
|
|
sent to Telegram
|
|
delivery uncertain
|
|
blocked by policy
|
|
needs approval
|
|
```
|
|
|
|
This matters because the local app feed and Telegram topic will not always match exactly by design.
|
|
|
|
## Clean Architecture Placement
|
|
|
|
Core/domain:
|
|
|
|
```text
|
|
ConversationMessage
|
|
ConversationPolicy
|
|
ProjectionEligibility
|
|
ProviderMessageLink
|
|
ProjectionStateMachine
|
|
BackfillPolicy
|
|
```
|
|
|
|
Core/application:
|
|
|
|
```text
|
|
AppendInboundProviderMessageUseCase
|
|
AppendLocalReplyUseCase
|
|
EvaluateProjectionUseCase
|
|
ProjectConversationMessageUseCase
|
|
ReconcileProjectionUseCase
|
|
BuildBackfillPreviewUseCase
|
|
```
|
|
|
|
Ports:
|
|
|
|
```text
|
|
MessengerConversationStore
|
|
MessengerProjectionLedger
|
|
MessengerProviderGateway
|
|
TeamMessageSource
|
|
ExternalVisibilityPolicy
|
|
```
|
|
|
|
Adapters:
|
|
|
|
```text
|
|
TeamMessageFeedInputAdapter
|
|
TelegramProjectionAdapter
|
|
FileConversationStore
|
|
FileProjectionLedger
|
|
```
|
|
|
|
Important dependency rule:
|
|
|
|
```text
|
|
TeamMessageFeedInputAdapter may depend on existing team services.
|
|
Core policy must not depend on TeamMessageFeedService.
|
|
```
|
|
|
|
## Edge Cases To Test
|
|
|
|
History and projection:
|
|
|
|
- Topic created after team already has a long local message history.
|
|
- No automatic raw backfill occurs.
|
|
- User-approved summary backfill sends only approved summary.
|
|
- `sentMessages.json` drops old rows, but MessengerConversationStore keeps route conversation history.
|
|
- Same local message appears in both inbox and sent messages, only one conversation row is created.
|
|
- Same conversation row is not projected twice.
|
|
|
|
Teammate messages:
|
|
|
|
- Alice sends `to=user` in a route-linked conversation, Telegram gets `[Alice]`.
|
|
- Alice sends `to=user` outside a route-linked conversation, Telegram gets nothing.
|
|
- Alice sends teammate-internal message, Telegram gets nothing.
|
|
- Lead sends generic thought with no `to=user`, Telegram gets nothing.
|
|
- Slash command result is visible in UI, Telegram gets nothing by default.
|
|
|
|
Reply routing:
|
|
|
|
- User replies to Alice's Telegram message, provider link maps to Alice context.
|
|
- User replies to system offline notice, route remains lead fallback.
|
|
- User writes in topic without reply, route goes to lead.
|
|
- User writes outside topic with multiple teams connected, bot asks to choose topic.
|
|
- Unknown provider message id does not crash routing.
|
|
|
|
Provider behavior:
|
|
|
|
- `sendMessage` success stores provider message link.
|
|
- `sendMessage` timeout marks ambiguous and does not auto-duplicate.
|
|
- `deleteMessage` failure does not corrupt local conversation.
|
|
- `editForumTopic` failure does not reroute by title.
|
|
- Topic repair creates new topic and marks old projection state historical.
|
|
|
|
Privacy:
|
|
|
|
- Internal blocks stripped before projectable rows.
|
|
- Policy blocks `permission_request` JSON.
|
|
- Policy blocks tool stdout/stderr unless manually approved.
|
|
- Backfill preview redacts secrets and requires explicit approval.
|
|
|
|
## Decision Update
|
|
|
|
Add this to the implementation plan:
|
|
|
|
```text
|
|
MessengerConversationStore is mandatory for MVP.
|
|
Telegram topic is provider projection only.
|
|
No raw automatic history backfill.
|
|
One topic per team route.
|
|
One open conversation per team topic in MVP.
|
|
Teammate messages to user are projected only when route-linked and external-safe.
|
|
```
|
|
|
|
Recommended MVP behavior:
|
|
|
|
```text
|
|
Connect Telegram
|
|
-> create one topic per selected team
|
|
-> send a short connection marker
|
|
-> start projecting new inbound/outbound external-safe messages
|
|
-> show local projection status in desktop
|
|
```
|
|
|
|
Main remaining uncertainty:
|
|
|
|
```text
|
|
Should reply-to a teammate message route directly to that teammate,
|
|
or always go through lead with reply context?
|
|
```
|
|
|
|
My current recommendation:
|
|
|
|
🎯 8 🛡️ 8 🧠 5 Approx change size: +800-1800 LOC
|
|
|
|
```text
|
|
MVP routes all Telegram inbound through lead,
|
|
but includes reply-to teammate context in the prompt.
|
|
Add direct teammate routing later as an explicit per-team setting.
|
|
```
|
|
|
|
Reason:
|
|
|
|
```text
|
|
It preserves coordination, avoids surprising teammate interruptions,
|
|
and still lets the lead tell Alice "the user replied to your message".
|
|
```
|