Skip to content

fix: correctly extract text from TG Rich Message (Bot API 10.1)#93

Open
changhansung wants to merge 342 commits into
suzuke:mainfrom
songsid:fix/tg-rich-message-extract-v2
Open

fix: correctly extract text from TG Rich Message (Bot API 10.1)#93
changhansung wants to merge 342 commits into
suzuke:mainfrom
songsid:fix/tg-rich-message-extract-v2

Conversation

@changhansung

Copy link
Copy Markdown
Contributor

Rewrites extractRichText based on actual getUpdates JSON structure:

  • paragraph/heading: text is array of string | {type, text}
  • table: cells is 2D array of {text}
  • pre: same as paragraph
  • Removes [TG-RAW] debug dump

Enables bot-to-bot @mention when sender uses sendRichMessage.

OpenCode shows 'Permission required' when accessing external directories,
followed by a 'confirm' prompt. Add getRuntimeDialogs to auto-press Enter
on both layers.
Runtime dialogs (Permission required, confirm) need fast response.
30s meant each prompt waited up to 30s before auto-dismiss.
5s keeps CPU impact minimal (single capture-pane per cycle).
Select 'Allow always' instead of 'Allow once' so the same directory
won't prompt again. Added Right/Left to sendSpecialKey and dialog
handler SPECIAL_KEYS set.
Kiro CLI 2.0 TUI mode uses ~1.1GB per instance. Classic mode uses
~300MB. Add --classic flag to kiro-cli chat command.
1. classic-channel-manager.ts:
   - ClassicChannel.collab field (persisted to classicBot.yaml)
   - toggleCollab(channelId): toggle + save, returns new state
   - isCollab(channelId): check collab status

2. discord-adapter.ts:
   - Register /collab slash command
   - Only block own messages (not all bots) — let fleet-manager decide
   - Pass isBotMessage flag in message emit
   - Emit bot user ID with 'started' event

3. channel/types.ts:
   - InboundMessage.isBotMessage?: boolean

4. fleet-manager.ts:
   - /collab handler: admin-only toggle, responds with new state
   - handleInboundMessage: bot messages only pass through to collab channels
   - handleClassicChannelMessage collab mode:
     - Log all messages (including bots) to chat-logs
     - Trigger on exact @mention (<@BOT_USER_ID>) only
     - @everyone/@here/@ROLE don't trigger (not <@id> format)
     - Strip @mention, forward clean text to agent
   - Store botUserId from adapter 'started' event
Part 1: pre_task_command
- types.ts: InstanceConfig.pre_task_command?: string
- classic-channel-manager.ts: ClassicChannel.preTaskCommand, load/save,
  getPreTaskCommand(channelId)
- daemon.ts: in pasteLock, deliverMessage(pre_task_command) before the
  actual message (waits for idle between both)
- fleet-manager.ts: startClassicInstance passes preTaskCommand from
  classicChannels config into InstanceConfig

Part 2: General steering
- GENERAL_INSTRUCTIONS: add 'Instance Configuration Tips' section with
  reviewer pre_task_command, collab mode, and cost_guard suggestions
