diff --git a/agent/prompt_builder.py b/agent/prompt_builder.py index 765743c60f4..4ae71cbb3a3 100644 --- a/agent/prompt_builder.py +++ b/agent/prompt_builder.py @@ -805,26 +805,19 @@ PLATFORM_HINTS = { ), "matrix": ( "You are in a Matrix room communicating with your user. " - "The adapter converts your Markdown to HTML for rich display.\n\n" - "CRITICAL FORMATTING RULES (Matrix renders Markdown inconsistently):\n\n" - "Supported and safe: **bold**, *italic*, `inline code`, ``` code blocks, " - "- bullet lists, [links](url), > blockquote, labeled **Label:** value pairs, " - "and key: value pairs.\n\n" - "Do NOT use (they render incorrectly or not at all):\n" - "- Tables — become one continuous line; use labeled **Label:** value pairs\n" - "- Numbered lists — collapse to bullets; use - bullets\n" - "- Horizontal rules (---) — invisible\n" - "- ||spoiler|| tags — no visual effect\n" - "- ~strikethrough~ — no visual effect\n" - "- Headings (# ## ###) — no visual effect\n" - "- Checkboxes (- [ ] / - [x]) — render as plain ASCII\n\n" - "LINEBREAKS ARE CRITICAL: A single newline does NOT create a new line — " - "adjacent lines merge together. Use TWO trailing spaces at the end of a " - "line for a soft linebreak. Use a blank line between paragraphs. " - "Without two trailing spaces or a blank line, everything runs together.\n\n" - "LINKS: Always use [descriptive link text](url) — never show bare URLs. " - "When referencing something with an associated URL (event names, venues, " - "people, sources), make the name a clickable link.\n\n" + "The adapter converts your Markdown to HTML for rich display — bold, " + "italic, inline code, fenced code blocks, headings, bullet and " + "numbered lists, blockquotes, and links all render.\n\n" + "Do NOT use Markdown tables: many popular Matrix clients (Element X, " + "Beeper, most mobile apps) do not render HTML tables, so the cells " + "collapse into one continuous run of text. Present tabular data as " + "labeled '**Label:** value' lines or bullet lists instead.\n\n" + "Avoid ||spoiler|| tags, ~~strikethrough~~, and checkboxes " + "(- [ ] / - [x]) — they are not converted and appear as literal " + "characters.\n\n" + "LINKS: prefer [descriptive link text](url) over bare URLs. When " + "referencing something with an associated URL (events, sources, " + "people), make the name a clickable link.\n\n" "You can send media files natively: include MEDIA:/absolute/path/to/file " "in your response. Images (.jpg, .png, .webp) are sent as inline photos, " "audio (.ogg, .mp3) as voice/audio messages, video (.mp4) inline, " diff --git a/contributors/emails/ryan.kelln@gmail.com b/contributors/emails/ryan.kelln@gmail.com new file mode 100644 index 00000000000..e0d10922e2b --- /dev/null +++ b/contributors/emails/ryan.kelln@gmail.com @@ -0,0 +1 @@ +RKelln diff --git a/tests/agent/test_prompt_builder.py b/tests/agent/test_prompt_builder.py index a0acdea0433..633fde7c02d 100644 --- a/tests/agent/test_prompt_builder.py +++ b/tests/agent/test_prompt_builder.py @@ -1169,6 +1169,11 @@ class TestPromptBuilderConstants: assert "Matrix" in hint assert "MEDIA:" in hint assert "Markdown" in hint + # Regression (#52552): the hint must steer models away from Markdown + # tables — popular Matrix clients don't render HTML tables and the + # cells collapse into one continuous line. + assert "table" in hint.lower() + assert "Do NOT use Markdown tables" in hint def test_platform_hints_feishu(self): hint = PLATFORM_HINTS["feishu"] diff --git a/tests/gateway/test_matrix.py b/tests/gateway/test_matrix.py index d239728b794..75bb826332e 100644 --- a/tests/gateway/test_matrix.py +++ b/tests/gateway/test_matrix.py @@ -1033,7 +1033,10 @@ class TestMatrixRenderingPayloads: @pytest.mark.asyncio async def test_long_response_split_preserves_thread_context(self): - long_text = "Intro\n```python\n" + ("print('hello')\n" * 500) + "```\nDone" + # Build a payload guaranteed to exceed the adapter's outbound chunk + # size (configurable since #53026) so send() must split it. + repeats = (self.adapter.max_message_length // 15) + 200 + long_text = "Intro\n```python\n" + ("print('hello')\n" * repeats) + "```\nDone" result = await self.adapter.send( "!room:example.org", diff --git a/website/docs/user-guide/messaging/matrix.md b/website/docs/user-guide/messaging/matrix.md index 75babf5cbab..8dc73b1643c 100644 --- a/website/docs/user-guide/messaging/matrix.md +++ b/website/docs/user-guide/messaging/matrix.md @@ -97,6 +97,7 @@ matrix: session_scope: room # auto|room|thread; room is recommended for project rooms auto_thread: true # Auto-create threads for responses (default: true) dm_mention_threads: false # Create thread when @mentioned in DM (default: false) + max_message_length: 16000 # Outbound chunk size in chars (default: 16000, max: 65535) ``` Or via environment variables: