threadBridge is a workspace-first Codex runtime with a macOS desktop owner, a Telegram adapter, and a local browser management surface.
The current supported product shape is:
- a macOS desktop runtime owner started via
threadbridge_desktop - a local management API and browser UI
- a Telegram bot adapter for workspace threads
- real bound workspaces with a managed
.threadbridge/runtime surface - shared workspace
codex app-serverdaemons plus a managed localhcodexentrypoint
If docs and implementation differ, treat the code as authoritative. The maintainer plan registry lives in docs/plan/README.md.
The current codebase already ships these major pieces:
- desktop-first runtime ownership and reconcile flow
- one managed Telegram workspace thread per real workspace
- shared workspace runtime control for workspace ensure, session bind/new/repair, and Telegram-to-TUI routing
- app-server observer projection plus adapter-owned Telegram interaction bridging
- Telegram text and image turns that resume the saved Codex session
- Telegram collaboration mode switching between
defaultandplan - Telegram question flow for
Questionsprompts plus the post-planImplement this plan?callback - preview drafts via
sendMessageDraft, with HTML-first delivery and plain-text fallback - final replies with HTML-first delivery, plain-text fallback, and Markdown attachment fallback for oversized inline responses
- local/browser management UI for setup, launch, reconnect, archive/restore, runtime repair, and transcript inspection
- workspace-scoped execution modes:
full_autoandyolo
Still tracked as design or follow-up work:
- deeper runtime and adapter abstraction beyond Telegram
- richer delivery queue and status-control semantics
- broader observability beyond the current local management surface
For slash-command details, see the maintainer reference in docs/telegram-slash-commands.md.
The current runtime is organized like this:
threadbridge_desktopis the formal runtime owner.- The desktop process hosts the local management API, tray menu, and runtime-owner reconcile loop.
- Shared runtime control handles workspace runtime ensure, session bind/new/repair, and Telegram-to-live-TUI routing.
- App-server observer owns transcript and process projection; Telegram interaction UI is bridged separately.
- Telegram is an adapter on top of the runtime, not the runtime owner.
- Each managed workspace is a real local directory, not a projected copy under the bot-local data root.
- Each workspace gets a managed
.threadbridge/surface plus an appended runtime block inAGENTS.md. - Codex session continuity is stored in bot-local metadata under the runtime data root, for example
data/<thread-key>/session-binding.jsonin debug builds.
The supported startup path is desktop-first. Any non-desktop compatibility paths are internal support surfaces, not the intended operating model.
Execution mode is a workspace-scoped runtime setting stored in ./.threadbridge/state/workspace-config.json.
- default mode:
full_auto - optional mode:
yolo
Current semantics are aligned to Codex:
full_auto=>approvalPolicy=on-request+sandbox=workspace-writeyolo=>approvalPolicy=never+sandbox=danger-full-access
This setting is sticky per workspace:
- fresh sessions start with the workspace mode
hcodexnew-session and resume commands use the workspace mode- Telegram turns re-assert the workspace mode on resume
- if the workspace mode changes, the next turn or resume converges the current session to that mode
- macOS for the supported desktop runtime path
- Rust toolchain
- Python 3
codexCLI installed and authenticated on the machine- a Telegram bot token from BotFather
- Telegram topics enabled if you want workspace-thread workflows in private chat
- Copy
.env.exampletodata/config.env.local. - Fill in at least:
TELEGRAM_BOT_TOKENAUTHORIZED_TELEGRAM_USER_IDS
- Start the desktop runtime:
export CARGO_HOME="$PWD/.cargo" CARGO_TARGET_DIR="$PWD/target"
cargo run --bin threadbridge_desktopThe desktop runtime can also start without Telegram credentials. In that state, the tray and local management UI still work, and you can finish setup before polling is active.
Bot-local runtime state defaults by build profile:
- debug builds use repo-local
./data - release builds use the platform local app-data directory
- on macOS, the bundled release data root is
~/Library/Application Support/threadBridge/data - bundled releases install runtime support under
~/Library/Application Support/threadBridge/runtime_support DATA_ROOTandDEBUG_LOG_PATHcan override either mode explicitly
Default local management address:
http://127.0.0.1:38420
Override it with THREADBRIDGE_MANAGEMENT_BIND_ADDR.
Use the repo-local Cargo paths:
export CARGO_HOME="$PWD/.cargo" CARGO_TARGET_DIR="$PWD/target"
cargo check
cargo test
cargo fmt
cargo clippy --all-targets --all-featuresSupported desktop runtime entrypoint:
cargo run --bin threadbridge_desktopscripts/local_threadbridge.sh is the supported macOS helper for day-to-day local development. It builds the latest code, bundles the app locally, and restarts the desktop runtime quickly.
Typical usage:
scripts/local_threadbridge.sh build
scripts/local_threadbridge.sh bundle
scripts/local_threadbridge.sh start
scripts/local_threadbridge.sh restart
scripts/local_threadbridge.sh status
scripts/local_threadbridge.sh logsThe helper also controls which Codex binary the managed hcodex path should prefer:
scripts/local_threadbridge.sh build --codex-source brew
scripts/local_threadbridge.sh build --codex-source sourcebrew: prefer the systemcodexonPATHsource: buildcodex-clifrom a local Codex Rust workspace and cache it under.threadbridge/codex/codex
That preference is persisted in .threadbridge/codex/source.txt.
On macOS, build, bundle, and start refresh the app icon assets from the canonical source icon/EXPORT_mac_icon.png. This image is already the intended 1024x1024 macOS tile, so the icon pipeline does not apply an extra zoom or rounded-mask step.
start launches the bundled app executable from threadBridge.app, so the running desktop process inherits the bundle icon instead of the generic bare-binary icon. The bundled desktop runtime is also marked as a menubar-only app, so normal operation stays out of the Dock.
Use scripts/release_threadbridge.sh for the public macOS release pipeline. This is separate from the local dev helper and is the supported path for signed, notarized, distributable artifacts.
Typical subcommands:
scripts/release_threadbridge.sh build --version 0.1.0-rc.1
scripts/release_threadbridge.sh sign --version 0.1.0-rc.1 --codesign-identity "Developer ID Application: Example, Inc. (TEAMID)"
scripts/release_threadbridge.sh dmg --version 0.1.0-rc.1 --codesign-identity "Developer ID Application: Example, Inc. (TEAMID)"
scripts/release_threadbridge.sh notarize --version 0.1.0-rc.1 --codesign-identity "Developer ID Application: Example, Inc. (TEAMID)"
scripts/release_threadbridge.sh release --version 0.1.0-rc.1 --notes-file docs/releases/0.1.0-rc.1.md --codesign-identity "Developer ID Application: Example, Inc. (TEAMID)"The release command performs the full build -> sign -> DMG -> notarize -> publish pipeline.
Artifacts are written to dist/release/<version>/.
Current pipeline contract:
- builds a universal macOS app bundle for
arm64andx86_64 - copies
app_server_ws_workerinto the bundled app so the distributed runtime can launch its workspace worker - signs the app with hardened runtime
- creates a single canonical DMG and checksum
- submits that DMG with
notarytool, then staples and validates it - publishes the notarized DMG and checksum to a GitHub draft prerelease
The release script is macOS-only and fails fast if the worktree is dirty or required CLIs are missing.
The intended user flow is:
- Start
threadbridge_desktop. - Open the local management UI or use the tray.
- Send
/startto the bot in your private Telegram chat so the control chat exists. - Add a workspace:
- from Telegram:
/add_workspace <absolute-path> - or from the local management UI or tray folder picker
- from Telegram:
- threadBridge binds that real workspace, installs
.threadbridge/, and starts a fresh Codex session. - Continue using either:
- the Telegram workspace thread
- or local
./.threadbridge/bin/hcodex
The maintained slash-command reference lives in docs/telegram-slash-commands.md.
Current command groups:
- control chat:
/start,/add_workspace <absolute-path>,/restore_workspace - workspace thread:
/start,/start_fresh_session,/repair_session_binding,/workspace_info,/rename_workspace,/archive_workspace,/launch_local_session,/get_workspace_execution_mode,/set_workspace_execution_mode,/sessions,/session_log <session_id>,/stop,/plan_mode,/default_mode
Operationally:
- the main private chat is the control console
- each managed workspace gets its own Telegram topic or thread
- normal messages in a workspace thread continue the saved Codex session
- option-based questions render inline buttons plus
Other; freeform questions use the next text message in the same thread - plan-mode turns can end with an
Implement this plan?inline prompt that continues on the same saved session
After a workspace is bound, use the managed local TUI path:
./.threadbridge/bin/hcodexResume a specific session with:
./.threadbridge/bin/hcodex resume <session-id>Important current behavior:
hcodexdepends on the desktop runtime ownerhcodexno longer self-heals missing workspace runtime statehcodexlaunch and resume commands follow the workspace execution mode- raw
codexlaunches that bypasshcodexare outside the managed path
Bot-local state lives under the runtime data root:
- debug builds default this root to
data/ - release builds default this root to the platform local app-data directory
- debug mode stores
data/main-thread/for the control console - debug mode also stores
data/<thread-key>/for metadata, transcripts, session binding, and image-state artifacts
Workspace-local managed runtime surface:
AGENTS.mdmanaged appendix block.threadbridge/bin/build_prompt_config.threadbridge/bin/generate_image.threadbridge/bin/hcodex.threadbridge/bin/send_telegram_media.threadbridge/state/workspace-config.json.threadbridge/state/app-server/current.json.threadbridge/state/runtime-observer/current.json.threadbridge/state/runtime-observer/events.jsonl.threadbridge/tool_requests/.threadbridge/tool_results/
The real workspace is authoritative for project files. The bot-local runtime data root is threadBridge state, not a projected copy of the repo.
- plan registry and design references: docs/plan/README.md
- maintainer guide: AGENTS.md
- workspace runtime appendix source: runtime_support/templates/AGENTS.md
- slash-command reference: docs/telegram-slash-commands.md
- release notes index: docs/releases/README.md
The plan directory contains a mix of landed work, partial work, and pure drafts. Do not assume every plan document describes current behavior.
- Keep secrets in
data/config.env.local. - Do not commit repo-local
data/, logs, generated images, or provider payloads unless they are intentional fixtures. - Bot-local state and workspace-local runtime artifacts may contain prompts, transcripts, image references, and provider metadata.
- app icon source: icon/EXPORT_mac_icon.png
- bundled app icon: rust/static/app_icon/threadBridge.icns