Fleet start syncs src/general-knowledge/*.md to General instance's
.kiro/steering/ directory. Knowledge files are version-controlled —
updating AgEnD automatically gives General new skills.

Includes initial skills.md: tmux health check, reviewer session
management, fork instance (session cloning).
Previously, any instructions change (including knowledge file sync)
forced skipResume=true, losing conversation context on every restart.

Now: resume is always preserved. When instructions change, a [system]
notice is injected before the next user message telling the agent to
re-read its steering files.

Also: syncGeneralKnowledge only writes files when content actually
changed, preventing unnecessary instructions hash diffs.
Telegram ClassicBot allows /start in private chats and regular groups
(not the primary forum group) to spawn lightweight agent instances.

Changes:
- telegram.ts: allow non-primary-group messages through access control
  for classic mode routing (fleet-manager handles its own ACL)
- classic-channel-manager.ts: add allowed_groups/allowed_users to
  ClassicBotYaml interface + isGroupAllowed/isUserAllowed methods
- fleet-manager.ts: add Telegram classic mode routing in
  handleInboundMessage — /start, /stop, /chat commands + private chat
  auto-forward (no /chat prefix needed)
- topic-commands.ts: register /start, /stop, /chat as default-scope
  bot commands via setMyCommands API

Coexists with topic mode on the same bot token:
- Primary forum group (group_id) → topic mode (unchanged)
- Other groups/private chats → classic mode
Telegram classic mode messages (threadId=null, chatId != primaryGroupId)
were blocked by fleet-level access control before reaching the classic
mode routing block. Now these messages bypass fleet ACL and rely on
classicBot.yaml's own isGroupAllowed/isUserAllowed checks.
Core multi-adapter architecture enabling simultaneous Telegram + Discord:

types.ts:
- ChannelConfig: add optional 'id' field (adapter identifier)
- FleetConfig: add 'channels?: ChannelConfig[]' (backward compat with 'channel')

config.ts:
- loadFleetConfig: normalize 'channel' singular → 'channels' array
- Auto-assign id = type when not specified

fleet-manager.ts:
- Add adapters Map<string, ChannelAdapter> for multi-adapter lookup
- Add instanceAdapterBinding Map to track which adapter each instance uses
- Add getAdapterForInstance() / bindInstanceAdapter() helpers
- Split startSharedAdapter into loop + startSingleAdapter + startAdditionalAdapter
- Primary adapter (first channel) stays as this.adapter for backward compat
- Additional adapters get full event wiring (message, callback_query, slash_command)
- handleOutboundFromInstance: route via getAdapterForInstance
- handleToolStatusFromInstance: route via getAdapterForInstance
- Bind instances to adapters on inbound message (topic, classic, general)
- stopAll: stop all adapters

Backward compatible: single 'channel' config works exactly as before.
Multi-channel: add 'channels' array to run both platforms simultaneously.
…und react

1. bindInstanceAdapter: skip general_topic instances (always use primary)
2. stopAll: single loop over adapters map, no double-stop
3. General inbound: use msg.adapterId to resolve correct adapter for
   react() and processAttachments() instead of hardcoded this.adapter
Quickstart now supports simultaneous Telegram + Discord setup:

Fresh setup:
- Step 2 offers: 1. Telegram, 2. Discord, 3. Both
- 'Both' runs Telegram flow then Discord flow sequentially
- Single platform: writes channel (singular) for backward compat
- Both: writes channels array format

Existing fleet.yaml:
- New option '2. Add another platform'
- Auto-detects which platforms are already configured
- Runs the missing platform's flow
- Converts channel → channels array, appends new platform
- Appends token to .env

Refactored:
- Extracted runTelegramFlow() and runDiscordFlow() as reusable functions
- PlatformResult interface for structured flow output
- buildChannelConfig() helper for config object generation
… update

1. startAdditionalAdapter: add topic_closed handler, admin slash commands
   (/compact, /save, /load, /ctx, /collab), remove unused _fleet param
2. quickstart 'Add another platform': .env token replace (not skip) when
   key already exists — supports token rotation

Verified stable (no changes needed):
3. Instance names: classicInstanceName uses channelName+channelId, topic
   mode uses fleet.yaml instance keys — unaffected by multi-adapter
4. --resume: skipResume in daemon.ts only triggers on crash loop or failed
   resume — multi-adapter changes don't touch daemon lifecycle
Telegram API timeout in registerBotCommands was crashing the entire
startAll flow, preventing classic instances from starting. Now both
are non-fatal — fleet continues with available adapters.
When multiple channels are configured (e.g. Discord + Telegram),
create general-discord and general-telegram instead of a single
general. Single channel keeps the name 'general' (backward compat).
- findGeneralInstance(adapterId) matches by name suffix
- autoCreateTopics assigns topic_id by name: *-telegram→1, *-discord→general_channel_id
- handleInboundMessage passes msg.adapterId to findGeneralInstance
…rtup

fleet.yaml with channels: (plural array) left fleet.channel undefined,
causing topicMode=false and skipping adapter startup + classic instances.
Now checks both fleet.channel and fleet.channels.
…p_id

Bug: when fleet.yaml uses channels[] array format, fleetConfig.channel
was undefined, causing primaryGroupId to be empty and all Telegram
messages to be incorrectly treated as classic candidates.

Fixes:
1. config.ts: always set channel to first entry of channels[] as
   fallback — all existing this.fleetConfig?.channel references work
2. fleet-manager: add getChannelConfig(adapterId) helper to look up
   per-adapter channel config from channels[]
3. handleInboundMessage: use getChannelConfig(msg.adapterId) for
   group_id lookup instead of hardcoded fleetConfig.channel.group_id

This ensures correct routing in both single-channel and multi-channel
modes. Telegram General Topic messages route correctly, Discord
unaffected.
changhansung and others added 30 commits June 16, 2026 16:52
fix: agend start fallback to direct fleet start when systemd unavailable
docs: complete configuration and commands reference
fix: add admin check to /restart + correct docs permissions
fix: add version to /sysinfo + improve kiro-cli context parser
fix: /status adds tmux capture fallback for context percentage
fix: remove /chat from TG ClassicBot menu
fix: TG ClassicBot chat-log + error notification delivery
Reviewer feedback: rawText starting with / would trigger /chat forwarding.
Guard: any /command without @mention is logged as empty (won't forward).
fix: guard against /command forwarding without @mention
1. Log bot outbound replies to classic instance chat-log
2. TG classic: allow bot messages through if they @mention our bot
   (previously all bot messages dropped due to !threadId early return)

Both are multi-adapter refactor regressions (ecb3169e714649).
fix: TG ClassicBot bot reply logging + bot-to-bot @mention
…pter

1. TG adapter was never setting isBotMessage (from.is_bot) — now included
2. Debug logs at adapter access control + fleet-manager entry point
3. This is likely the root cause: without isBotMessage, the bot-to-bot
   fix in beta.17 never triggered
debug: TG bot message trace + fix missing isBotMessage
fix: extract text from TG Rich Message for bot-to-bot communication
debug: dump raw TG msg when bot text is empty
kiro-cli errors if LLM ends with only a tool call and no text output.
All instances now know to output '.' after completing reply.
fix: instruct instances to produce final text after reply tool
Odd number of ``` in a message causes CLI to interpret Enter as
newline (waiting for fence closure) instead of submit. Strip all
fences when count is odd.
fix: strip unclosed code fences before CLI paste
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