npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

conductor-telegram

v0.12.1

Published

Telegram bot for remote oversight of Conductor workspaces. Built by Belong.net.

Readme

conductor-telegram

Remote oversight for Conductor workspaces via Telegram. Run AI agents, approve decisions, and monitor progress from your phone.

Built by Belong.net

For an independently hosted gateway using only Conductor's native API, use the OVH cloud-only deployment and migration guide. Existing installations continue to default to hybrid mode.

Quickstart

npm i -g conductor-telegram
conductor-telegram setup
conductor-telegram

That's it. The setup wizard walks you through Telegram bot creation, configuration, and MCP plugin installation.

How it works

┌──────────────┐     ┌─────────────────┐     ┌─────────────────────┐
│   Telegram   │◄───►│  conductor-     │◄───►│  Local Conductor    │
│   (you)      │     │  telegram bot   │     │  workspaces/agents  │
└──────────────┘     └──────┬────┬─────┘     └──────────┬──────────┘
                            │    │                      │
              official API │    │   ┌──────────────┐   │
                            │    └──►│ SQLite (WAL) │◄──┘
                            ▼        └──────────────┘
                    Conductor Cloud

The bot polls local Conductor sessions every 5 seconds and Cloud sessions every 15 seconds, forwarding agent messages to Telegram. When an agent uses the MCP server to ask a question, the bot surfaces it as an interactive Telegram message with buttons or free-form reply.

In cloud-only mode, agent replies, questions, progress reports, and artifact links render Markdown as Telegram rich text, including bold, italics, strikethrough, inline code, code blocks, and clickable web links. Long replies keep their formatting across messages; answer buttons appear on the last message of a long question. Local file links remain readable as a label and path, and formatting that cannot be rendered safely falls back to literal text.

Each message that starts, continues, or controls Cloud work gets one status card. The acknowledgement arrives silently and is edited in place as the task is handed to the agent and when it finishes. Failures update the card to Not done: …; a failure reported more than 15 seconds after the acknowledgement also sends a notification. Agents in workspaces the gateway created are told about the Telegram MCP tools; agents in workspaces discovered from Conductor are asked to answer inline instead.

Architecture

src/
├── cli/               # CLI entry points
│   ├── index.ts       # Command parser and dispatcher
│   ├── setup.ts       # Interactive configuration wizard
│   ├── config.ts      # Config loading (flags > env > config.json > defaults)
│   ├── doctor.ts      # System validation and diagnostics
│   └── install-plugin.ts  # MCP plugin installer
├── bot/               # Telegram bot
│   ├── index.ts       # Bot init, polling loops, message forwarding
│   ├── commands.ts    # All command and callback handlers
│   ├── launcher.ts    # Agent spawning and session management
│   ├── polling-policy.ts  # Cloud recovery scheduling and notice publication
│   ├── middleware.ts  # Authentication guard
│   ├── format.ts      # Markdown→HTML, styled buttons, escaping
│   ├── forum.ts       # Forum topic lifecycle
│   └── callback-server.ts  # Webhook/callback handling
├── cloud/             # Cloud-only gateway (TELEGRAM_RUNTIME_MODE=cloud-only)
│   ├── runtime.ts     # Gateway startup, lease, and worker wiring
│   ├── commands.ts    # Commands, repo topic routing, and callbacks
│   ├── engine.ts      # Workspace launch, delivery, provider recovery
│   ├── catalog.ts     # Conductor project catalog lookups
│   ├── sync.ts        # Discovery of Cloud workspaces created elsewhere
│   ├── poller.ts      # Independent session pollers
│   ├── telegram.ts    # Durable outbound Telegram queue and sender
│   ├── bridge.ts      # Scoped HTTPS file/MCP bridge and health probes
│   └── store.ts       # Additive gateway queue and state tables
├── mcp/               # MCP server (runs inside workspaces)
│   └── server.ts      # report_status, report_artifact, request_human
├── store/             # Database layer
│   ├── db.ts          # SQLite init, schema, migrations
│   └── queries.ts     # CRUD operations
├── integrations/
│   └── conductor-api.ts  # Supported Conductor Cloud API transport
├── lanes/
│   ├── config.ts          # Optional lanes.json loading
│   ├── decide.ts          # Pure queue/nudge/create decisions
│   ├── pipeline.ts        # Review, finals, merge, validation, and hygiene
│   ├── scheduler.ts       # Backwards-compatible in-process scheduler
│   ├── manifest.ts        # Strict Manifest v2 loader and prompt hashing
│   ├── controller.ts      # Fenced deterministic delivery controller
│   ├── state-store*.ts    # Command Center HTTP + explicit test SQLite seams
│   └── worker.ts          # Independent Mac/OVH lease worker
└── types/
    └── index.ts       # TypeScript interfaces

