codex-telegram-bridge 0.5.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. codex_telegram_bridge-0.5.3/LICENSE +21 -0
  2. codex_telegram_bridge-0.5.3/PKG-INFO +620 -0
  3. codex_telegram_bridge-0.5.3/README.md +593 -0
  4. codex_telegram_bridge-0.5.3/agent_runtime/__init__.py +17 -0
  5. codex_telegram_bridge-0.5.3/agent_runtime/adapters/__init__.py +6 -0
  6. codex_telegram_bridge-0.5.3/agent_runtime/adapters/base.py +32 -0
  7. codex_telegram_bridge-0.5.3/agent_runtime/adapters/codex_repl.py +109 -0
  8. codex_telegram_bridge-0.5.3/agent_runtime/approvals.py +87 -0
  9. codex_telegram_bridge-0.5.3/agent_runtime/capabilities.py +59 -0
  10. codex_telegram_bridge-0.5.3/agent_runtime/locks.py +111 -0
  11. codex_telegram_bridge-0.5.3/agent_runtime/transport.py +77 -0
  12. codex_telegram_bridge-0.5.3/agent_runtime/types.py +33 -0
  13. codex_telegram_bridge-0.5.3/bridge_setup.py +1134 -0
  14. codex_telegram_bridge-0.5.3/bridge_watchdog.py +192 -0
  15. codex_telegram_bridge-0.5.3/codex_audio_transcribe.py +66 -0
  16. codex_telegram_bridge-0.5.3/codex_repl_bridge.py +5414 -0
  17. codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/PKG-INFO +620 -0
  18. codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/SOURCES.txt +29 -0
  19. codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/dependency_links.txt +1 -0
  20. codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/entry_points.txt +4 -0
  21. codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/requires.txt +4 -0
  22. codex_telegram_bridge-0.5.3/codex_telegram_bridge.egg-info/top_level.txt +6 -0
  23. codex_telegram_bridge-0.5.3/pyproject.toml +53 -0
  24. codex_telegram_bridge-0.5.3/setup.cfg +4 -0
  25. codex_telegram_bridge-0.5.3/telegram_agent_bridge.py +589 -0
  26. codex_telegram_bridge-0.5.3/tests/test_agent_runtime.py +174 -0
  27. codex_telegram_bridge-0.5.3/tests/test_bridge.py +278 -0
  28. codex_telegram_bridge-0.5.3/tests/test_public_export.py +51 -0
  29. codex_telegram_bridge-0.5.3/tests/test_repl_bridge.py +1907 -0
  30. codex_telegram_bridge-0.5.3/tests/test_setup_wizard.py +239 -0
  31. codex_telegram_bridge-0.5.3/tests/test_watchdog.py +126 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Codex Telegram Bridge contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,620 @@
