139 lines
3.4 KiB
TypeScript
139 lines
3.4 KiB
TypeScript
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>)
|