Telegram bot commands

| Command | Usage | Description | |---------|-------|-------------| | /setup | /setup | Check setup diagnostics and apply current chat | | /run | /run <repo> <prompt> | Start a Cloud-first workspace with a local fallback | | /cloud | /cloud <project> <prompt> | Start a ☁️ Conductor Cloud workspace via the API (no local checkout needed) | | /projects | /projects [name] | List cloud projects, or one project's recent workspaces | | /link | /link [project] (inside a repo topic, cloud-only mode) | Show or change the Conductor project a repo topic routes to | | /fleet | /fleet [hours] | Org-wide cloud activity report from transcript search (default 24h, max 168) | | /lanes | /lanes [pause\|resume\|retry\|provider-disable\|archive-approval\|shadow\|cutover\|rollback] | Durable lane status and audited controls when Manifest v2 is configured; legacy scheduler controls otherwise | | /rename | /rename <name> (inside a topic or as a reply) | Rename the current cloud workspace via the API | | /renamethread | /renamethread <name> (inside a topic or as a reply) | Rename the current cloud thread via the API | | /review | /review <workspace> [instructions] (hybrid mode) | Launch a local code review session | | /review | /review [PR number or URL] (inside a topic, cloud-only mode) | With native reviews enabled, review that workspace's pull request. Bare /review finds the PR; /review 500 and a GitHub URL also work | | /send | /send <workspace> <message> | Send a follow-up message to a running agent | | /threads | /threads [workspace] | List Conductor threads, switch the default thread, or start a new thread | | /skills | /skills [workspace] | List built-in gstack skills plus workspace skills parsed from CLAUDE.md or AGENTS.md | | /skill | /skill <workspace> <name> [instructions] | Invoke a specific workspace skill | | /gstack | /gstack <workspace> [instructions] | Use GStack skills (ship, qa, browse, etc.) | | /ship, /qa, /investigate, /retro, /health, /checkpoint, /document_release, /land_and_deploy, /office_hours, /design_review | /ship [instructions] (reply or use inside a topic) | Shortcuts for well-known gstack skills, registered in Telegram's slash menu. /land and /document are short spellings of the last two | | /workspaces | /workspaces | List all tracked workspaces | | /prs, /ship_status | /prs | Show PR, check, merge, and stale-branch status for tracked workspaces | | /decisions | /decisions | Show unanswered agent questions for this chat | | /status | /status | Show active workspace summary | | /ping | /ping | Bot liveness check (uptime, heartbeat, version) | | /stop | /stop <name> | Stop a running workspace | | /repos | /repos | List available repositories (tap to select) | | /help | /help | Show help message |

Ways to target work from Telegram:

  1. Reply to any forwarded workspace message with text, media, /send, /review, /skills, /skill, /gstack, or any skill shortcut. If that message came from a specific Conductor thread, the reply goes back to that exact thread.
  2. Send inside the workspace's forum topic — skill shortcuts and /skill / /gstack pick up the topic's workspace automatically. Plain messages go to the workspace's active Conductor thread.
  3. Send inside a repo topic — in forum mode, tap Topic beside a repo in /repos to create a durable repo topic. Text, photos, screenshots, generic files, and voice notes sent there start a new workspace for that repo without guessing from the message. In cloud-only mode the topic routes itself: its repository name is matched against the Conductor project catalog by project name or by the repository name in each project's remote, and a single match is recorded so every later message in that topic goes straight there. No match or more than one match asks with a picker instead of guessing, and says plainly that the message was not sent. Every message that names a project names the repository it points at, so a wrong route is visible the first time it happens. Use /link to see or change where a topic routes. One topic is one workspace: the first task adopts the topic, and later messages continue that same workspace instead of opening another, so an album or a split paste reaches one workspace rather than one per message. /run <project> <task> rolls the topic onto new work. A workspace living in a repo topic never renames or closes it, so the topic keeps its repository's name.
  4. Hashtag a skill anywhere in a message (text or voice) — e.g. #ship fix the failing test or can you #qa this flow please. The bot rewrites the message into a skill-invocation prompt for the target workspace. Voice transcripts are scanned for hashtags too.

Conductor 0.72+ threads are mirrored into the same Telegram workspace topic. When a workspace has multiple visible Conductor sessions, forwarded messages include a 🧵 thread label. Use /threads in the topic to switch the active thread or start a new one.

