This guide covers how to configure AI agents to stay in the Telegram dequeue loop reliably — including hook installation for both VS Code (GitHub Copilot Chat) and Claude Code.
When an agent is running a Telegram Bridge MCP session, it must remain alive and call dequeue continuously to receive messages. Without a loop guard, the host IDE may terminate the agent conversation at any time — dropping the session silently.
The loop guard is a Stop hook that checks for an active Telegram session file before allowing the host to shut down the agent. If a session file is found with content, the hook blocks the stop and prompts the agent to resume the dequeue loop. If no session file exists (or it is empty), the hook exits cleanly and allows normal shutdown.
Two variants are provided:
| File | Host | Platform |
|---|---|---|
.github/hooks/telegram-loop-guard.ps1 + .json |
VS Code / GitHub Copilot Chat | Windows (PowerShell) |
.claude/hooks/telegram-loop-guard.sh |
Claude Code | macOS / Linux (Bash) |
-
If you cloned this repo, the hook files are already present at:
.github/hooks/telegram-loop-guard.json .github/hooks/telegram-loop-guard.ps1If you are setting up hooks in a different project, copy both files into that project's
.github/hooks/directory. -
Restart VS Code (or reload the window). VS Code auto-discovers hook files from
.github/hooks/— no additional settings entry required.
When Copilot Chat tries to end the agent conversation, VS Code fires the Stop hook before terminating. The hook:
- Reads the hook event JSON from stdin.
- Extracts the
cwdto locate the VS Code workspace storage directory. - Finds the session memory folder under
GitHub.copilot-chat\memory-tool\memories\. - Looks for
telegram-session.md— if it exists and has content, the hook outputs adecision: blockresponse. - VS Code keeps the agent alive and the agent is prompted to resume the dequeue loop.
The hook uses the sessionId from hook input (when available) to find the exact session memory directory. When sessionId is absent (VS Code omits it on some Stop events), it falls back to scanning all memory directories for a non-empty telegram-session.md.
Agents must write their session credentials to telegram-session.md in their Copilot memory using the memory tool. The file should contain the session token, SID, and session name. The hook only blocks stop when this file has content — an empty file is treated as no active session.
-
Copy the hook script into your project's
.claude/hooks/directory:.claude/hooks/telegram-loop-guard.shThis file ships in this repo. After copying, ensure it is executable:
chmod +x .claude/hooks/telegram-loop-guard.sh
-
Register the hook in
.claude/settings.local.json:{ "hooks": { "Stop": [ { "matcher": "", "hooks": [ { "type": "command", "command": ".claude/hooks/telegram-loop-guard.sh" } ] } ] } } -
Restart Claude Code to activate the hook.
When Claude Code is about to stop a session, it fires the Stop hook and reads stdout for a decision. The hook:
- Reads the hook event JSON from stdin.
- Checks
stop_hook_active— if true, exits immediately to prevent an infinite loop. - Searches
~/.claude/projects/for anytelegram/session.mdfile with content. - If found, outputs a JSON block response with
decision: block. - Claude Code keeps the session alive and the agent is prompted to resume the dequeue loop.
Claude Code agents write their session credentials to a telegram/session.md file in the project memory directory (~/.claude/projects/<project-hash>/memory/telegram/session.md). The hook looks for any such file with content under ~/.claude/projects/. An empty session file is treated as no active session.
Agents using this MCP must not exit between messages. The correct pattern is:
action(type: "session/start") → drain (dequeue max_wait:0 until empty) → block (dequeue) → handle → drain → block → ...
- Drain first — call
dequeue(max_wait: 0)in a loop untilempty: trueto clear any backlog. - Block — call
dequeue()(no args) to wait up to 300 seconds for the next message. - On timeout — call
dequeue()again immediately. Optionally send a brief check-insend(type: "notification"). - On message — handle it, then drain, then block again.
There is no exit condition. The agent loops until it receives a shutdown signal or action(type: "session/close") is called. The loop guard enforces this at the host level — if the host tries to stop the agent while a session is active, the guard blocks it.
Reducing token usage with compact mode: Pass response_format: "compact" on each dequeue call to save approximately 445 tokens per session. In compact mode, empty: true is omitted on empty drain polls (infer empty from the absence of updates), while timed_out: true is always emitted. See docs/compact-mode-migration.md for the before/after loop pattern and a full field-suppression table.
For full behavioral rules and tool usage patterns, see docs/help/guide.md.
- In VS Code: check the Output panel for Copilot Chat hook errors. Confirm
.github/hooks/telegram-loop-guard.jsonand.ps1are present in the repo. - In Claude Code: confirm the hook is registered in
.claude/settings.local.jsonand the.shfile has execute permission (chmod +x). - Verify the session file exists and has content — an empty session file allows stop.
Clear the session file to allow the host to stop cleanly:
VS Code (PowerShell):
# Find and clear your telegram-session.md
Set-Content -Path "<path-to-memories>\telegram-session.md" -Value ""Claude Code:
# Clear the session file
> ~/.claude/projects/<project-hash>/memory/telegram/session.mdOnce the file is empty, the hook exits 0 and the host can shut down the agent normally.
If the Telegram Bridge MCP server is down or unreachable, the agent may be blocked from stopping but unable to resume the loop. Clear the session file as described above to unblock the stop.