File size: 3,441 Bytes
94e3086 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 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'))
}
// 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<InputSchema, Output>)
|