In cloud-only mode, the bot can also discover Cloud workspaces created in Conductor or through another integration and attach each one to a Telegram forum topic. Set TELEGRAM_CLOUD_SYNC_CHAT_ID to the forum group ID. Existing transcript history is not replayed; the topic receives a connection note, the latest visible reply, and then new activity. Replies preserve the exact Conductor session and model. See the Cloud workspace sync specification for behavior, setup, migration, and the planned guided onboarding flow.

Conductor Cloud workspaces use Conductor's official API when CONDUCTOR_API_KEY is configured. Telegram can create cloud workspaces (/cloud), browse projects (/projects), send messages, create ordinary threads, poll transcripts/status, rename workspaces and threads, search org-wide transcripts (/fleet), cancel sessions, and archive workspaces without writing Conductor's private database. Without an API key, cloud workspaces remain observe-only through the desktop app's local mirror.

Cloud workspaces created with /cloud are driven entirely over the API — discovery, prompt delivery, and polling work even when the Conductor desktop app is closed or absent. Project arguments to /cloud and /projects accept a list number from /projects, a project id, an exact name, or a unique name prefix. When the bot itself runs inside a Conductor cloud workspace, it honors CONDUCTOR_API_URL and attributes its requests via an X-Conductor-Session-Id header taken from CONDUCTOR_SESSION_ID — both injected by the cloud workspace environment, not user config.

Repo-targeted Telegram launches are Cloud-first. /run, repo-topic messages, and AI-routed new tasks use Cloud automatically when CONDUCTOR_API_KEY is configured and exactly one Cloud project matches the local repository's origin URL (SSH and HTTPS forms are treated as the same repository). Missing or ambiguous origins always fall back locally; in that hybrid mode automatic routing never guesses from a project name or remote basename. The cloud-only gateway has no local checkout to read an origin from, so repo topics there route on the topic's own repository name instead, matched whole against project names and against the repository name in each project's remote, and ask whenever that is not unique. /run and /cloud name a project for a single task there: they adopt an unlinked topic and say so, and never re-point a topic that is already linked. /link is the one way to change it. The bot states when it falls back to a local workspace because Cloud is unconfigured, project lookup failed, no project matched, or Telegram attachments require the local file bridge. /cloud remains available when you want to choose a Cloud project explicitly.

If a local prompt later fails because its CLI login disappeared, the bot can take the same Telegram workspace over through Cloud. Automatic replay is limited to launcher-confirmed startup authentication failures before any assistant or tool activity. The worktree must be clean and its exact commit must already exist on an origin branch; the bot rechecks that branch after Cloud provisioning and before it sends work. Because the public Cloud API does not expose the provisioned checkout SHA, the handoff carries the expected SHA and tells the Cloud agent to verify HEAD before any side effect. Dirty files, unpushed commits, and partially executed prompts are never silently replayed. The Cloud binding and first prompt are persisted as a recoverable pending launch before delivery; later overlapping requests use a durable, ordered outbox with stable message identities. Stop intent and uncertain cleanup also survive restarts, so a canceled pending launch cannot be replayed later. A Stop or Archive the API rejects is retried across restarts, but only until it is clearly hopeless: because a pending terminal request blocks later sends, one that cannot succeed is retired with an explanatory message rather than gating the workspace forever. Restricted read-only reviews remain local until the public Cloud API exposes equivalent permission-policy enforcement.

Cloud commands act on your whole Conductor organization with the configured CONDUCTOR_API_KEY. In a group chat, set OWNER_USER_ID so only you can create (/cloud), rename, query (/projects, /fleet), or run the lanes scheduler (/lanes) — without it, every member of the configured group shares that privilege.

The official API is still beta. Cloud operations therefore use runtime response and resource-identity validation, bounded retries only for idempotent requests, throttled non-overlapping polls, and persisted message-ID cursors that are never mixed with desktop SQLite row IDs. Enforced review permission policies are not exposed by the API, so hybrid-mode cloud /review attempts fail closed. Cloud-only mode can opt into native reviews, which use normal Conductor permissions.

Photos, screenshots, voice notes, and audio files sent as replies are staged or transcribed for the agent. General-topic messages that the bot can only infer now ask for confirmation before starting or routing work.

Durable lanes controller (Manifest v2)

Manifest v2 is the production orchestration path. It is disabled by default and runs as an independent conductor-telegram lanes worker process, so Telegram polling failures cannot stop delivery. Runtime workspace/session IDs and PR URLs live only in Command Center/Postgres. The versioned manifest contains repositories, prompt paths and hashes, dependency milestones, provider policy, merge policy, and deterministic validation only.

