diff --git a/docs/tools/todos.md b/docs/tools/todos.md index abb44c0927..d198b872ea 100644 --- a/docs/tools/todos.md +++ b/docs/tools/todos.md @@ -13,7 +13,8 @@ updates to the CLI interface. - `todos` (array of objects, required): The complete list of tasks. Each object includes: - `description` (string): Technical description of the task. - - `status` (enum): `pending`, `in_progress`, `completed`, or `cancelled`. + - `status` (enum): `pending`, `in_progress`, `completed`, `cancelled`, or + `blocked`. ## Technical behavior diff --git a/packages/cli/src/ui/components/ChecklistItem.test.tsx b/packages/cli/src/ui/components/ChecklistItem.test.tsx index 0f6c0eb0b0..4176f7914b 100644 --- a/packages/cli/src/ui/components/ChecklistItem.test.tsx +++ b/packages/cli/src/ui/components/ChecklistItem.test.tsx @@ -15,6 +15,7 @@ describe('', () => { { status: 'in_progress', label: 'Doing this' }, { status: 'completed', label: 'Done this' }, { status: 'cancelled', label: 'Skipped this' }, + { status: 'blocked', label: 'Blocked this' }, ] as ChecklistItemData[])('renders %s item correctly', async (item) => { const { lastFrame, waitUntilReady } = render(); await waitUntilReady(); diff --git a/packages/cli/src/ui/components/ChecklistItem.tsx b/packages/cli/src/ui/components/ChecklistItem.tsx index 6e08e0af6b..065c79d516 100644 --- a/packages/cli/src/ui/components/ChecklistItem.tsx +++ b/packages/cli/src/ui/components/ChecklistItem.tsx @@ -13,7 +13,8 @@ export type ChecklistStatus = | 'pending' | 'in_progress' | 'completed' - | 'cancelled'; + | 'cancelled' + | 'blocked'; export interface ChecklistItemData { status: ChecklistStatus; @@ -48,6 +49,12 @@ const ChecklistStatusDisplay: React.FC<{ status: ChecklistStatus }> = ({ ✗ ); + case 'blocked': + return ( + + ⛔ + + ); default: checkExhaustive(status); } @@ -70,6 +77,7 @@ export const ChecklistItem: React.FC = ({ return theme.text.accent; case 'completed': case 'cancelled': + case 'blocked': return theme.text.secondary; case 'pending': return theme.text.primary; diff --git a/packages/cli/src/ui/components/__snapshots__/ChecklistItem.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/ChecklistItem.test.tsx.snap index 9cd5fbb64c..80599ae878 100644 --- a/packages/cli/src/ui/components/__snapshots__/ChecklistItem.test.tsx.snap +++ b/packages/cli/src/ui/components/__snapshots__/ChecklistItem.test.tsx.snap @@ -1,5 +1,10 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[` > renders { status: 'blocked', label: 'Blocked this' } item correctly 1`] = ` +"⛔ Blocked this +" +`; + exports[` > renders { status: 'cancelled', label: 'Skipped this' } item correctly 1`] = ` "✗ Skipped this " diff --git a/packages/core/src/services/trackerTypes.ts b/packages/core/src/services/trackerTypes.ts index d0e94bb986..6c21456fe1 100644 --- a/packages/core/src/services/trackerTypes.ts +++ b/packages/core/src/services/trackerTypes.ts @@ -22,6 +22,7 @@ export const TASK_TYPE_LABELS: Record = { export enum TaskStatus { OPEN = 'open', IN_PROGRESS = 'in_progress', + BLOCKED = 'blocked', CLOSED = 'closed', } export const TaskStatusSchema = z.nativeEnum(TaskStatus); diff --git a/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap b/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap index e3a80eddd7..e2bab4d050 100644 --- a/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap +++ b/packages/core/src/tools/definitions/__snapshots__/coreToolsModelSnapshots.test.ts.snap @@ -697,6 +697,7 @@ DO NOT use this tool for simple tasks that can be completed in less than 2 steps - in_progress: Marked just prior to beginning work on a given subtask. You should only have one subtask as in_progress at a time. - completed: Subtask was successfully completed with no errors or issues. If the subtask required more steps to complete, update the todo list with the subtasks. All steps should be identified as completed only when they are completed. - cancelled: As you update the todo list, some tasks are not required anymore due to the dynamic nature of the task. In this case, mark the subtasks as cancelled. +- blocked: Subtask is blocked and cannot be completed at this time. ## Methodology for using this tool @@ -766,6 +767,7 @@ The agent did not use the todo list because this task could be completed by a ti "in_progress", "completed", "cancelled", + "blocked", ], "type": "string", }, @@ -1451,6 +1453,7 @@ DO NOT use this tool for simple tasks that can be completed in less than 2 steps - in_progress: Marked just prior to beginning work on a given subtask. You should only have one subtask as in_progress at a time. - completed: Subtask was successfully completed with no errors or issues. If the subtask required more steps to complete, update the todo list with the subtasks. All steps should be identified as completed only when they are completed. - cancelled: As you update the todo list, some tasks are not required anymore due to the dynamic nature of the task. In this case, mark the subtasks as cancelled. +- blocked: Subtask is blocked and cannot be completed at this time. ## Methodology for using this tool @@ -1520,6 +1523,7 @@ The agent did not use the todo list because this task could be completed by a ti "in_progress", "completed", "cancelled", + "blocked", ], "type": "string", }, diff --git a/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts b/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts index 3309fcc5ba..5c219f4685 100644 --- a/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts +++ b/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts @@ -543,6 +543,7 @@ DO NOT use this tool for simple tasks that can be completed in less than 2 steps - in_progress: Marked just prior to beginning work on a given subtask. You should only have one subtask as in_progress at a time. - completed: Subtask was successfully completed with no errors or issues. If the subtask required more steps to complete, update the todo list with the subtasks. All steps should be identified as completed only when they are completed. - cancelled: As you update the todo list, some tasks are not required anymore due to the dynamic nature of the task. In this case, mark the subtasks as cancelled. +- blocked: Subtask is blocked and cannot be completed at this time. ## Methodology for using this tool @@ -609,7 +610,13 @@ The agent did not use the todo list because this task could be completed by a ti [TODOS_ITEM_PARAM_STATUS]: { type: 'string', description: 'The current status of the task.', - enum: ['pending', 'in_progress', 'completed', 'cancelled'], + enum: [ + 'pending', + 'in_progress', + 'completed', + 'cancelled', + 'blocked', + ], }, }, required: [TODOS_ITEM_PARAM_DESCRIPTION, TODOS_ITEM_PARAM_STATUS], diff --git a/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts b/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts index 2c0375baa3..cac98a90b3 100644 --- a/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts +++ b/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts @@ -518,6 +518,7 @@ DO NOT use this tool for simple tasks that can be completed in less than 2 steps - in_progress: Marked just prior to beginning work on a given subtask. You should only have one subtask as in_progress at a time. - completed: Subtask was successfully completed with no errors or issues. If the subtask required more steps to complete, update the todo list with the subtasks. All steps should be identified as completed only when they are completed. - cancelled: As you update the todo list, some tasks are not required anymore due to the dynamic nature of the task. In this case, mark the subtasks as cancelled. +- blocked: Subtask is blocked and cannot be completed at this time. ## Methodology for using this tool @@ -584,7 +585,13 @@ The agent did not use the todo list because this task could be completed by a ti [TODOS_ITEM_PARAM_STATUS]: { type: 'string', description: 'The current status of the task.', - enum: ['pending', 'in_progress', 'completed', 'cancelled'], + enum: [ + 'pending', + 'in_progress', + 'completed', + 'cancelled', + 'blocked', + ], }, }, required: [TODOS_ITEM_PARAM_DESCRIPTION, TODOS_ITEM_PARAM_STATUS], diff --git a/packages/core/src/tools/tools.ts b/packages/core/src/tools/tools.ts index c94cef4a92..3865aaf357 100644 --- a/packages/core/src/tools/tools.ts +++ b/packages/core/src/tools/tools.ts @@ -823,7 +823,12 @@ export type ToolResultDisplay = | TodoList | SubagentProgress; -export type TodoStatus = 'pending' | 'in_progress' | 'completed' | 'cancelled'; +export type TodoStatus = + | 'pending' + | 'in_progress' + | 'completed' + | 'cancelled' + | 'blocked'; export interface Todo { description: string; diff --git a/packages/core/src/tools/trackerTools.test.ts b/packages/core/src/tools/trackerTools.test.ts index 8236dba3a1..6513a71dd5 100644 --- a/packages/core/src/tools/trackerTools.test.ts +++ b/packages/core/src/tools/trackerTools.test.ts @@ -222,15 +222,23 @@ describe('Tracker Tools Integration', () => { status: TaskStatus.IN_PROGRESS, dependencies: [], }; + const t4 = { + id: 't4', + title: 'T4', + type: TaskType.TASK, + status: TaskStatus.BLOCKED, + dependencies: [], + }; const mockService = { - listTasks: async () => [t1, t2, t3], + listTasks: async () => [t1, t2, t3, t4], } as unknown as TrackerService; const display = await buildTodosReturnDisplay(mockService); expect(display.todos).toEqual([ { description: `task: T3 (t3)`, status: 'in_progress' }, { description: `task: T2 (t2)`, status: 'pending' }, + { description: `task: T4 (t4)`, status: 'blocked' }, { description: `task: T1 (t1)`, status: 'completed' }, ]); }); diff --git a/packages/core/src/tools/trackerTools.ts b/packages/core/src/tools/trackerTools.ts index 18f3ccc3cc..1594cceca8 100644 --- a/packages/core/src/tools/trackerTools.ts +++ b/packages/core/src/tools/trackerTools.ts @@ -48,10 +48,11 @@ export async function buildTodosReturnDisplay( } } - const statusOrder = { + const statusOrder: Record = { [TaskStatus.IN_PROGRESS]: 0, [TaskStatus.OPEN]: 1, - [TaskStatus.CLOSED]: 2, + [TaskStatus.BLOCKED]: 2, + [TaskStatus.CLOSED]: 3, }; const sortTasks = (a: TrackerTask, b: TrackerTask) => { @@ -80,6 +81,8 @@ export async function buildTodosReturnDisplay( status = 'in_progress'; } else if (task.status === TaskStatus.CLOSED) { status = 'completed'; + } else if (task.status === TaskStatus.BLOCKED) { + status = 'blocked'; } const indent = ' '.repeat(depth); @@ -585,6 +588,7 @@ class TrackerVisualizeInvocation extends BaseToolInvocation< const statusEmojis: Record = { open: '⭕', in_progress: '🚧', + blocked: '⛔', closed: '✅', }; diff --git a/packages/core/src/tools/write-todos.test.ts b/packages/core/src/tools/write-todos.test.ts index 117a3d2681..47ce8c2b6e 100644 --- a/packages/core/src/tools/write-todos.test.ts +++ b/packages/core/src/tools/write-todos.test.ts @@ -19,6 +19,7 @@ describe('WriteTodosTool', () => { { description: 'Task 1', status: 'pending' }, { description: 'Task 2', status: 'in_progress' }, { description: 'Task 3', status: 'completed' }, + { description: 'Task 4', status: 'blocked' }, ], }; await expect(tool.buildAndExecute(params, signal)).resolves.toBeDefined(); @@ -96,13 +97,15 @@ describe('WriteTodosTool', () => { { description: 'First task', status: 'completed' }, { description: 'Second task', status: 'in_progress' }, { description: 'Third task', status: 'pending' }, + { description: 'Fourth task', status: 'blocked' }, ], }; const result = await tool.buildAndExecute(params, signal); const expectedOutput = `Successfully updated the todo list. The current list is now: 1. [completed] First task 2. [in_progress] Second task -3. [pending] Third task`; +3. [pending] Third task +4. [blocked] Fourth task`; expect(result.llmContent).toBe(expectedOutput); expect(result.returnDisplay).toEqual(params); }); diff --git a/packages/core/src/tools/write-todos.ts b/packages/core/src/tools/write-todos.ts index dd7ab780e6..746219ecd7 100644 --- a/packages/core/src/tools/write-todos.ts +++ b/packages/core/src/tools/write-todos.ts @@ -22,6 +22,7 @@ const TODO_STATUSES = [ 'in_progress', 'completed', 'cancelled', + 'blocked', ] as const; export interface WriteTodosToolParams {