The slackify_markdown library (markdown-it) fails to convert **bold** when
the closing ** is immediately followed by non-space text (e.g. **Status:**OK).
This is a very common LLM output pattern that results in raw ** showing up
in Slack messages.
Add _fixup_mrkdwn() post-processor that:
- Converts leftover **bold** → *bold* (Slack mrkdwn)
- Converts leftover ## headers → *bold* (safety net)
- Fixes over-escaped & in bare URLs
- Protects code fences and inline code from being mangled
Co-authored-by: Cursor <cursoragent@cursor.com>
The slackify_markdown library misses several patterns that LLMs commonly
produce, causing raw Markdown symbols (**bold**, ##headers) to appear
in Slack messages.
Add _fixup_mrkdwn() post-processor that:
- Converts leftover **bold** patterns (e.g. **Status:**OK where closing
** is adjacent to non-space chars)
- Fixes & over-escaping in bare URLs
- Protects code blocks from false-positive fixups
Co-authored-by: Cursor <cursoragent@cursor.com>
- Defer Brave API key resolution to execute() time instead of __init__,
so env var or config changes take effect without gateway restart
- Improve error message to reference actual config path
(tools.web.search.apiKey) instead of only mentioning env var
Fixes#1069 (issues 1 and 2 of 3)
Previously, `autoReplyEnabled=false` would block ALL email sends,
including proactive emails triggered from other channels (e.g., asking
nanobot on Feishu to send an email).
Now `autoReplyEnabled` only controls automatic replies to incoming
emails, not proactive sends. This allows users to disable auto-replies
while still being able to ask nanobot to send emails on demand.
Changes:
- Check if recipient is in `_last_subject_by_chat` to determine if
it's a reply
- Only skip sending when it's a reply AND auto_reply_enabled is false
- Add test for proactive send with auto_reply_enabled=false
- Update existing test to verify reply behavior
Always provide an explicit httpx client to prevent MCP HTTP transport from inheriting httpx's default 5-second timeout, thereby avoiding conflicts with the upper layer tool's timeout settings.
Fixes#1042. When the LLM returns tool call arguments as a dict or
JSON string instead of parsed values, memory consolidation would fail
with "TypeError: data must be str, not dict".
Changes:
- Add type guard in MemoryStore.consolidate() to parse string arguments
and reject unexpected types gracefully
- Add regression tests covering dict args, string args, and edge cases
Each Slack thread now gets its own conversation session instead of
sharing one session per channel. DM sessions are unchanged.
Added as a generic feature to also support if Feishu threads support
is added in the future.