From 7156e745e0f8a2be0eff22e998e88a859206d933 Mon Sep 17 00:00:00 2001 From: Andrew Chen <48723787+chuenchen309@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:11:15 +0800 Subject: [PATCH] fix(channels): don't treat a bare "connect" as a bind command (#4251) `extract_connect_code` matched `command in {"/connect", "connect"}`, so any message whose first word is the ordinary English word "connect" was parsed as `/connect ` and its next word swallowed as a one-time bind code. For example `extract_connect_code("connect the database to the api")` returned `"the"` instead of `None`, meaning a normal chat message never reached the agent and instead attempted a channel bind. Every other channel control command requires a leading slash: `is_known_channel_command` only matches `/`-prefixed tokens and `KNOWN_CHANNEL_COMMANDS` holds only slash forms (`/goal`, `/new`, ...). The docstrings and AGENTS.md advertise only `/connect `. The bare alias was inconsistent with all of that and produced the false-positive bind. Match only `/connect`. The `.lower()` keeps it case-insensitive (`/Connect`) and the mention-prefix handling from #4222 is unaffected, so `@bot /connect ` still binds. Co-authored-by: Claude Opus 4.8 (1M context) --- backend/app/channels/commands.py | 2 +- .../test_additional_channel_connections.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/backend/app/channels/commands.py b/backend/app/channels/commands.py index 2293b03ba..bd7b5a08f 100644 --- a/backend/app/channels/commands.py +++ b/backend/app/channels/commands.py @@ -77,7 +77,7 @@ def extract_connect_code(text: str) -> str | None: if index + 1 >= len(parts): return None command = parts[index].lower() - if command in {"/connect", "connect"}: + if command == "/connect": return parts[index + 1] return None diff --git a/backend/tests/test_additional_channel_connections.py b/backend/tests/test_additional_channel_connections.py index 546860f56..55c76cc87 100644 --- a/backend/tests/test_additional_channel_connections.py +++ b/backend/tests/test_additional_channel_connections.py @@ -60,6 +60,23 @@ def test_pending_connect_code_accepts_leading_platform_mentions(): assert channel._pending_connect_code("@_user_1 /connect via-base") == "via-base" +def test_bare_connect_without_slash_does_not_bind(): + """A message that merely starts with the word "connect" is normal chat. + + Every other channel control command requires a leading slash + (``is_known_channel_command`` only matches ``/``-prefixed tokens, and + ``KNOWN_CHANNEL_COMMANDS`` holds only slash forms), so a bare ``connect`` + must not be treated as ``/connect `` and swallow the next word as a + bind code — otherwise "connect the database to the api" binds to "the". + """ + assert extract_connect_code("connect the database to the api") is None + assert extract_connect_code("connect abc") is None + assert extract_connect_code("@bot connect abc") is None + # The real slash command still binds, including after a mention prefix. + assert extract_connect_code("/connect abc") == "abc" + assert extract_connect_code("@bot /connect abc") == "abc" + + def test_pending_connect_code_is_none_when_connections_disabled(): # With no connection repo, binding is not configured and connect codes are # ignored so the message falls through to normal handling.