Skip to content

Conversation

@ammar-agent
Copy link
Collaborator

Refactors TODO display to pin at bottom of chat (before StreamingBarrier) with state managed by Aggregator.

Changes

Shared TODO List Component:

  • Extract TODO items rendering from TodoToolCall
  • Reusable for both pinned display and tool call history

Backend - Aggregator:

  • Track current TODO state in StreamAggregator
  • Expose via IPC for efficient rendering
  • Automatic cleanup when stream ends

Frontend - WorkspaceStore:

  • Cache TODO state by stream token
  • Subscribe to Aggregator updates
  • Filter TODOs by current stream

UI:

  • PinnedTodoList at bottom of chat (before StreamingBarrier)
  • TodoToolCall collapsed by default, uses shared component when expanded
  • Only shows TODOs from active stream

Generated with cmux

- Add TodoWrite and TodoRead tools for managing task lists during operations
- Schema includes content, status (pending/in_progress/completed), and activeForm fields
- In-memory storage with workspace isolation (ephemeral, conversation-scoped)
- UI component displays checklist with visual indicators (✓/⏳/○)
- Full-replacement semantics (replaces entire list on each call)
- Unit tests for storage layer

Generated with `cmux`
Changes:
- Replace in-memory Map storage with filesystem storage in stream's tmpdir
- TODOs are now automatically cleaned up when stream ends
- Each stream has isolated TODO storage (no cross-stream pollution)
- Test helper functions updated to use tempDir instead of workspaceId

Benefits:
- Fixes memory leaks (TODOs no longer survive across conversations)
- Proper stream isolation (concurrent streams don't share TODOs)
- Automatic lifecycle management via streamManager cleanup
- TODOs are inspectable during debugging (stored as todos.json)

_Generated with `cmux`_
Enforce ordering rules for todo_write tool:
- TODOs must be ordered: pending → in_progress → completed
- Only one task can be in_progress at a time
- Empty lists are allowed (all completed or nothing to do)

Changes:
- Added validateTodos() function with state machine validation
- writeTodos() now validates before persisting to disk
- Updated all tests to use valid todo ordering
- Added 4 new test cases for validation rules

Benefits:
- Prevents model from creating invalid TODO states
- Clear error messages guide model to fix ordering
- Validation happens before filesystem write (atomic)

_Generated with `cmux`_
Remove invalid type parameters from jest.fn() mock - Jest's Mock type
only accepts 0-1 type arguments in TypeScript, not the tuple syntax
used previously.

_Generated with `cmux`_
- Reduced vertical spacing: gap 8px→3px, padding 8px→4px
- Tighter line-height: 1.5→1.35
- Smaller font size: 12px→11px (activeForm 10px)
- Thinner border-left: 3px→2px
- Subtler backgrounds (92%/96% transparency vs 90%/95%)
- Added opacity to completed items for visual hierarchy
- Refined icon and text sizes for density
- In-progress tasks now display activeForm instead of content
- Added CSS animation for ellipsis (...) on activeForm
- Animation cycles through '', '.', '..', '...' every 1.5s
- Increased activeForm font size to 11px (matches other text)
- Removed margin-top since activeForm is now the primary text
- Changed animation from content change to width animation
- Uses overflow: hidden with steps(4, end) for smooth dots reveal
- Fixed width prevents layout shifts and line breaks
- Ellipsis animates from 0 width to 1em, showing dots progressively
Changes:
- Validation now enforces completed first, in_progress middle, pending last
- Updated all test fixtures to match new order
- Updated tool description to emphasize using TODOs for all complex multi-step operations
- Fixed ellipsis animation to prevent line breaks (width animation with overflow:hidden)
- In-progress tasks now show only activeForm (not content) with animated ellipsis

This creates a more natural progress view: what's done → what's happening → what's next
- Use TooltipWrapper pattern for emoji display (matches other tools)
- Removed ToolName component in favor of tooltip
- Added instruction to mark all todos completed before finishing stream
- Ensures consistency with bash, file_read, etc. tool displays
- Created TodoList.tsx with all styling and rendering logic
- TodoToolCall now uses shared component
- Changed default expansion to false (collapsed by default)
- Keeps historical record when expanded
- Prepares for PinnedTodoList component reuse
- Track current TODOs in Aggregator when todo_write succeeds
- Clear TODOs when stream ends, aborts, or errors
- Add getCurrentTodos() getter for efficient access
- Single source of truth for TODO state
- Automatic cleanup on stream completion
- Simple passthrough to Aggregator.getCurrentTodos()
- Returns empty array if workspace doesn't exist
- Efficient O(1) lookup by workspace ID
- No caching needed since Aggregator manages state
- Reuses TodoList component for rendering
- Subscribes to WorkspaceStore for reactivity
- Shows only when TODOs exist
- Styled with border and max-height
- Ready to be positioned in chat
- Added to AIView.tsx right before StreamingBarrier
- Shows at bottom of message list
- Only renders when TODOs exist for current workspace
- Automatically updates when TODOs change via WorkspaceStore subscription
Resolved conflict in TodoToolCall.tsx by keeping refactored version that uses shared TodoList component.
- Remove unused workspaceState variable in PinnedTodoList
- Add proper TodoItem type imports instead of inline import()
- Use TodoItem[] for simple array types (not Array<TodoItem>)
- Fix formatting with prettier
Instead of duplicating 'this.currentTodos = []' and 'this.activeStreams.delete()'
in handleStreamEnd, handleStreamAbort, and handleStreamError, extract to a
single private method.

Benefits:
- DRY: Single source of truth for stream cleanup
- Maintainability: Adding new stream-scoped state only requires updating one place
- Clarity: Explicit method name documents intent
- Small, subtle header above the pinned TODO list
- Uses monospace font at 10px with secondary text color
- Letterspacing for visual clarity
- Helps identify the component in the chat view
Use 1px dashed border with hsl(0deg 0% 28.64%) for a more subtle,
distinguished look that separates the TODO list from the chat.
Schema changes:
- Removed activeForm field from TodoItem type
- Updated tool description to use tense-based content:
  - Past tense for completed ('Added tests')
  - Present progressive for in_progress ('Adding tests')
  - Imperative for pending ('Add tests')
- Saves tokens by using single field with context-appropriate tense

UI improvements:
- Made pinned TODO list collapsible with animated caret
- Colon disappears when collapsed ('TODO:' → 'TODO')
- Collapse state persisted globally with usePersistedState
- Blue color and ellipsis animation for in_progress items
- Updated TodoList component to handle all statuses with single field

Tests:
- Removed all activeForm references from tests
- All 11 tests still pass
Changed 'before finishing the stream' to 'before finishing your response'
for better clarity from the AI's perspective.
@ammario ammario marked this pull request as ready for review October 15, 2025 01:04
@ammario ammario merged commit 43a47d1 into main Oct 15, 2025
7 checks passed
@ammario ammario deleted the pinned-todos branch October 15, 2025 01:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants