feat: add reply-to message threading#30
Merged
Killea merged 4 commits intoKillea:mainfrom Mar 3, 2026
Merged
Conversation
Add optional reply_to_msg_id field to messages, allowing any message to reference a parent message in the same thread. Changes: - models.py: add reply_to_msg_id: Optional[str] = None to Message dataclass - database.py: additive migration ADD COLUMN reply_to_msg_id TEXT + index - crud.py: _row_to_message() fallback for older DBs, new msg_get() function, msg_post() validation (parent must exist in same thread), SSE msg.reply event - mcp_server.py: reply_to_msg_id property in msg_post inputSchema - dispatch.py: handle_msg_post passes/returns reply_to_msg_id, handle_msg_list includes reply_to_msg_id, ValueError caught and returned as structured error - main.py: MessageCreate.reply_to_msg_id, POST/GET endpoints include field - index.html + main.css: visual reply-quote block (.msg-reply-quote) - tests/test_reply_threading.py: 18 tests (12 unit + 6 integration) - tests/conftest.py: compatibility check updated for UP-14 Backward compatible: reply_to_msg_id defaults to null, existing calls unchanged. Existing DBs are handled by a safe column fallback in _row_to_message().
8e2e610 to
72d5b66
Compare
The function was present in the feat/metrics-endpoint branch (PR Killea#27) but was not included when feat/reply-threading was created. Adding it back so that test_metrics.py passes in CI.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Messages in AgentChatBus are purely sequential (identified by
seqnumber only). There is no way for a message to explicitly reference a parent message, making it impossible to model reply chains, threaded discussions, or contextual annotations within a thread.This is a universal messaging primitive present in all major platforms (Slack, Discord, GitHub comments, email threads).
Solution
Add an optional
reply_to_msg_idfield to theMessagemodel. When set, it references a parent message that must exist in the same thread. The field is stored as a dedicated column (not in metadata) for queryability.Changes
src/db/models.py:reply_to_msg_id: Optional[str] = Noneadded toMessagedataclasssrc/db/database.py: additive migrationALTER TABLE messages ADD COLUMN reply_to_msg_id TEXT+ indexidx_messages_replysrc/db/crud.py: safe fallback in_row_to_message()for older DBs; newmsg_get()function;msg_post()validates parent exists and belongs to the same thread; emitsmsg.replySSE event whenreply_to_msg_idis setsrc/mcp_server.py:reply_to_msg_idproperty added tomsg_postinputSchemasrc/tools/dispatch.py:handle_msg_post()passes/returnsreply_to_msg_id;handle_msg_list()includes field;ValueErrorfrom validation returned as structured errorsrc/main.py:MessageCreate.reply_to_msg_id; POST response and GET message list include the fieldsrc/static/index.html+src/static/css/main.css: visual reply-quote block (.msg-reply-quote) shown above message content whenreply_to_msg_idis presenttests/test_reply_threading.py: 18 tests (12 unit with in-memory DB + 6 integration against real server)Backward Compatibility
reply_to_msg_iddefaults tonull— all existingmsg_postcalls are unchanged_row_to_message()Validation Rules
reply_to_msg_idmust reference an existing message ID in the same threadValueError(HTTP 400 via REST, structured error via MCP)ValueError(HTTP 400)SSE Event
A new
msg.replyevent is emitted when a reply is posted:{ "type": "msg.reply", "payload": { "msg_id": "...", "reply_to_msg_id": "...", "thread_id": "...", "author": "agent-name", "seq": 5 } }