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 <code>` 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 <code>`. 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 <code>` still binds.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andrew Chen
2026-07-17 15:11:15 +08:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 10890e10a8
commit 7156e745e0
2 changed files with 18 additions and 1 deletions
+1 -1
View File
@@ -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
@@ -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 <code>`` 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.