From 0d24d315b5f70b1fdb177f4122d2545236665ca2 Mon Sep 17 00:00:00 2001 From: MagMueller Date: Fri, 15 May 2026 13:17:48 -0700 Subject: [PATCH] v16: register composio MCP for codex too; simplify autopilot trigger paragraph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The doctrine reviewer flagged the claude/codex composio gap: bootstrap.sh registers the cloud composio MCP server for claude only (`claude mcp add`), and the system prompt referenced composio tools as if both CLIs had them. Codex lanes silently couldn't onboard, voice-mirror, or call Gmail/Slack tools. Fixed by adding a parallel codex registration. Codex CLI ≥ 0.30 supports HTTP-transport MCP natively (no mcp-remote bridge): codex mcp add composio \ --bearer-token-env-var BUX_BOX_TOKEN \ -- https://api.browser-use.com/cloud/composio/mcp Writes the entry to ~/.codex/config.toml. The bearer token isn't baked into the file — codex reads BUX_BOX_TOKEN at MCP-connect time. telegram_bot.py:_build_env now forwards BUX_BOX_TOKEN to the codex subprocess so the env var is available. Verification mirrors the claude path: `codex mcp list | grep '^composio'` after registration; warn loudly if it didn't take. Source: https://developers.openai.com/codex/mcp + https://developers.openai.com/codex/config-reference **Autopilot trigger paragraph simplified.** Per your direction, removed the second sentence ("Colloquial throwaways like 'just do it' or 'don't bother me' are not triggers"). The rule is now just: unlock autopilot when the user's prompt clearly says no approvals — anything close to 'autopilot', 'full autonomy', 'no approvals', 'don't ask me anything', 'completely autonomous'. When in doubt, copilot. Tests: 21 pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- agent/bootstrap.sh | 30 +++++++++++++++++++++++++++++- agent/system-prompt.md | 2 +- agent/telegram_bot.py | 6 ++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/agent/bootstrap.sh b/agent/bootstrap.sh index f594755..944c49b 100755 --- a/agent/bootstrap.sh +++ b/agent/bootstrap.sh @@ -215,7 +215,35 @@ else if ! sudo -u bux -H bash -c 'cd /home/bux && claude mcp list 2>/dev/null' | grep -q '^composio'; then echo "bootstrap: WARN composio MCP registration didn't take" >&2 else - echo "bootstrap: registered cloud Composio MCP server" + echo "bootstrap: registered cloud Composio MCP server (claude)" + fi +fi + +# Same composio MCP for codex. Codex CLI ≥ 0.30 supports HTTP-transport MCP +# servers natively (no mcp-remote bridge needed). `codex mcp add` writes to +# ~/.codex/config.toml. We use --bearer-token-env-var so the token isn't +# baked into the config file — codex reads BUX_BOX_TOKEN at MCP-connect time. +# telegram_bot.py:_build_env forwards BUX_BOX_TOKEN to the codex subprocess. +if [ -z "${BUX_BOX_TOKEN:-}" ]; then + echo "bootstrap: BUX_BOX_TOKEN not set; skipping codex Composio MCP registration" >&2 +elif ! sudo -iu bux command -v codex >/dev/null 2>&1; then + echo "bootstrap: codex CLI not on PATH; skipping codex Composio MCP registration" >&2 +else + # `sudo -iu bux` (login shell) so per-user PATH from ~/.profile picks up + # ~/.npm-global/bin where codex actually lives — `-u bux -H` skips that. + sudo -iu bux codex mcp remove composio >/dev/null 2>&1 || true + # HTTP-transport MCP servers in codex use `--url `, not `-- `. + # The `--` form is for stdio commands. Keep stderr unredirected so any + # real failure surfaces in install.log instead of getting swallowed. + if ! ( set +x; sudo -iu bux codex mcp add composio \ + --bearer-token-env-var BUX_BOX_TOKEN \ + --url https://api.browser-use.com/cloud/composio/mcp >/dev/null ); then + echo "bootstrap: WARN failed to register cloud Composio MCP server for codex" >&2 + fi + if ! sudo -iu bux codex mcp list 2>/dev/null | grep -q '^composio'; then + echo "bootstrap: WARN codex composio MCP registration didn't take" >&2 + else + echo "bootstrap: registered cloud Composio MCP server (codex)" fi fi diff --git a/agent/system-prompt.md b/agent/system-prompt.md index ca629ff..ca4621e 100644 --- a/agent/system-prompt.md +++ b/agent/system-prompt.md @@ -9,7 +9,7 @@ You are **agency**, the user's 24/7 employee on a Linux VPS. They text you from - **Telegram is the only inbox.** One forum topic = one persistent agent session. - **Default mode everywhere is copilot** — do every reversible thing right away (read, draft, query, scrape, render) then **propose** the next visible action as a card the user accepts with one tap. Ask before anything visible to other people: sending email, posting publicly, merging, paying, deleting hard-to-recover data, anything that affects another person's view. - **`/goal ` = continuous goal-mode, still copilot by default.** You keep working on the goal across turns — scan, draft, post cards, end turn. The user taps to accept; that's a new turn (`--resume` carries session context); pick up where you left off, queue up the next concrete action, post the next card. Persist state to `agency.db` / `goals.md` / `notebook.md` so each turn knows what's done. No 30-min timeout; a `/goal` can run for days. Self-schedule with `tg-schedule` when you're waiting on something (a reply, CI, an event). -- **Autopilot is unlocked only by an unambiguous opt-in phrase.** The user's prompt must contain something close to *"autopilot"*, *"full autonomy"*, *"no approvals"*, *"don't ask me anything"*, or *"completely autonomous"* — exact wording that names the mode or explicitly waives approvals. Colloquial throwaways like "just do it" or "don't bother me" are **not** triggers (too easy to say in passing). When in doubt, stay copilot. Without an unambiguous cue, **stay copilot even inside `/goal`**. +- **Autopilot is unlocked only when the user clearly wants no approvals.** Phrases like *"autopilot"*, *"full autonomy"*, *"no approvals"*, *"don't ask me anything"*, *"completely autonomous"* — or any wording that unambiguously says "don't ask me to approve, just act". When in doubt, stay copilot. Without a clear cue, **stay copilot even inside `/goal`**. - **When the user mentions a goal in natural language** (e.g. "make my startup successful", "get more users", "respond to this email"), treat it the same as `/goal` — continuous copilot. The slash command is just a convention; it isn't a magic mode flip. - **Silence is allowed.** If nothing's actionable, send nothing. Empty turns are fine; filler isn't. diff --git a/agent/telegram_bot.py b/agent/telegram_bot.py index d1fc48b..0a0edfa 100644 --- a/agent/telegram_bot.py +++ b/agent/telegram_bot.py @@ -3656,6 +3656,12 @@ def _build_env( if box_env.get("BUX_PROFILE_ID"): env["BUX_PROFILE_ID"] = box_env["BUX_PROFILE_ID"] env["BU_PROFILE_ID"] = box_env["BUX_PROFILE_ID"] + if box_env.get("BUX_BOX_TOKEN"): + # Codex's composio MCP entry uses `bearer_token_env_var = "BUX_BOX_TOKEN"` + # — codex reads this env var at MCP-connect time and sends + # `Authorization: Bearer `. Without it in the codex + # subprocess env, the MCP call hits the cloud with no auth. + env["BUX_BOX_TOKEN"] = box_env["BUX_BOX_TOKEN"] for k in ("BU_CDP_WS", "BU_BROWSER_ID"): if browser_env.get(k): env[k] = browser_env[k]