-
Notifications
You must be signed in to change notification settings - Fork 3.6k
feat: pin TodoWriteTool above inputbar in Agent Session #12468
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
The selector was showing TodoWrite tasks from all sessions. Now it only shows todos from the current session by filtering blocks based on the topicId. Changes: - Rename selectLatestTodoWriteBlock to selectLatestTodoWriteBlockForTopic - Add topicId parameter to selector, hook, and component - Pass sessionTopicId from AgentSessionInputbar to PinnedTodoPanel Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Display all todos (completed, in_progress, pending) in the panel so users can see the full progress. Completed tasks are shown with strikethrough text and reduced opacity. Changes: - Update useActiveTodos to return all todos instead of just incomplete - Add visual styling for completed todos (strikethrough, opacity) - Add i18n translation for "completed" status Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This comment was marked as resolved.
This comment was marked as resolved.
|
Note This issue/comment/review was translated by Claude. Fixed. Original Content
修复了 |
Problem: When blocks are added via AgentMessageDataSource.updateBlocks(), the block objects are added to the `blocks` array but their IDs are NOT added to `message.blocks`. This causes: 1. Block deletion to fail (removeBlockFromMessage checks message.blocks) 2. PinnedTodoPanel close button not persisting after app restart Root cause: - AgentPersistedMessage has two related fields: - message.blocks: string[] (ID references) - blocks: MessageBlock[] (actual block data) - updateBlocks() only updates `blocks` array, not `message.blocks` Solution: 1. Data migration (runs once on app startup): - Scans all session_messages - Finds blocks in `blocks` array whose IDs are missing from `message.blocks` - Adds missing IDs to `message.blocks` - Tracked in migrations table (version 10001) to prevent re-runs 2. Fix removeBlockFromMessage(): - Now removes from both `message.blocks` AND `blocks` array - Works even if ID only exists in one of them 3. Fix PinnedTodoPanel close: - Now deletes ALL TodoWrite blocks for the session - Prevents "rollback" behavior where previous TodoWrite appears Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This comment was marked as resolved.
This comment was marked as resolved.
TodoWrite tools are always rendered in PinnedTodoPanel, making the dedicated component in MessageAgentTools dead code. Remove the component and related test cases. Amp-Thread-ID: https://ampcode.com/threads/T-019c00bf-c223-726e-9165-794500c6c934 Co-authored-by: Amp <amp@ampcode.com>
GeorgeDong32
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! The implementation is clean and the data migration strategy is sound.
One suggestion for optimization in src/renderer/src/store/messageBlock.ts:
The selectLatestTodoWriteBlockForTopic selector currently iterates over messageBlocksSelectors.selectAll (all blocks in the store). If the store grows large (many loaded sessions), this O(N) scan might become expensive.
Since we already have messageIdsByTopic, it would be more performant to iterate the topic's messages in reverse and look up their blocks.
Proposed optimization:
export const selectLatestTodoWriteBlockForTopic = createSelector(
[
(state: RootState) => state.messages.entities,
(state: RootState) => state.messageBlocks.entities,
(state: RootState) => state.messages.messageIdsByTopic,
(_state: RootState, topicId: string) => topicId
],
(messageEntities, blockEntities, messageIdsByTopic, topicId): ToolMessageBlock | undefined => {
const topicMessageIds = messageIdsByTopic[topicId]
if (!topicMessageIds?.length) return undefined
// Iterate messages in reverse (newest first)
for (let i = topicMessageIds.length - 1; i >= 0; i--) {
const messageId = topicMessageIds[i]
const message = messageEntities[messageId]
if (!message?.blocks?.length) continue
// Check blocks of the message
for (const blockId of message.blocks) {
const block = blockEntities[blockId]
// ... check if it is TodoWrite and has incomplete todos ...
if (
block?.type === MessageBlockType.TOOL &&
(block.metadata?.rawMcpToolResponse as NormalToolResponse)?.tool?.name === 'TodoWrite'
) {
// ... logic ...
return block as ToolMessageBlock
}
}
}
return undefined
}
)This ensures we only scan relevant messages/blocks and respects the message order strictly.
What this PR does
UI Enhancement: Pin TodoWriteTool above inputbar
Before this PR:
After this PR:
Bug Fix: Data migration for block reference inconsistency
Problem discovered:
When clicking the X button to close PinnedTodoPanel, the deletion was not persisted after app restart. Investigation revealed a deeper data inconsistency issue.
Root cause:
AgentPersistedMessagehas two related fields:message.blocks:string[]- ID referencesblocks:MessageBlock[]- actual block dataWhen blocks are added via
AgentMessageDataSource.updateBlocks(), the block objects are added to theblocksarray but their IDs are NOT added tomessage.blocks. This causes:removeBlockFromMessageonly checkedmessage.blocks)Solution:
Data migration (runs once on app startup):
session_messagesin agents.dbblocksarray whose IDs are missing frommessage.blocksmessage.blocksmigrationstable (version 10001) to prevent re-runsFix
removeBlockFromMessage():message.blocksANDblocksarrayFix PinnedTodoPanel close behavior:
Why we need it and why it was done in this way
The following tradeoffs were made:
The following alternatives were considered:
Breaking changes
None. This is a UI enhancement with a backward-compatible data migration that doesn't change any APIs.
Special notes for your reviewer
UI Changes
selectLatestTodoWriteBlock) to efficiently find the latest TodoWrite block with incomplete todosData Migration
DataMigrationService.ts- Manages data migrations with version trackingmigrateBlockReferences.ts- The actual migration logicmigrateBlockReferences.utils.ts- Pure utility functions (testable)migrateBlockReferences.test.ts- Unit tests{ totalMessages, messagesFixed, blockReferencesAdded, errors }Checklist
Release note