mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-28 17:06:05 +00:00
`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>
90 lines
3.3 KiB
Python
90 lines
3.3 KiB
Python
"""Shared command definitions used by all channel implementations.
|
|
|
|
Keeping the authoritative command set in one place ensures that channel
|
|
parsers (e.g. Feishu) and the ChannelManager dispatcher stay in sync
|
|
automatically — adding or removing a command here is the single edit
|
|
required.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
KNOWN_CHANNEL_COMMANDS: frozenset[str] = frozenset(
|
|
{
|
|
"/bootstrap",
|
|
"/goal",
|
|
"/new",
|
|
"/status",
|
|
"/models",
|
|
"/memory",
|
|
"/help",
|
|
}
|
|
)
|
|
|
|
|
|
def _is_leading_mention_token(token: str) -> bool:
|
|
"""Return whether *token* looks like a platform bot/user mention.
|
|
|
|
Group chats often require ``@bot`` before the message is delivered. Slack
|
|
and Discord strip those tokens before connect parsing; Feishu / DingTalk
|
|
leave them in the text (``@_user_1``, ``@bot``, ``<@id>``). Treat them as
|
|
transport noise only when they lead the message so
|
|
``@bot /connect <code>`` still binds.
|
|
"""
|
|
if not token:
|
|
return False
|
|
# Slack / Discord style: <@U123> or <@!U123> or <@U123|name>
|
|
if token.startswith("<@") and token.endswith(">"):
|
|
return True
|
|
# Feishu / DingTalk / generic: @_user_1, @bot, @nickname
|
|
if token.startswith("@") and len(token) > 1:
|
|
return True
|
|
return False
|
|
|
|
|
|
def strip_leading_mentions(text: str) -> str:
|
|
"""Drop leading platform mention tokens (``@bot``, ``<@id>``) so a group-chat
|
|
``@bot /goal`` reads as ``/goal`` for command classification and dispatch.
|
|
|
|
A mention must be flush at the start (no preceding whitespace), mirroring the
|
|
"a control command must be at position 0" rule in :func:`is_known_channel_command`:
|
|
text with a leading space or no leading mention is returned unchanged, so
|
|
``" /new"`` stays a non-command. Whitespace is otherwise preserved (unlike
|
|
:func:`extract_connect_code`, which is deliberately whitespace-lenient for the
|
|
bind path). Channels that resolve their own bot id (Slack/Discord) strip only
|
|
the bot's mention upstream; this is for adapters that leave the mention in the
|
|
text and cannot tell the bot's mention from another user's (Feishu/DingTalk).
|
|
"""
|
|
remainder = text
|
|
while True:
|
|
parts = remainder.split(maxsplit=1)
|
|
if not parts or remainder[0].isspace() or not _is_leading_mention_token(parts[0]):
|
|
break
|
|
remainder = parts[1] if len(parts) > 1 else ""
|
|
return remainder
|
|
|
|
|
|
def extract_connect_code(text: str) -> str | None:
|
|
"""Extract the one-time channel binding code from a connect command.
|
|
|
|
Accepts a leading platform mention so group ``@bot /connect <code>``
|
|
messages bind the same way as bare ``/connect <code>`` (Slack/Discord
|
|
already strip mentions before calling this helper).
|
|
"""
|
|
parts = text.strip().split()
|
|
index = 0
|
|
while index < len(parts) and _is_leading_mention_token(parts[index]):
|
|
index += 1
|
|
if index + 1 >= len(parts):
|
|
return None
|
|
command = parts[index].lower()
|
|
if command == "/connect":
|
|
return parts[index + 1]
|
|
return None
|
|
|
|
|
|
def is_known_channel_command(text: str) -> bool:
|
|
"""Return whether text starts with a registered channel control command."""
|
|
if not text.startswith("/"):
|
|
return False
|
|
return text.split(maxsplit=1)[0].lower() in KNOWN_CHANNEL_COMMANDS
|