The Mac worker is the preferred lease holder and the OVH service is a standby. A 75-second renewable fenced lease, 20-second heartbeat, 30-second active poll, 3-minute idle poll, and 15-minute full reconciliation ensure that only one worker mutates Conductor or a Git host. The returning Mac does not preempt a live OVH lease. Every external mutation is preceded by a deterministic durable action intent; messages, attestations, and notices bind their exact SHA-256 body. A lost response must reconcile the exact external payload before it can retry, so a lookalike tag cannot be mistaken for the commissioned result.

Copy docs/lanes.manifest-v2.example.json to ~/.conductor-telegram/lanes.manifest.v2.json. Manifest v2 accepts only the approved provider/model/cap map (claude/fable-5-1 at 3, codex/gpt-5.6-sol at 2, cursor/grok-4.7 at 2), rejects runtime IDs and dependency cycles, and verifies every prompt SHA-256 both on startup and immediately before a commissioned delivery. Recurring schedules are daily, weekly, or every <positive integer><m|h|d>.

Production workers require LANES_STATE_BACKEND=http, COMMAND_CENTER_API_BASE_URL, COMMAND_CENTER_API_KEY, CONDUCTOR_API_KEY, BOT_TOKEN, and OWNER_CHAT_ID. The OVH service is headless—it never polls or consumes Telegram updates—but it retains send-only credentials so the active lease holder can always emit a deduplicated safety alert. The Mac Telegram process additionally receives the separate BELONG_HUMAN_APPROVAL_KEY; that human key is forcibly removed from both lane-worker environments. LANES_MANIFEST_SOURCE_REF may carry the canonical Git revision; when omitted, both workers use the same content-addressed manifest SHA-256 rather than host-specific file paths. Add GITLAB_TOKEN only when a lane uses the GitLab adapter. SQLite requires both LANES_STATE_BACKEND=sqlite and LANES_STANDALONE=1; it is intended only for standalone tests and is never an HTTP fallback.

The production HTTP/Postgres schema and transaction contract are maintained by the pinned Command Center implementation documented in the lanes integration contract. Apply and verify that control-plane slice before enabling this worker; this repository does not run ad-hoc SQL against Command Center.

The durable CLI is:

conductor-telegram lanes worker
conductor-telegram lanes status --json
conductor-telegram lanes reconcile
conductor-telegram lanes import-legacy --source /path/to/legacy-queue.json --dry-run
conductor-telegram lanes import-legacy --source /path/to/legacy-queue.json --apply

Run the dry-run first. Pinned legacy IDs are candidates only: the importer prefers the workspace linked to the current PR head, then the newest unarchived candidate with verified progress, and quarantines ambiguity. If an exact merged PR is the only authoritative truth and all of its old workspace candidates are archived, the importer adopts the merged SHA without reviving or binding an archived workspace. An apply requires the controller lease to be free and the exact manifest revision already active; it renews that lease during the authoritative rescan and revalidates the fence immediately before writing. Untagged adopted workspaces remain untrusted for hygiene and need one exact, 24-hour Telegram archive batch approval (/lanes archive-approval batch).

On the Mac, save the production credentials in the configured Doppler environment, then install the independent launchd job:

conductor-telegram service install --with-lanes \
  --doppler-project <project> \
  --doppler-config <config>
conductor-telegram doctor

The OVH standby unit is packaging/systemd/conductor-telegram-lanes.service. Its root-owned /etc/conductor-telegram/lanes.env must point at the same HTTP state plane and manifest revision. The service is headless and does not run Telegram polling.

The worker stages a deterministic revision such as growth-v2-<20 hex> and logs it. Start with /lanes shadow <revision>. A shadow full reconciliation is read-only and reports missing/ambiguous projects, managed/duplicate workspace names, binding drift, exact PR/head drift, and read errors. Active full reconciliation applies the same inventory checks to every manifest repository and managed workspace, including unknown run tags and archived/live drift; an early corrective action does not postpone the unfinished sweep for another 15 minutes. After that comparison, credential doctor, legacy import report, disposable canary, and CI all pass, the single human cutover is /lanes cutover <revision>. /lanes rollback disables dispatch without deleting state or workspaces; it never enables the legacy Python shepherd. /lanes resume can recover only a safety pause that originated in active mode; shadow and rollback require their explicit cutover sequence. A manifest revision cannot replace the active policy while an older revision still has nonterminal work.

