| 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<typeof inputSchema> |
|
|
| const outputSchema = lazySchema(() => |
| z.object({ |
| task: z.object({ |
| id: z.string(), |
| subject: z.string(), |
| }), |
| }), |
| ) |
| type OutputSchema = ReturnType<typeof outputSchema> |
|
|
| export type Output = z.infer<OutputSchema> |
|
|
| 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')) |
| } |
|
|
| |
| 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<InputSchema, Output>) |
|
|