import { z } from 'zod/v4' import { buildTool, type ToolDef } from '../../Tool.js' import { executeTaskCreatedHooks, getTaskCreatedHookMessage, } from '../../utils/hooks.js' import { lazySchema } from '../../utils/lazySchema.js' import { createTask, deleteTask, getTaskListId, isTodoV2Enabled, } from '../../utils/tasks.js' import { getAgentName, getTeamName } from '../../utils/teammate.js' import { TASK_CREATE_TOOL_NAME } from './constants.js' import { DESCRIPTION, getPrompt } from './prompt.js' const inputSchema = lazySchema(() => z.strictObject({ subject: z.string().describe('A brief title for the task'), description: z.string().describe('What needs to be done'), activeForm: z .string() .optional() .describe( 'Present continuous form shown in spinner when in_progress (e.g., "Running tests")', ), metadata: z .record(z.string(), z.unknown()) .optional() .describe('Arbitrary metadata to attach to the task'), }), ) type InputSchema = ReturnType const outputSchema = lazySchema(() => z.object({ task: z.object({ id: z.string(), subject: z.string(), }), }), ) type OutputSchema = ReturnType export type Output = z.infer export const TaskCreateTool = buildTool({ name: TASK_CREATE_TOOL_NAME, searchHint: 'create a task in the task list', maxResultSizeChars: 100_000, async description() { return DESCRIPTION }, async prompt() { return getPrompt() }, get inputSchema(): InputSchema { return inputSchema() }, get outputSchema(): OutputSchema { return outputSchema() }, userFacingName() { return 'TaskCreate' }, shouldDefer: true, isEnabled() { return isTodoV2Enabled() }, isConcurrencySafe() { return true }, toAutoClassifierInput(input) { return input.subject }, renderToolUseMessage() { return null }, async call({ subject, description, activeForm, metadata }, context) { const taskId = await createTask(getTaskListId(), { subject, description, activeForm, status: 'pending', owner: undefined, blocks: [], blockedBy: [], metadata, }) const blockingErrors: string[] = [] const generator = executeTaskCreatedHooks( taskId, subject, description, getAgentName(), getTeamName(), undefined, context?.abortController?.signal, undefined, context, ) for await (const result of generator) { if (result.blockingError) { blockingErrors.push(getTaskCreatedHookMessage(result.blockingError)) } } if (blockingErrors.length > 0) { await deleteTask(getTaskListId(), taskId) throw new Error(blockingErrors.join('\n')) } // Auto-expand task list when creating tasks context.setAppState(prev => { if (prev.expandedView === 'tasks') return prev return { ...prev, expandedView: 'tasks' as const } }) return { data: { task: { id: taskId, subject, }, }, } }, mapToolResultToToolResultBlockParam(content, toolUseID) { const { task } = content as Output return { tool_use_id: toolUseID, type: 'tool_result', content: `Task #${task.id} created successfully: ${task.subject}`, } }, } satisfies ToolDef)