( messages: Message[] | undefined, toolUseContext: ToolUseContext, )
| 3264 | } |
| 3265 | |
| 3266 | async function getTodoReminderAttachments( |
| 3267 | messages: Message[] | undefined, |
| 3268 | toolUseContext: ToolUseContext, |
| 3269 | ): Promise<Attachment[]> { |
| 3270 | // Skip if TodoWrite tool is not available |
| 3271 | if ( |
| 3272 | !toolUseContext.options.tools.some(t => |
| 3273 | toolMatchesName(t, TODO_WRITE_TOOL_NAME), |
| 3274 | ) |
| 3275 | ) { |
| 3276 | return [] |
| 3277 | } |
| 3278 | |
| 3279 | // When SendUserMessage is in the toolkit, it's the primary communication |
| 3280 | // channel and the model is always told to use it (#20467). TodoWrite |
| 3281 | // becomes a side channel — nudging the model about it conflicts with the |
| 3282 | // brief workflow. The tool itself stays available; this only gates the |
| 3283 | // "you haven't used it in a while" nag. |
| 3284 | if ( |
| 3285 | BRIEF_TOOL_NAME && |
| 3286 | toolUseContext.options.tools.some(t => toolMatchesName(t, BRIEF_TOOL_NAME)) |
| 3287 | ) { |
| 3288 | return [] |
| 3289 | } |
| 3290 | |
| 3291 | // Skip if no messages provided |
| 3292 | if (!messages || messages.length === 0) { |
| 3293 | return [] |
| 3294 | } |
| 3295 | |
| 3296 | const { turnsSinceLastTodoWrite, turnsSinceLastReminder } = |
| 3297 | getTodoReminderTurnCounts(messages) |
| 3298 | |
| 3299 | // Check if we should show a reminder |
| 3300 | if ( |
| 3301 | turnsSinceLastTodoWrite >= TODO_REMINDER_CONFIG.TURNS_SINCE_WRITE && |
| 3302 | turnsSinceLastReminder >= TODO_REMINDER_CONFIG.TURNS_BETWEEN_REMINDERS |
| 3303 | ) { |
| 3304 | const todoKey = toolUseContext.agentId ?? getSessionId() |
| 3305 | const appState = toolUseContext.getAppState() |
| 3306 | const todos = appState.todos[todoKey] ?? [] |
| 3307 | return [ |
| 3308 | { |
| 3309 | type: 'todo_reminder', |
| 3310 | content: todos, |
| 3311 | itemCount: todos.length, |
| 3312 | }, |
| 3313 | ] |
| 3314 | } |
| 3315 | |
| 3316 | return [] |
| 3317 | } |
| 3318 | |
| 3319 | function getTaskReminderTurnCounts(messages: Message[]): { |
| 3320 | turnsSinceLastTaskManagement: number |
no test coverage detected