Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/agent-core/src/profile/default/system.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ The user may ask you to research on certain topics, process or generate certain
- Understand the user's requirements thoroughly, ask for clarification before you start if needed.
- Make plans before doing deep or wide research, to ensure you are always on track.
- Search on the Internet if possible, with carefully-designed search queries to improve efficiency and accuracy.
- Use proper tools or shell commands or Python packages to process or generate images, videos, PDFs, docs, spreadsheets, presentations, or other multimedia files. Detect if there are already such tools in the environment. If you have to install third-party tools/packages, you MUST ensure that they are installed in a virtual/isolated environment.
- Use proper tools or shell commands or Python packages to process or generate images, PDFs, docs, spreadsheets, presentations, or other multimedia files. **For video files, prefer using the `ReadMediaFile` tool to read them directly rather than writing Python scripts or ffmpeg commands to extract frames manually.** Detect if there are already such tools in the environment. If you have to install third-party tools/packages, you MUST ensure that they are installed in a virtual/isolated environment.
- Once you generate or edit any images, videos or other media files, try to read it again before proceed, to ensure that the content is as expected.
- Avoid installing or deleting anything to/from outside of the current working directory. If you have to do so, ask the user for confirmation.

Expand Down
3 changes: 2 additions & 1 deletion packages/agent-core/src/tools/builtin/state/todo-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ Use this tool to maintain a structured TODO list as you work through a multi-ste
- If no available tool can move any task forward, tell the user where you are stuck instead of repeatedly re-ordering the same todos.

**How to use:**
- Call with `todos: [...]` to replace the full list. Statuses: pending / in_progress / done.
- Call with `todos: [...]` to replace the full list. Statuses: `pending` / `in_progress` / `done`.
- Call with no `todos` argument to retrieve the current list without changing it.
- Call with `todos: []` to clear the list.
- **Important:** the status must be exactly `done`, not `completed` or `finished`.
- Keep titles short and actionable (e.g. "Read session-control.ts", "Add planMode flag to TurnManager").
- Update statuses as you make progress.
- When work is underway, keep exactly one task `in_progress`.
Expand Down
12 changes: 9 additions & 3 deletions packages/agent-core/src/tools/builtin/state/todo-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const TODO_STORE_KEY = 'todo';
const TODO_LIST_WRITE_REMINDER =
'Ensure that you continue to use the todo list to track progress. Mark tasks done immediately after finishing them, and keep exactly one task in_progress when work is underway.';

export type TodoStatus = 'pending' | 'in_progress' | 'done';
export type TodoStatus = 'pending' | 'in_progress' | 'done' | 'completed';

export interface TodoItem {
readonly title: string;
Expand All @@ -45,7 +45,9 @@ declare module '../../store' {

const TodoItemSchema = z.object({
title: z.string().min(1).describe('Short, actionable title for the todo.'),
status: z.enum(['pending', 'in_progress', 'done']).describe('Current status of the todo.'),
status: z
.preprocess((val) => (val === 'completed' ? 'done' : val), z.enum(['pending', 'in_progress', 'done']))
Comment on lines +48 to +49

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep TodoList status validation intact

With this z.preprocess, the JSON Schema exposed by toInputJsonSchema(..., { io: 'input' }) describes the preprocess input side for status rather than the enum, while the real tool path only runs AJV against tool.parameters in validateExecutableToolArgs and never calls TodoListInputSchema.safeParse. As a result, statuses like finished or wip can pass preflight, and setTodos stores anything other than the special-cased completed, corrupting TodoList state despite the tool contract saying only pending, in_progress, and done are valid.

Useful? React with 👍 / 👎.

.describe('Current status of the todo. Must be exactly one of: pending, in_progress, done. Do NOT use completed or finished.'),
});

export interface TodoListInput {
Expand Down Expand Up @@ -81,6 +83,7 @@ function statusMarker(status: TodoStatus): string {
case 'in_progress':
return '[in_progress]';
case 'done':
case 'completed':
return '[done]';
default: {
const _exhaustive: never = status;
Expand Down Expand Up @@ -133,7 +136,10 @@ export class TodoListTool implements BuiltinTool<TodoListInput> {
private setTodos(todos: readonly TodoItem[]): void {
this.store.set(
TODO_STORE_KEY,
todos.map((todo) => ({ title: todo.title, status: todo.status })),
todos.map((todo) => ({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize completed before publishing TodoList args

When a model sends status: "completed", this normalizes only the stored state after the tool.call event has already published the original args; the CLI TUI live panel later reads matchedCall.args.todos in session-event-handler.ts and filters through isTodoItemShape, which still accepts only pending | in_progress | done. Because successful TodoList result text is suppressed in the transcript, those completed items disappear from the live panel until a resume hydrates from the normalized store, so the accepted alias should be normalized before dispatch or handled by the TUI.

Useful? React with 👍 / 👎.

title: todo.title,
status: todo.status === 'completed' ? 'done' : todo.status,
})),
);
}
}
17 changes: 17 additions & 0 deletions packages/agent-core/test/tools/todo-list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,23 @@ describe('TodoListTool', () => {
]);
});

it('accepts "completed" as a status and maps it to "done"', async () => {
const { tool, getTodos } = makeTool();

const result = await executeTool(tool, {
turnId: 't1',
toolCallId: 'call_1',
args: {
todos: [{ title: 'done task', status: 'completed' }],
},
signal,
});

expect(result).toMatchObject({ isError: false });
expect(result.output).toContain('[done] done task');
expect(getTodos()).toEqual([{ title: 'done task', status: 'done' }]);
});

it('renders a done todo with a marker matching the status enum value', async () => {
const { tool } = makeTool([{ title: 'shipped', status: 'done' }]);

Expand Down
Loading