In project-based sidebar grouping, the "Chats" section (non-project
conversations) was always appended at the end regardless of its most
recent updated_at. This meant the newest conversation could appear
below older project groups.
Move Chats group insertion before the global sort, compute its
updatedAt from its most recently updated session, and sort all groups
together by updatedAt descending.
Add a new config option group_user_isolation (default: false) to the
DingTalk channel. When enabled, each user in a group chat gets their own
session while bot replies are still routed to the shared group chat.
The previous fix made retain_recent_legal_suffix return the actual dropped
message list, but already_consolidated was still computed with
min(before_last_consolidated, len(dropped)), which assumes dropped messages
are always a prefix. In the else branch (tail has no user messages), dropped
may include messages from after the consolidated prefix, causing
already_consolidated to skip too many and leaving tail messages neither
retained nor raw-archived.
Fix by having retain_recent_legal_suffix return (dropped,
already_consolidated_count) where already_consolidated_count is computed
against original message indices. Also fix last_consolidated update to count
how many retained messages were inside the old consolidated prefix.
When retain_recent_legal_suffix hits the else branch (tail has no user
messages), it takes a non-contiguous slice from the middle of the session.
enforce_file_cap incorrectly assumed dropped messages were always a prefix
(before[:dropped_count]), causing user messages to be both archived and
retained, and some messages to silently disappear.
Fix by having retain_recent_legal_suffix return the actual dropped message
list using identity-based diff, so enforce_file_cap no longer needs to
guess which messages were removed.
When fenced code blocks have no language specifier, react-syntax-highlighter
receives undefined for the language prop, causing a white screen crash.
- CodeBlock.tsx: fallback to 'text' when language is undefined
- MarkdownTextRenderer.tsx: defensive fallback at fence rendering site
- Added test cases for both components
Fixes#4116
- Remove ## Completed section from HEARTBEAT.md template; completed
tasks should be deleted, not accumulated
- Change in_active_section from tri-state (None/True/False) to bool
(True/False) so stray text before any ## heading no longer triggers
heartbeat
- Add test cases for stray pre-heading text and ## Notes section
- Update docs/chat-commands.md to reference ## Active Tasks
- Apply _normalize_addr in _is_allowed_loopback_target so
::ffff:127.0.0.1 is correctly identified as loopback
- Add test for contains_internal_url with IPv6-mapped addresses
- Add test for whitelist + IPv6-mapped CGNAT interaction
::ffff:127.0.0.1 and ::ffff:169.254.169.254 are IPv6Address objects
that match neither the IPv4 blocklists (127.0.0.0/8, 169.254.0.0/16)
nor the IPv6 ones (::1/128), allowing SSRF bypass via DNS responses
that return IPv6-mapped IPv4 addresses.
Add _normalize_addr() to convert ipv4_mapped IPv6 addresses to their
IPv4 form before blocklist/allowlist matching.