1
+ Metadata-Version: 2.4
2
+ Name: codex-telegram-bridge
3
+ Version: 0.5.3
4
+ Summary: Control a live Codex CLI session from Telegram.
5
+ Author: Kang Daejong
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ssamssae/codex-telegram-bridge
8
+ Project-URL: Repository, https://github.com/ssamssae/codex-telegram-bridge
9
+ Project-URL: Releases, https://github.com/ssamssae/codex-telegram-bridge/releases
10
+ Project-URL: Issues, https://github.com/ssamssae/codex-telegram-bridge/issues
11
+ Keywords: codex,telegram,cli,automation,agent
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Topic :: Communications :: Chat
19
+ Classifier: Topic :: Software Development
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Provides-Extra: asr
24
+ Requires-Dist: faster-whisper; extra == "asr"
25
+ Requires-Dist: imageio-ffmpeg; extra == "asr"
26
+ Dynamic: license-file
27
+
28
+ # Codex Telegram Bridge
29
+
30
+ [![Release](https://img.shields.io/github/v/release/ssamssae/codex-telegram-bridge)](https://github.com/ssamssae/codex-telegram-bridge/releases)
31
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
32
+
33
+ Control your live Codex CLI session from Telegram. Only Codex is supported.
34
+ Other AI CLIs are intentionally out of scope; a Claude bridge should be a
35
+ separate Claude-specific program, not a shared mode in this repository.
36
+
37
+ Codex Telegram Bridge is a phone remote for your already-running Codex TUI. Send
38
+ prompts, screenshots, videos, voice notes, and files from Telegram; the bridge
39
+ pastes them into your visible tmux Codex session, watches Codex's structured
40
+ JSONL session log, then mirrors final answers and generated media back to
41
+ Telegram.
42
+
43
+ Default `repl` mode is REPL sync, not a separate hidden chat. Your terminal
44
+ stays the source of truth, the transcript remains readable, and Telegram becomes
45
+ the remote control when you are away from the keyboard.
46
+
47
+ Install with `pipx`, then run the setup wizard:
48
+
49
+ ```bash
50
+ pipx install "git+https://github.com/ssamssae/codex-telegram-bridge.git@v0.5.1"
51
+ codex-telegram-bridge setup
52
+ codex-telegram-bridge doctor
53
+ ```
54
+
55
+ Or install from a clone:
56
+
57
+ ```bash
58
+ git clone https://github.com/ssamssae/codex-telegram-bridge.git
59
+ cd codex-telegram-bridge
60
+ python3 bridge_setup.py setup
61
+ python3 bridge_setup.py doctor
62
+ ```
63
+
64
+ Token safety rule: BotFather shows the bot token in Telegram, but paste it only
65
+ into the local terminal setup wizard. In Telegram, send only `/start`, `/ping`,
66
+ or normal prompts to your bot.
67
+
68
+ ## Public Export Model
69
+
70
+ This public repository is maintained from a private operator source through a
71
+ sanitized export step. The export keeps the reusable bridge behavior, setup
72
+ wizard, and BYO signal contract, while stripping private chat ids, token paths,
73
+ hostnames, node labels, and local automation paths before release.
74
+
75
+ Do not copy another operator's private wrapper scripts into your setup. Treat
76
+ `CRB_SIGNAL_PATH` / `TAB_LOCAL_INPUT` as the public integration boundary: your
77
+ cron job, local queue, or orchestrator writes one prompt line to the FIFO, and
78
+ the bridge owns only local delivery into the visible Codex session plus Telegram
79
+ mirroring.
80
+
81
+ Release: <https://github.com/ssamssae/codex-telegram-bridge/releases/latest>
82
+
83
+ Promo video from the v0.3 demo release:
84
+ <https://github.com/ssamssae/codex-telegram-bridge/releases/download/v0.3.10/codex-telegram-bridge-promo-v0.3.10.mp4>
85
+
86
+ The repo also includes a simpler one-shot `codex exec` mode. Both modes are
87
+ Codex-only by design so maintenance stays focused and predictable.
88
+
89
+ This is not MCP. It is a small standalone relay daemon. Default `repl` mode:
90
+
91
+ ```text
92
+ Telegram message/media
93
+ -> getUpdates polling
94
+ -> single-user chat_id allowlist
95
+ -> paste a prompt into tmux -L codex / Codex TUI
96
+ -> watch Codex JSONL session logs
97
+ -> mirror final answers to Telegram
98
+
99
+ Codex CLI input
100
+ -> JSONL user event
101
+ -> Telegram "typing..." while Codex is working
102
+ -> recover Telegram "typing..." if the bridge restarts while the Codex pane is still busy
103
+ -> send a one-shot fallback progress reply if final_answer is delayed
104
+ -> periodic detailed progress updates for long Telegram-origin turns
105
+ -> final answer mirrored to Telegram
106
+
107
+ Codex approval prompt
108
+ -> detect "Would you like to run..." in the tmux pane
109
+ -> send Telegram buttons for the visible approval choices
110
+ -> mark the selected button and remove stale choices
111
+ -> inject the selected key back into the Codex TUI
112
+
113
+ Codex selection prompt
114
+ -> detect numbered/lettered menus and y/n confirmations in the tmux pane
115
+ -> send Telegram buttons for the visible options
116
+ -> mark the selected button and remove stale choices
117
+ -> inject shortcut keys or arrow+Enter navigation back into the Codex TUI
118
+
119
+ Codex slash command
120
+ -> detect single-line commands such as /model from Telegram
121
+ -> submit them with Enter instead of the normal queued prompt key
122
+ -> mirror "Unrecognized command" errors back to Telegram
123
+ -> clear the Codex composer before Telegram input so stale typo commands cannot be appended
124
+ -> keep Telegram typing and progress updates active for long-running commands such as /goal
125
+ -> auto-request a missing second /goal + 상세스펙/상세설명 copy-paste payload once, and split combined payloads into two Telegram messages
126
+ -> scope copy-paste deduplication to the current Telegram prompt so an explicit resend can send the same /goal body again
127
+ -> classify Korean two-message copy-paste requests with bare "골", 두번/두 번, repeated 보내 verbs, 상세스팩 typo, and single-message override words
128
+
129
+ Answer media attachments
130
+ -> detect local image/video/audio paths in final answers
131
+ -> hide the local path in Telegram
132
+ -> send the actual media with sendPhoto/sendVideo/sendVoice/sendAudio
133
+
134
+ Service restart
135
+ -> run a local watchdog every 60 seconds when installed as a service
136
+ -> restart or kickstart the bridge if the user service is inactive
137
+ -> load a persistent JSONL cursor and final-answer dedup ring
138
+ -> resume from the cursor when it still matches the current session file
139
+ -> otherwise tail-scan recent JSONL after the latest user event
140
+ -> backfill the latest eligible final answer once, then resume live watching
141
+ ```
142
+
143
+ ## Why REPL Sync
144
+
145
+ - You can keep using the local Codex TUI normally while Telegram mirrors the
146
+ final answers.
147
+ - Telegram-origin prompts are pasted into the same visible transcript instead of
148
+ disappearing into a separate hidden thread.
149
+ - Terminal-origin prompts can still show Telegram `typing...` and final-answer
150
+ mirrors, so the phone stays informed even when the work started locally.
151
+ - Long-running Telegram-origin turns send progress reports with the task label,
152
+ optional task id, elapsed time, latest public progress note, next step, and
153
+ blocker status.
154
+ - Telegram-origin turns also send one early fallback progress reply after 90
155
+ seconds by default, so a turn that is producing commentary/tool activity but
156
+ has not emitted `final_answer` yet does not look silent from Telegram.
157
+ - If the daemon restarts or joins mid-turn, it checks the visible Codex pane
158
+ every 10 seconds by default and restarts Telegram `typing...` while Codex is
159
+ still working. Set `CRB_TYPING_LIVENESS_SECONDS=0` to disable this recovery
160
+ loop.
161
+ - Approval and selection prompts remain real Codex TUI prompts; the bridge sends
162
+ Telegram buttons for the visible options and injects the selected key back
163
+ into tmux.
164
+ - The daemon uses polling and a single `chat_id` allowlist, so no public webhook
165
+ or inbound port is required.
166
+
167
+ ## Quickstart
168
+
169
+ The setup wizard is designed for first-time users. It shows six steps:
170
+
171
+ ```text
172
+ [1/6] Paste the BotFather token
173
+ [2/6] Connect your Telegram chat
174
+ [3/6] Check the local Codex mode
175
+ [4/6] Write the private config
176
+ [5/6] Install the background service
177
+ [6/6] Send a setup-complete test message
178
+ ```
179
+
180
+ Important: paste the BotFather token only into the terminal when the setup
181
+ wizard asks for it. In Telegram, send only `/start`, `/ping`, or normal prompts.
182
+
183
+ 1. Install and log in to Codex on the same machine. Start Codex in a named tmux
184
+ session for full REPL mode:
185
+
186
+ ```bash
187
+ tmux -L codex new -s codex
188
+ codex
189
+ ```
190
+
191
+ If you only want text-only one-shot mode without a visible Codex TUI, you can use
192
+ `codex-telegram-bridge setup --mode exec` instead.
193
+
194
+ 2. Create a Telegram bot with [@BotFather](https://t.me/BotFather) and copy the bot token.
195
+ Do not send this token in any Telegram chat. Paste it only into the local
196
+ terminal setup wizard in the next step.
197
+
198
+ 3. Run the setup wizard. If you used `pipx`, run:
199
+
200
+ ```bash
201
+ codex-telegram-bridge setup
202
+ ```
203
+
204
+ If you installed from a clone, run:
205
+
206
+ ```bash
207
+ git clone https://github.com/ssamssae/codex-telegram-bridge.git
208
+ cd codex-telegram-bridge
209
+ python3 bridge_setup.py setup
210
+ ```
211
+
212
+ The wizard will:
213
+
214
+ - validate the BotFather token with Telegram `getMe`
215
+ - ask you to send `/start` to the bot in Telegram
216
+ - detect your numeric `chat_id` automatically
217
+ - show what it is doing at each step
218
+ - write a private `~/.config/telegram-agent-bridge.env` with mode `0600`
219
+ - install `~/.local/bin/telegram-agent-bridge-run`
220
+ - install and start a user service with systemd on Linux/WSL or launchd on macOS
221
+ - install a watchdog timer/LaunchAgent that recovers an inactive bridge service
222
+ - send a setup-complete test message
223
+
224
+ Default setup mode is `repl`, which supports the visible Codex CLI transcript,
225
+ Telegram text, image prompts, video thumbnails/metadata, audio-file delivery,
226
+ generic file delivery, optional audio transcription, answer mirroring, Telegram
227
+ `typing...`, and Codex approval prompts. It also stores a JSONL cursor so a
228
+ daemon restart can resume watching the current Codex session and backfill the
229
+ latest eligible final answer that was produced while the bridge was down. If a
230
+ final answer contains a local image, video, or audio path or markdown link to an
231
+ allowed media file, the bridge hides that local path from the Telegram text and
232
+ sends the actual media attachment.
233
+
234
+ 4. Check the installation:
235
+
236
+ ```bash
237
+ codex-telegram-bridge doctor
238
+ # or, from a clone:
239
+ python3 bridge_setup.py doctor
240
+ ```
241
+
242
+ 5. Send `/ping` to the bot. Then send a normal prompt.
243
+
244
+ Token safety rule: BotFather shows the token in Telegram, but you should copy it
245
+ from BotFather and paste it into the local setup wizard in your terminal. In
246
+ Telegram, send only `/start` or normal prompts to your bot.
247
+
248
+ For local terminal input without scraping the Codex TUI, either type into the
249
+ foreground bridge process or write one prompt per line to the FIFO:
250
+
251
+ ```bash
252
+ printf '%s\n' 'continue from the terminal' > ~/.local/state/telegram-agent-bridge/input.fifo
253
+ ```
254
+
255
+ Prompts and final answers are mirrored to both Telegram and terminal output.
256
+
257
+ ## BYO Signal Contract
258
+
259
+ Scripts that push work into a live agent usually contain local SSH aliases,
260
+ node names, chat ids, token paths, and tmux assumptions. This project only needs
261
+ a small local input contract: write one UTF-8 line to the configured signal
262
+ FIFO.
263
+
264
+ Enable a signal FIFO for `repl` mode:
265
+
266
+ ```bash
267
+ export CRB_SIGNAL_PATH="$HOME/.local/state/telegram-agent-bridge/input.fifo"
268
+ codex-telegram-bridge
269
+ ```
270
+
271
+ The bridge creates the FIFO if it does not already exist. Any local process that
272
+ can write to this path can inject a prompt into Codex, so keep it under your
273
+ private state directory and do not expose it through a network share.
274
+
275
+ Signal payloads can be plain text:
276
+
277
+ ```bash
278
+ printf '%s\n' 'review the latest failing test' > "$CRB_SIGNAL_PATH"
279
+ ```
280
+
281
+ Or one JSON object per line with `prompt`, `text`, or `message`:
282
+
283
+ ```bash
284
+ printf '%s\n' '{"prompt":"run the smoke checks and summarize failures"}' > "$CRB_SIGNAL_PATH"
285
+ ```
286
+
287
+ A minimal generic trigger wrapper is included:
288
+
289
+ ```bash
290
+ examples/triggers/byo-signal-submit.sh "summarize the current git diff"
291
+ ```
292
+
293
+ Use this as the boundary for your own cron job, webhook receiver, task queue, or
294
+ multi-node orchestrator. Keep site-specific dispatch logic outside this repo;
295
+ the public bridge only owns the local signal contract and Codex/Telegram flow.
296
+
297
+ ## Setup Commands
298
+
299
+ Interactive install:
300
+
301
+ ```bash
302
+ codex-telegram-bridge setup
303
+ # or:
304
+ python3 bridge_setup.py setup
305
+ ```
306
+
307
+ Text-only one-shot install:
308
+
309
+ ```bash
310
+ codex-telegram-bridge setup --mode exec
311
+ # or:
312
+ python3 bridge_setup.py setup --mode exec
313
+ ```
314
+
315
+ Install optional local audio transcription dependencies for voice/audio files:
316
+
317
+ ```bash
318
+ codex-telegram-bridge setup --install-asr
319
+ # or:
320
+ python3 bridge_setup.py setup --install-asr
321
+ ```
322
+
323
+ Non-interactive install, useful for scripts:
324
+
325
+ ```bash
326
+ codex-telegram-bridge setup \
327
+ --token '123456:BOT_TOKEN' \
328
+ --chat-id '123456789' \
329
+ --non-interactive \
330
+ -y
331
+
332
+ # or, from a clone:
333
+ python3 bridge_setup.py setup \
334
+ --token '123456:BOT_TOKEN' \
335
+ --chat-id '123456789' \
336
+ --non-interactive \
337
+ -y
338
+ ```
339
+
340
+ Health check:
341
+
342
+ ```bash
343
+ codex-telegram-bridge doctor
344
+ # or:
345
+ python3 bridge_setup.py doctor
346
+ ```
347
+
348
+ Uninstall the service and runner while keeping your private config:
349
+
350
+ ```bash
351
+ codex-telegram-bridge uninstall
352
+ # or:
353
+ python3 bridge_setup.py uninstall
354
+ ```
355
+
356
+ Remove the private config too:
357
+
358
+ ```bash
359
+ codex-telegram-bridge uninstall --purge
360
+ # or:
361
+ python3 bridge_setup.py uninstall --purge
362
+ ```
363
+
364
+ ## Manual Setup
365
+
366
+ If you do not want the setup wizard to install a service, create a private env
367
+ file manually:
368
+
369
+ ```bash
370
+ cp config.example.env ~/.config/telegram-agent-bridge.env
371
+ chmod 600 ~/.config/telegram-agent-bridge.env
372
+ $EDITOR ~/.config/telegram-agent-bridge.env
373
+ ```
374
+
375
+ Run the daemon in the foreground:
376
+
377
+ ```bash
378
+ set -a
379
+ . ~/.config/telegram-agent-bridge.env
380
+ set +a
381
+ python3 telegram_agent_bridge.py
382
+ ```
383
+
384
+ ## Configuration
385
+
386
+ Required settings are intentionally small and explicit.
387
+
388
+ | Variable | Required | Default | Description |
389
+ | --- | --- | --- | --- |
390
+ | `TAB_BRIDGE_MODE` | no | `repl` | `repl` for visible Codex CLI sync, or `exec` for text-only one-shot mode. |
391
+ | `TAB_BOT_TOKEN` | yes | none | Telegram bot token from BotFather. Keep it secret. |
392
+ | `TAB_CHAT_ID` | yes | none | The only Telegram chat id allowed to control Codex. Other chats are ignored. |
393
+ | `TAB_AGENT` | no | `codex` | Compatibility setting. Only `codex` is supported; other values are rejected. |
394
+ | `TAB_AGENT_CMD` | no | `codex` | Codex command or wrapper command. Split like shell arguments. |
395
+ | `TAB_STATE_DIR` | no | `~/.local/state/telegram-agent-bridge` | Offset and thread-id state directory. |
396
+ | `TAB_PREFIX` | no | empty | Prefix shown on the first Telegram reply chunk, for example an emoji or node label. |
397
+ | `TAB_PREFIX_LINE` | no | `0` | When `1`, puts `TAB_PREFIX` on its own first line. |
398
+ | `TAB_WORKDIR` | no | `~` | Working directory for the Codex process. Codex also receives `-C TAB_WORKDIR`. |
399
+ | `TAB_WORKDIR_LOCK` | no | `1` | Acquire a local workdir lock around one-shot Codex turns. |
400
+ | `TAB_TIMEOUT` | no | `600` | Per-turn timeout in seconds. |
401
+ | `TAB_TG_CHUNK` | no | `4096` | Telegram message chunk size. |
402
+ | `TAB_TYPING_INTERVAL` | no | `4` | Seconds between repeated Telegram `typing` actions while Codex is running. |
403
+ | `CRB_SIGNAL_PATH` | no | `TAB_LOCAL_INPUT` when set | FIFO path for external/local signal prompts in `repl` mode. Set to `0`/`off` to disable. |
404
+ | `TAB_LOCAL_INPUT` | no | `~/.local/state/telegram-agent-bridge/input.fifo` on POSIX | Compatibility FIFO path for local terminal prompts and `CRB_SIGNAL_PATH` fallback. Set to `0`/`off` to disable. |
405
+ | `TAB_STDIN_INPUT` | no | auto | Read local prompts from stdin. Defaults to on only when stdin is a TTY. |
406
+ | `TAB_CODEX_DANGEROUS_BYPASS` | no | `0` | When `1`, adds `--dangerously-bypass-approvals-and-sandbox` to Codex. |
407
+ | `TAB_CODEX_EXTRA_ARGS` | no | empty | Extra arguments inserted after `codex exec --json -o <tmp>`. |
408
+ | `CRB_TMUX_SOCKET` | repl only | `codex` | tmux socket for the visible Codex TUI. |
409
+ | `CRB_TMUX_SESSION` | repl only | `codex` | tmux session or target for the visible Codex TUI. |
410
+ | `CRB_TMUX_SUBMIT_KEY` | repl only | `Tab` | key sent after pasting Telegram prompts into Codex. |
411
+ | `CRB_TYPING_MAX_SECONDS` | no | `7200` | Maximum lifetime for repeated Telegram `typing` actions during one visible Codex turn. |
412
+ | `CRB_TELEGRAM_FALLBACK_SECONDS` | no | `90` | One-shot fallback progress reply delay for Telegram-origin REPL prompts when `final_answer` is delayed. Set `0` to disable. |
413
+ | `CRB_FLOW_MIRROR` | no | `1` | Mirror public Codex progress/commentary steps to Telegram with the `⚙️ 작업 흐름` header. |
414
+ | `CRB_REASONING_MIRROR` | no | `1` | Mirror Codex's public reasoning summary to Telegram with the `🧠 코덱스 사고` header, sent right after the final answer. Only the runtime-public summary is sent (never raw chain-of-thought); copy-payload replies do not emit a reasoning mirror. Set `0` to disable. |
415
+ | `CRB_LONG_RUNNING_PROGRESS_SECONDS` | no | `0` | Legacy periodic progress interval for long-running Telegram-origin REPL prompts. The flow mirror replaces it by default; set a positive second value to re-enable. |
416
+ | `CRB_AUDIO_TRANSCRIBE_CMD` | no | empty | Optional command template for audio transcription. Use `{path}` for the media file. |
417
+ | `CRB_APPROVAL_TTL_SECONDS` | no | `300` | Seconds before a Telegram approval button is treated as stale. |
418
+ | `CRB_STATE_PATH` | no | `TAB_STATE_DIR/codex-repl-bridge-<node>.state.json` | Persistent JSONL cursor and final-answer dedup state for `repl` mode. |
419
+ | `CRB_BACKFILL` | no | `1` | When enabled, startup without a valid cursor tail-scans the current session for a fresh missed final answer. |
420
+ | `CRB_BACKFILL_MAX` | no | `1` | Maximum missed final answers to backfill on startup. Clamped to `1`-`3`. |
421
+ | `CRB_BACKFILL_WINDOW_SEC` | no | `600` | Maximum age for startup backfill candidates. |
422
+ | `CRB_TAIL_SCAN_BYTES` | no | `65536` | Bytes to scan from the end of the Codex JSONL session when cursorless backfill is needed. |
423
+ | `CRB_STATE_RING_CAP` | no | `64` | Number of mirrored final-answer keys retained for deduplication. |
424
+ | `CRB_KILL` | no | `0` | Emergency switch that blocks Telegram answer sends while keeping the process alive. |
425
+ | `CRB_ATTACHMENT_ROOTS` | no | state dir, workdir, `/tmp` | `:`-separated roots where answer-referenced local media files may be uploaded from. |
426
+ | `CRB_MAX_ATTACHMENT_BYTES` | no | `52428800` | Maximum size for local answer attachments. |
427
+
428
+ ## REPL Mode Media Support
429
+
430
+ `TAB_BRIDGE_MODE=repl` supports:
431
+
432
+ - text messages
433
+ - Telegram photos and image documents
434
+ - Telegram videos, video notes, animations, and video documents
435
+ - Telegram voice/audio files
436
+ - generic Telegram document files
437
+ - Telegram `typing...` while Codex is generating
438
+ - terminal-origin Codex prompts mirrored back to Telegram
439
+ - Codex command approval prompts mirrored to Telegram with buttons
440
+ - Codex numbered/lettered selection prompts and y/n confirmations mirrored to
441
+ Telegram with buttons
442
+ - local image/video/audio paths in final answers hidden from Telegram text and
443
+ sent as Telegram attachments
444
+
445
+ Images are saved under `TAB_STATE_DIR` and passed to Codex as local paths in the
446
+ prompt. Video messages include the local video path, Telegram thumbnail when
447
+ available, and metadata. If `ffmpeg` is available, the bridge can extract video
448
+ frames. Audio messages include the local audio path and, when
449
+ `CRB_AUDIO_TRANSCRIBE_CMD` is configured, a transcript. Generic document files
450
+ are saved under the same local media directory and passed to Codex with
451
+ `local_path`, caption, MIME type, file name, and file size metadata.
452
+
453
+ The setup wizard can install a local `faster-whisper` transcription environment:
454
+
455
+ ```bash
456
+ python3 bridge_setup.py setup --install-asr
457
+ ```
458
+
459
+ This is optional because it downloads Python packages and Whisper model files.
460
+
461
+ ## REPL Mode Restart Backfill
462
+
463
+ In `repl` mode the bridge stores a JSONL cursor and a small final-answer dedup
464
+ ring in `CRB_STATE_PATH`. On restart, if that cursor still matches the current
465
+ Codex session file, the bridge resumes reading from the saved offset.
466
+
467
+ If there is no valid cursor and `CRB_START_AT_END=1`, the bridge scans the tail
468
+ of the current Codex JSONL session, finds the latest user event, and backfills
469
+ up to `CRB_BACKFILL_MAX` fresh final answers after that user event. Candidates
470
+ must be inside `CRB_BACKFILL_WINDOW_SEC` and absent from the dedup ring. This
471
+ reduces the common failure mode where Codex finished while the Telegram bridge
472
+ service was restarting.
473
+
474
+ ## Service Watchdog
475
+
476
+ When the setup wizard installs a background service, it also installs a small
477
+ watchdog.
478
+
479
+ On Linux and WSL, the wizard writes:
480
+
481
+ - `~/.config/systemd/user/telegram-agent-bridge-watchdog.service`
482
+ - `~/.config/systemd/user/telegram-agent-bridge-watchdog.timer`
483
+
484
+ The timer runs every 60 seconds. If `telegram-agent-bridge.service` is inactive,
485
+ the watchdog starts it and writes a status file at
486
+ `~/.local/state/telegram-agent-bridge/watchdog.status`.
487
+
488
+ On macOS, the wizard writes:
489
+
490
+ - `~/Library/LaunchAgents/com.user.telegram-agent-bridge-watchdog.plist`
491
+
492
+ The LaunchAgent runs every 60 seconds. If
493
+ `com.user.telegram-agent-bridge` is not running, the watchdog kickstarts it and
494
+ writes the same status file.
495
+
496
+ This covers the failure mode where the bridge was explicitly stopped or left
497
+ inactive. The normal `Restart=always` and launchd `KeepAlive` settings still
498
+ handle ordinary crashes.
499
+
500
+ ## REPL Mode Approval Prompts
501
+
502
+ When Codex is not running in a bypass/YOLO approval mode, it may pause inside the
503
+ terminal with a prompt such as:
504
+
505
+ ```text
506
+ Would you like to run the following command?
507
+ 1. Yes, proceed (y)
508
+ 2. Yes, and don't ask again... (p)
509
+ 3. No, and tell Codex what to do differently (esc)
510
+ ```
511
+
512
+ In `repl` mode the bridge watches the tmux pane for that prompt and sends a
513
+ Telegram message with buttons:
514
+
515
+ - `1. Yes`
516
+ - `2. Yes, don't ask again`
517
+ - `3. No`
518
+
519
+ You can also reply with the visible number or shortcut, for example `1`, `2`,
520
+ `y`, `p`, `esc`, or `/approve 1`. The bridge injects the matching key back into
521
+ the Codex TUI, edits the original Telegram approval message to show the selected
522
+ button, and removes stale choices.
523
+
524
+ ## REPL Mode Selection Prompts
525
+
526
+ The bridge also watches for non-approval Codex TUI choices, for example model
527
+ pickers, mode pickers, numbered/lettered menus, and inline confirmations such as
528
+ `[y/N]` or `(yes/no)`. When a selection prompt is visible, Telegram receives
529
+ buttons for the visible options.
530
+
531
+ If a visible option has a shortcut such as `(y)`, `(n)`, `(esc)`, or `(enter)`,
532
+ the bridge sends that shortcut back to Codex. If there is no explicit shortcut,
533
+ it uses the currently highlighted `›` row and sends Up/Down plus Enter. Text
534
+ replies such as `1`, `2`, `a`, `y`, `yes`, `n`, `no`, and `/choose 2` are also
535
+ accepted while the prompt is active.
536
+
537
+ ## Answer Media Attachments
538
+
539
+ If Codex answers with a local image, video, or audio path, the bridge removes
540
+ that local path from the Telegram text and uploads the actual media. This works
541
+ for raw paths and markdown links such as:
542
+
543
+ ```text
544
+ Here is the screenshot: [screenshot.png](/path/to/project/screenshot.png)
545
+ Here is the clip: /path/to/project/demo.mp4
546
+ Here is the audio: [voice.oga](/path/to/project/voice.oga)
547
+ ```
548
+
549
+ The bridge uses Telegram's media-specific upload methods when possible:
550
+ `sendPhoto`, `sendVideo`, `sendVoice`, and `sendAudio`; unsupported media falls
551
+ back to `sendDocument`.
552
+
553
+ For safety, only media files under `CRB_ATTACHMENT_ROOTS` are uploaded and
554
+ hidden from the Telegram text. By default those roots are the bridge state
555
+ directory, `TAB_WORKDIR`, and `/tmp`. Large files are skipped according to
556
+ `CRB_MAX_ATTACHMENT_BYTES`.
557
+
558
+ ## Codex Execution
559
+
560
+ It runs:
561
+
562
+ ```text
563
+ codex exec --json -o <tmp-answer-file> -C <TAB_WORKDIR> <prompt>
564
+ codex exec --json -o <tmp-answer-file> -C <TAB_WORKDIR> resume <thread_id> <prompt>
565
+ ```
566
+
567
+ The bridge stores the first `thread.started.thread_id` and resumes it on later messages. If a stored thread id is stale, the bridge clears it and retries once as a fresh Codex thread.
568
+
569
+ ## Service Examples
570
+
571
+ Examples live in:
572
+
573
+ - `examples/systemd/telegram-agent-bridge.service`
574
+ - `examples/systemd/telegram-agent-bridge-watchdog.service`
575
+ - `examples/systemd/telegram-agent-bridge-watchdog.timer`
576
+ - `examples/launchd/com.user.telegram-agent-bridge.plist`
577
+ - `examples/launchd/com.user.telegram-agent-bridge-watchdog.plist`
578
+
579
+ Review the `PATH` in each file. Services often start with a smaller environment than your shell, so include the directory where `codex` is installed.
580
+
581
+ ## Roadmap
582
+
583
+ These are product directions, not promises in the current release:
584
+
585
+ - queue controls for safe queueing, interruption, and side tasks
586
+ - inline Telegram settings for mode and delivery preferences
587
+ - richer Markdown fallback when Telegram rejects formatted messages
588
+ - optional multi-user and topic allowlists for small private groups
589
+ - richer service supervision dashboards and remote health summaries
590
+ - idle cleanup and maintenance commands for long-running bridge installs
591
+
592
+ ## Security Notes
593
+
594
+ - Treat `TAB_BOT_TOKEN` like a password. Do not commit it, paste it into logs, or share it.
595
+ - Do not paste the BotFather token into your Telegram bot chat. Paste it only
596
+ into the local terminal setup wizard. The only Telegram message needed during
597
+ setup is `/start`.
598
+ - Keep `TAB_CHAT_ID` set to the intended chat id. This single-user allowlist is the main safety boundary.
599
+ - Run this only on a trusted personal machine or trusted server. Telegram messages become Codex prompts.
600
+ - Protect `CRB_SIGNAL_PATH`/`TAB_LOCAL_INPUT`. Anyone who can write to the FIFO can send prompts to Codex.
601
+ - Be careful with `TAB_CODEX_DANGEROUS_BYPASS=1`. It adds `--dangerously-bypass-approvals-and-sandbox`, allowing Codex to act without normal approval and sandbox protections.
602
+ - Prefer a limited working directory in `TAB_WORKDIR` when possible.
603
+ - This daemon uses Telegram polling, not a public inbound webhook. You do not need to expose a local port.
604
+
605
+ ## Advanced Settings
606
+
607
+ Beyond the keys documented above, the bridges read further tuning knobs
608
+ (direct `CRB_BOT_TOKEN`/`CRB_CHAT_ID` overrides, `CRB_TOKEN_FILE`, media
609
+ helper binaries, generated-image autosend, watchdog probe tuning, and the
610
+ exec-backend tmux fallbacks). Every key ships with a safe default; the full
611
+ annotated list lives at the bottom of `config.example.env`.
612
+
613
+ ## Development
614
+
615
+ The core runtime uses only the Python standard library. The optional `asr`
616
+ extra installs local audio transcription dependencies.
617
+
618
+ ```bash
619
+ python3 -m unittest discover -s tests
620
+ ```