Uses difflib to find the best match and shows a helpful diff,
making it easier to debug edit_file failures.
Co-authored-by: Cursor <cursoragent@cursor.com>
- _split_message: return empty list for empty/None content instead
of a list with one empty string (Discord rejects empty content)
- _split_message: use pos <= 0 fallback to prevent empty chunks
when content starts with a newline or space
- _send_payload: return bool to indicate success/failure
- send: abort remaining chunks when a chunk fails to send,
preventing partial/corrupted message delivery
Follow-up to #864. Three f-string logger calls in base.py and dingtalk.py
were missed in the original sweep. These can cause KeyError if interpolated
values contain curly braces, since loguru interprets them as format placeholders.
list_sessions() previously reconstructed the session key by replacing all
underscores in the filename with colons. This is lossy: a key like
'cli:user_name' became 'cli:user:name' after round-tripping.
Now the actual key is persisted in the metadata line during save() and read
back in list_sessions(). Legacy files without the key field fall back to
replacing only the first underscore, which handles the common channel:chat_id
pattern correctly.
Closes#899
Discord's API rejects messages longer than 2000 characters with HTTP 400.
Previously, long agent responses were silently lost after retries exhausted.
Adds _split_message() (matching Telegram's approach) to chunk content at
line boundaries before sending. Only the first chunk carries the reply
reference. Retry logic extracted to _send_payload() for reuse across chunks.
Closes#898
Addresses Codex review: concurrent callers could both pass the
_mcp_connected guard and race through _connect_mcp(). Added
_mcp_connecting flag set immediately to serialize attempts.
The flag was set before the connection attempt, so if any MCP server
was temporarily unavailable, the flag stayed True and MCP tools were
permanently lost for the session.
Closes#889
Use files_upload_v2 API to upload media attachments in Slack messages.
This enables the message tool's media parameter to work correctly
when sending images or other files through the Slack channel.
Requires files:write OAuth scope.