Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import {
IToolResult,
ToolDataSource,
IToolInvocationPreparationContext,
IPreparedToolInvocation
IPreparedToolInvocation,
ToolInvocationPresentation
} from '../languageModelToolsService.js';
import { ILogService } from '../../../../../../platform/log/common/log.js';
import { ITelemetryService } from '../../../../../../platform/telemetry/common/telemetry.js';
Expand Down Expand Up @@ -162,6 +163,7 @@ export class ManageTodoListTool extends Disposable implements IToolImpl {

return {
invocationMessage,
presentation: items.length ? undefined : ToolInvocationPresentation.Hidden,
pastTenseMessage: new MarkdownString(message ?? localize('todo.updatedList', "Updated todo list")),
toolSpecificData: {
kind: 'todoList',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@
*--------------------------------------------------------------------------------------------*/

import assert from 'assert';
import { CancellationToken } from '../../../../../../../base/common/cancellation.js';
import { Event } from '../../../../../../../base/common/event.js';
import { URI } from '../../../../../../../base/common/uri.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../../base/test/common/utils.js';
import { createManageTodoListToolData } from '../../../../common/tools/builtinTools/manageTodoListTool.js';
import { IToolData } from '../../../../common/tools/languageModelToolsService.js';
import { NullLogService } from '../../../../../../../platform/log/common/log.js';
import { NullTelemetryService } from '../../../../../../../platform/telemetry/common/telemetryUtils.js';
import { createManageTodoListToolData, ManageTodoListTool } from '../../../../common/tools/builtinTools/manageTodoListTool.js';
import { IChatTodo, IChatTodoListService } from '../../../../common/tools/chatTodoListService.js';
import { IToolData, ToolInvocationPresentation } from '../../../../common/tools/languageModelToolsService.js';
import { IJSONSchema } from '../../../../../../../base/common/jsonSchema.js';

suite('ManageTodoListTool Schema', () => {
Expand Down Expand Up @@ -59,3 +65,83 @@ suite('ManageTodoListTool Schema', () => {
assert.deepStrictEqual(statusProperty.enum, ['not-started', 'in-progress', 'completed'], 'Status should have correct enum values');
});
});

suite('ManageTodoListTool prepareToolInvocation', () => {
const store = ensureNoDisposablesAreLeakedInTestSuite();
const sessionResource = URI.parse('vscode-chat://session/1');

function createMockTodoListService(todos: IChatTodo[] = []): IChatTodoListService {
return {
_serviceBrand: undefined,
onDidUpdateTodos: Event.None,
getTodos: () => todos,
setTodos: () => { },
migrateTodos: () => { },
};
}

function createTool(todos: IChatTodo[] = []): ManageTodoListTool {
return store.add(new ManageTodoListTool(
createMockTodoListService(todos),
new NullLogService(),
NullTelemetryService,
));
}

test('presentation is Hidden when todoList param is empty', async () => {
const tool = createTool();
const result = await tool.prepareToolInvocation({
parameters: { todoList: [] },
toolCallId: 'call-1',
chatSessionResource: sessionResource,
}, CancellationToken.None);

assert.strictEqual(result?.presentation, ToolInvocationPresentation.Hidden);
});

test('presentation is undefined when todoList param has items', async () => {
const tool = createTool();
const result = await tool.prepareToolInvocation({
parameters: {
todoList: [{ id: 1, title: 'Task 1', status: 'not-started' }],
},
toolCallId: 'call-1',
chatSessionResource: sessionResource,
}, CancellationToken.None);

assert.strictEqual(result?.presentation, undefined);
});

test('presentation is Hidden for read operation with no existing todos', async () => {
const tool = createTool([]);
const result = await tool.prepareToolInvocation({
parameters: { operation: 'read' },
toolCallId: 'call-1',
chatSessionResource: sessionResource,
}, CancellationToken.None);

assert.strictEqual(result?.presentation, ToolInvocationPresentation.Hidden);
});

test('presentation is undefined for read operation with existing todos', async () => {
const tool = createTool([{ id: 1, title: 'Existing', status: 'in-progress' }]);
const result = await tool.prepareToolInvocation({
parameters: { operation: 'read' },
toolCallId: 'call-1',
chatSessionResource: sessionResource,
}, CancellationToken.None);

assert.strictEqual(result?.presentation, undefined);
});

test('returns undefined when no chatSessionResource is provided', async () => {
const tool = createTool();
const result = await tool.prepareToolInvocation({
parameters: { todoList: [{ id: 1, title: 'Task', status: 'not-started' }] },
toolCallId: 'call-1',
chatSessionResource: undefined,
}, CancellationToken.None);

assert.strictEqual(result, undefined);
});
});
Loading