Delivery requires an adversarial reviewer distinct from the author, then two current-head final attestations from distinct providers. Attestations are accepted only from commissioned attempts with the exact nonce, run, stage, provider, head SHA, tag, and body hash, and terminal PR/review/validation output is not consumed until its Conductor session is idle. Merge is bound to owner/repository/base/head branch/head SHA and requires the configured named checks to pass; both Git hosts reassert the expected head after a merge response. Completion requires CI on the merged SHA and the manifest's exact command/probe evidence; commentary markers alone do not count. Terminal workspace grace starts at the original terminal transition, and an untagged archive approval is bound to an immutable, expiring list containing the current workspace ID. Archive hygiene performs a second live-session preflight after recording its intent and immediately before mutation; a newly working session pauses the controller instead of being archived. The controller observes repository-native CI but never deploys, publishes, performs outreach, spends money, or changes secrets.

Legacy lanes scheduler

An optional in-process scheduler keeps at most one working Cloud lane per configured provider, using that provider's model, from an ordered queue with dependencies. It is inert unless a config file is present at LANES_CONFIG or ~/.conductor-telegram/lanes.json. Copy docs/lanes.example.json and replace the placeholders (L1, https://github.com/example-org/example-repo, example model ids).

Each tick (every intervalMinutes, and on /lanes run) looks up matching workspaces including archived workspaces by [lane:<id>:…] name containment, or by an explicit sessionId / workspaceId; names containing [abandoned are ignored. A listing outage — including a partial per-project fallback outage — skips the tick instead of creating a duplicate workspace. A lane is working when its newest session is working (even if the transcript is empty or unread), done when the last idle-turn assistant text contains a GitHub pull-request URL (tool payloads and reasoning/thinking items are ignored), initializing when no user message has been recorded yet, paused when it exists and is idle or in error but not done, unknown when status cannot be read or an idle transcript cannot be read, and not created otherwise. Providers under maxActive resume or create dependency-ready work. /lanes pause suppresses scheduled Cloud walks before any listing.

Each lane may add an optional delivery object, with any combination of review, finals, merge, and validation. merge requires finals, and validation requires merge, so the scheduler can prove its preconditions:

  • review creates one adversarial reviewer on the first free provider in rotation other than the author, waits for its GitHub review, then messages the author once.
  • finals runs two sequential final reviewers on distinct non-author providers. Their GitHub review body starts with FINAL-REVIEW (<real model>): {"verdict":"approve"|"changes", ...}. A change verdict returns the lane to its author and starts a fresh round after the next pushed turn.
  • merge refreshes GitHub directly and requires a repo-bound open PR, the exact reviewed head, two commissioned approving GitHub attestations from distinct providers on that head, passing checks, and a conflict-free merge state. It does not depend on GitHub's aggregate approval state, so same-account validators remain verifiable. It uses method (squash, merge, or rebase) and accepts MERGED BY AGENTS only when its full SHA matches GitHub's merge commit. Conflicts go back to the author for a rebase.
  • validation runs the exact configured verification once against the merged base on a provider distinct from the author and merge executor. A VALIDATED (<real model>) or VALIDATION FAILED (<real model>) marker is advisory until a matching terminal Conductor command/tool receipt is present; passing completion also requires green CI on the merged SHA. Failed validation may open a narrowly scoped repair PR but never deploys directly.

Stage prompts are paths relative to lanes.json and support {{laneId}}, {{laneTitle}}, {{prUrl}}, {{round}}, {{slot}}, {{model}}, {{previousFinalReview}}, {{mergeMethod}}, {{mergeHeadSha}}, {{deployNotes}}, {{replayNotes}}, and {{verification}}. Required safety/marker instructions are appended by the scheduler. The example config and prompt templates live in docs/lanes.example.json and docs/prompts/.

The scheduler keeps its delivery and nudge ledger in SQLite. Two nudges without a newer assistant response mark a session dead; recovery starts a new session in the same workspace and always targets that workspace's newest session. Rate-limit reset timestamps found in assistant transcripts suppress work until the reset, and the next available provider in a stage rotation acts as the stand-in using its real model in markers.

Hygiene runs after each active tick. It archives finished review/final/merge/validation workspaces and all lane workspaces after merge, never archives a working newest session, and never recreates a matching archived workspace. /lanes archive runs the same hygiene immediately. /lanes merge <id> forces a policy check but cannot bypass final approvals or dependency/conflict rules. /lanes shows author → review → finals → merge → validation progress per lane. All /lanes forms remain owner-only through the existing middleware; merges, validations, and archive batches send one-line Telegram notices.

Prompt paths in the config are relative to the config file's directory.

Manual Telegram setup

If you want to configure the bot manually instead of using the CLI wizard, the bot supports two operating modes:

  • Private chat mode: talk to the bot directly in a one-on-one chat.
  • Forum topic mode: run the bot in a Telegram supergroup with Topics enabled so each workspace gets its own topic.

Private chat mode

  1. Create a bot with BotFather and copy the token into BOT_TOKEN.
  2. Temporarily set OWNER_CHAT_ID=0.
  3. Start the bot.
  4. Open a direct chat with the bot and send /start or /setup.
  5. If the bot shows a Use This Chat button, tap it. The bot will save this private chat automatically.
  6. Leave OWNER_USER_ID empty.
  7. Restart the bot only if you are running it with hardcoded env vars outside the CLI.

Forum topic mode

Add the bot to your target group, make it admin, then run setup in that group.

  1. Create a Telegram supergroup.
  2. Enable Topics in the supergroup settings.
  3. Add the bot to the supergroup.
  4. Promote the bot to admin with permission to create/manage topics and send messages.
  5. Temporarily set OWNER_CHAT_ID=0 and OWNER_USER_ID=0.
  6. Start the bot.
  7. Send /setup in the target supergroup.
  8. If the bot shows a Use This Chat button, tap it. The bot will save this supergroup and your Telegram user automatically.
  9. Restart the bot only if you are running it with hardcoded env vars outside the CLI.

New workspaces will create one forum topic per workspace automatically. Repo topics can also be created from /repos and reused as the stable home for that repository's current workspace. If topic creation fails because the chat is not a forum or the bot lacks permissions, the bot falls back to normal chat messages.

If the bot is already configured for your private chat, you can also add it to a new group and send /setup there from the same Telegram account. The bot will show what is missing and can switch itself into group/forum mode from that chat without first resetting OWNER_CHAT_ID.

Bootstrap mode

When OWNER_CHAT_ID=0, the bot temporarily allows /start, /help, and /setup before auth is configured. This is the intended bootstrap mode for letting the bot configure the active chat for you.

CLI commands

conductor-telegram              Start the bot (foreground)
conductor-telegram setup        Interactive configuration wizard
conductor-telegram doctor       Validate config, token, paths, and connectivity
conductor-telegram status       Show configuration health
conductor-telegram lanes worker Run the independent fenced lane controller
conductor-telegram lanes status --json  Read the durable control-plane snapshot
conductor-telegram lanes reconcile      Run one full reconciliation pass
conductor-telegram lanes import-legacy  Plan/apply the one-time legacy adoption
conductor-telegram install-plugin  Install MCP server into Claude Code
conductor-telegram help         Show all commands
conductor-telegram --version    Show version

Configuration

Config is stored at ~/.conductor-telegram/config.json (created by setup).

Precedence: CLI flags > environment variables > config.json > defaults

| Flag | Env Var | Description | |------|---------|-------------| | --token | BOT_TOKEN | Telegram bot token | | --chat-id | OWNER_CHAT_ID | Your Telegram chat ID | | --db-path | DB_PATH | SQLite database path | | --doppler-project | CONDUCTOR_TELEGRAM_DOPPLER_PROJECT | Doppler project used by foreground and launchd runtimes | | --doppler-config | CONDUCTOR_TELEGRAM_DOPPLER_CONFIG | Doppler config used by foreground and launchd runtimes | | | OWNER_USER_ID | Your Telegram user ID (required for forum mode) | | | TELEGRAM_CLOUD_SYNC_CHAT_ID | Forum group that receives Cloud workspaces created outside Telegram (cloud-only mode) | | | TELEGRAM_CLOUD_SYNC_INPUT | commands during gateway coexistence, or all for ordinary text and voice replies (default: all) | | | CONDUCTOR_WORKSPACES_DIR | Conductor workspaces directory | | | CONDUCTOR_REPOS_DIR | Repository directory | | | CONDUCTOR_DB_PATH | Conductor's own database path | | | TELEGRAM_DEFAULT_AGENT_TYPE | Default agent: claude or codex | | | TELEGRAM_DEFAULT_MODEL | Default model for agents | | | TELEGRAM_REVIEW_AGENT_TYPE | Agent type for /review sessions | | | TELEGRAM_REVIEW_MODEL | Model for /review sessions | | | TELEGRAM_AGENT_PERMISSION_MODE | Legacy Claude permission mode (default: acceptEdits) | | | CONDUCTOR_API_BASE_URL | Conductor API origin (default: https://api.conductor.build) | | | CONDUCTOR_API_KEY | Bearer API key for supported Conductor Cloud operations | | | CONDUCTOR_CLOUD_BACKEND | auto (use API when keyed), api (require key), or off | | | LANES_CONFIG | Path to the optional lanes scheduler JSON (default ~/.conductor-telegram/lanes.json) | | | LANES_MANIFEST | Path to strict Manifest v2 (default ~/.conductor-telegram/lanes.manifest.v2.json) | | | LANES_MANIFEST_SOURCE_REF | Optional canonical Git source revision; defaults to the cross-host manifest SHA-256 | | | LANES_STATE_BACKEND | Durable state backend: production must be http; sqlite is explicit standalone/test only | | | COMMAND_CENTER_API_BASE_URL | Command Center origin for the durable lane state API | | | COMMAND_CENTER_API_KEY | Service credential for the durable lane state API | | | BELONG_HUMAN_APPROVAL_KEY | Separate credential used by Telegram for cutover/rollback/archive approvals; excluded from the worker | | | GITLAB_TOKEN | GitLab API token, required only by manifests containing GitLab lanes | | | TELEGRAM_WHISPER_MODEL | whisper.cpp model name or path (default: base) |

Conductor app settings are read from ~/.conductor/settings.toml first, with the legacy Conductor DB settings table as fallback. The bot uses Conductor's default/review model settings, Codex thinking levels, Claude effort levels, and git branch prefix settings when Telegram-specific env vars are not set.

To keep runtime secrets in Doppler, persist only the non-secret project/config references:

conductor-telegram service install \
  --doppler-project <project> \
  --doppler-config <config>
conductor-telegram doctor

The installer verifies the persistent Doppler identity available to launchd, removes each Doppler-managed value from config.json, and writes a plist containing only the Doppler executable, project/config references, and an explicit allowlist of environment names—not values or a Doppler service token. The allowed names are BOT_TOKEN, OWNER_CHAT_ID, OWNER_USER_ID, TELEGRAM_CLOUD_SYNC_CHAT_ID, TELEGRAM_CLOUD_SYNC_INPUT, CONDUCTOR_API_BASE_URL, CONDUCTOR_API_KEY, CONDUCTOR_CLOUD_BACKEND, COMMAND_CENTER_API_BASE_URL, COMMAND_CENTER_API_KEY, BELONG_HUMAN_APPROVAL_KEY, GITLAB_TOKEN, LANES_MANIFEST, and LANES_MANIFEST_SOURCE_REF. The lane worker deliberately receives every applicable value except BELONG_HUMAN_APPROVAL_KEY; only the Telegram process can commission human-gated controls. Use BOT_TOKEN exactly; TELEGRAM_BOT_TOKEN is not an alias.

start, status, and doctor automatically re-enter the configured Doppler runtime. Run service install again after adding a new allowed secret name. A value-only rotation needs only a service restart.

If Doppler is not configured, keep CONDUCTOR_API_KEY only in the bot's mode-0600 config file or service environment. It is excluded from child-agent environments and must not be copied into repositories, Conductor workspace environment variables, prompts, or MCP configuration.

Existing .env files are auto-detected and can be imported during setup.

MCP server

The MCP server runs inside Conductor workspaces and gives agents these tools:

| Tool | Description | |------|-------------| | report_status | Report progress back to Telegram (status label + message) | | report_artifact | Report a deliverable: PR, commit, or file | | request_human | Ask the operator a question, optionally with button choices |

The request_human tool blocks (polls for up to 5 minutes) until the operator answers via Telegram — either by tapping a button or replying with free-form text.

Install with conductor-telegram install-plugin or during setup.

Database

SQLite database at ~/.conductor-telegram/conductor-telegram.db with WAL mode for concurrent writes from the bot and multiple MCP server instances.

Tables:

| Table | Purpose | |-------|---------| | workspaces | Tracked workspace state, status, repo path, Telegram thread | | events | Status updates, artifacts, and human requests from MCP | | decisions | Questions posed to the operator with answers | | telegram_message_links | Maps Telegram messages to workspaces for reply routing | | thread_cursors | Per-Conductor-session forwarding cursors for thread fan-out | | bot_heartbeat | Process liveness, boot count, and last exit details | | meta | Durable Cloud launches, ordered messages, stop/archive intents, work leases, recovery notices, and the lanes pause flag | | pr_records | GitHub PR/check/merge state verified by repo + branch | | merge_intents | Expiring requester-bound confirmations for an exact PR head SHA | | repo_topics | Durable Telegram forum topics mapped to repos for no-guess launch routing | | route_attempts | Redacted routing audit log for routed, failed, confirmed, and cancelled attempts | | lane_actions | Create/nudge history for the optional lanes scheduler | | lane_delivery_state | Durable per-lane review, final, merge, and validation stage state | | lane_session_health | Unanswered-nudge and reset-time ledger for lane sessions | | lane_provider_outages | Provider rate-limit reset times used by stage rotations |

Development

git clone https://github.com/belongnet/conductor-telegram.git
cd conductor-telegram
npm install

# Run in development mode
npm run dev           # CLI entry point
npm run dev:bot       # Bot directly
npm run dev:mcp       # MCP server

# Build
npm run build

# Type check
npm run typecheck

# Run tests
npm test

Requires Node.js v22+. See CONTRIBUTING.md for branching, commit style, and PR guidelines. Intentionally deferred repository work is tracked in TODOS.md.

Troubleshooting

Run conductor-telegram doctor to check all components:

$ conductor-telegram doctor

  Node.js     ✓ v22.14.0 (required >=22)
  Config      ✓ ~/.conductor-telegram/config.json (0600)
  Bot token   ✓ @MyBot connected
  Database    ✓ ~/.conductor-telegram/conductor-telegram.db
  Conductor   ✓ ~/Library/Application Support/com.conductor.app/conductor.db
  Conductor Cloud API  ✓ api-key authenticated; 3 cloud project(s) visible
  GitHub CLI  ✓ gh version 2.x.x
  MCP Plugin  ✓ ~/.claude/plugins/conductor-telegram-mcp installed
  Repos       ✓ ~/conductor/repos (4 repositories)

Common issues:

  • "Bot token is invalid": Token may be revoked. Create a new one with @BotFather and run conductor-telegram setup.
  • "better-sqlite3 failed to load": Run npm rebuild better-sqlite3. If that fails, install Xcode CLI tools: xcode-select --install.
  • "Conductor DB not found": Install Conductor or set conductorDbPath in config.

Upgrading

npm i -g conductor-telegram@latest
conductor-telegram doctor

Config is preserved across upgrades. The doctor command validates everything still works. Release notes live in CHANGELOG.md.

Mac gateway deployment

A gateway host keeps itself current. Enrolling is opt-in:

conductor-telegram service install --with-updater

This registers a third LaunchAgent, net.belong.conductor-telegram.updater, alongside the bot and its watchdog (plus the independent lanes job when --with-lanes is selected). A plain service install never enrolls the updater, so published-package users keep the normal npm i -g conductor-telegram@latest upgrade contract. Every minute the agent fetches origin/main into a canonical checkout at ~/.conductor-telegram/gateway/repo (cloning it on first run, so a fresh machine self-bootstraps), and whenever the remote moves it redeploys:

scripts/gateway-update.sh       # the poller — copied to ~/.conductor-telegram/bin/ at install
scripts/deploy-mac-gateway.sh   # the deploy it triggers

Each deploy runs the Node 22 typecheck/tests/build in the checkout, then packs a release tarball and installs that globally — the live gateway is a copy, so nothing the deploy does to the checkout (git reset, npm ci) can touch running code before all gates pass. Only then does it reinstall and restart the launchd service and run doctor as a configuration/connectivity gate. A failure before the install step leaves the previous gateway untouched and running; a failure at the doctor stage means the new build is already live and gets retried. Failed revisions retry on a 30-minute backoff until a new push lands. Deploys have a hard 40-minute timeout, refuse non-fast-forward (force-pushed) branch tips, and report success/failure straight to the owner's Telegram chat when the bot token is in config.json. All three agents are RunAtLoad, so a reboot restarts the bot and immediately catches up on any pushes it slept through. Progress lands in ~/.conductor-telegram/update.log; service status shows the deployed revision; service stop is respected — auto-deploys will not resurrect a bot the operator stopped.

Polling means no self-hosted runner, no inbound webhook, and no GitHub credentials on the machine — the repo is public, so the updater fetches anonymously. Note the flip side: enrolling a machine means anyone who can push to main can run code on it within a minute. Saved Doppler project/config references survive each reinstall, so deployment never needs to materialize secret values in the checkout or launchd plist.

Updater environment overrides, baked into the agent when set at service install --with-updater time: CONDUCTOR_TELEGRAM_GATEWAY_HOME (state + checkout root, default ~/.conductor-telegram/gateway), CONDUCTOR_TELEGRAM_GATEWAY_REMOTE (git URL), CONDUCTOR_TELEGRAM_GATEWAY_BRANCH (default main), CONDUCTOR_TELEGRAM_GATEWAY_LOG (default ~/.conductor-telegram/update.log).

License

MIT - Built by Belong.net