Based on my analysis of the issues and the codebase, here is a comprehensive plan to introduce a Message Transformation Layer. This "XSLT-like" layer will strictly separate the raw message content from its presentation, allowing us to consistently handle specific tags and format requirements across the application.
1. Analysis & Strategy
The core issue is that UserMessage and AssistantMessage currently render textContent directly into a Markdown component. We need a processing pipeline that intercepts the content before rendering to:
- Strip internal protocol tags (Issues #70, #73).
- Transform structured tags into rich UI components (Issue #65).
Strategy: Implement a useMessageTransforms hook that runs a pipeline of "Transformers". Each transformer takes a string or existing render parts and returns a modified list of parts (strings or React Nodes).
2. Architecture: The Transformation Pipeline
We will introduce a new module at frontend/lib/message-transforms.
Directory Structure
text
Core Logic (useMessageTransforms)
The hook will implement a "split-and-replace" algorithm using useMemo for performance.
- Input:
message.textContent (String).
- Initial State:
[message.textContent].
- Process: Iterate through registered transformers.
- Each transformer scans string parts using Regex.
- Matches are replaced with React Components (e.g.,
<CommandBlock />) or removed (replaced with empty strings).
- Unmatched text remains as strings.
- Output:
(String | ReactNode)[].
Rendering:
Components will map over the output:
- Strings -> Wrapped in
<Markdown />.
- ReactNodes -> Rendered directly.
3. Specific Fixes
| Issue | Problem | Transformer Solution |
|---|
| #70 | Codex leaks <environment_context> | StripEnvironmentContext: Regex match /<environment_context>[\s\S]*?<\/environment_context>/ and replace with "". If the resulting message is empty, the UI handles it gracefully. |
| #73 | Agent Teams leaks <teammate-messages> | StripTeammateMessages: Regex match /<teammate-messages>[\s\S]*?<\/teammate-messages>/ and replace with "". |
| #65 | Claude <command-...> tags | RenderCommandTags: Regex match /<(command-[-\w]+)(?:\s+[^>]*)?>([\s\S]*?)<\/\1>/. Transform into a new <CommandBadge> or <CodeSnippet> component to distinguish it from user text. |
4. Implementation Steps
-
Setup Worktree & Structure:
- Create worktree
fix/message-rendering.
- Create
frontend/lib/message-transforms structure.
-
Implement Core Logic:
- Define
MessageTransformer type.
- Implement
applyTransformers utility and useMessageTransforms hook.
-
Implement Transformers:
- Write regex-based transformers for
#70 and #73 (Stripping).
- Write component-based transformer for
#65 (Rendering).
- Create a simple UI component for the command tags (e.g., a styled badge or code block).
-
Integrate:
- Modify
frontend/components/messages/UserMessage.tsx to use the hook.
- Modify
frontend/components/messages/AssistantMessage.tsx (for consistency).
-
Verification:
- Verify that tags from issues #70 and #73 are gone.
- Verify that command tags from #65 are rendered distinctively.
- Ensure standard Markdown rendering still works and performance is not impacted.
This plan is FAST (regex-based, memoized), Generic (easy to add new tags later), and Consistent (applied centrally).
User Approval:
Shall I proceed with creating the worktree and implementing this plan?