source dump of claude code
at main 115 lines 3.9 kB view raw
1import { feature } from 'bun:bundle' 2import { z } from 'zod/v4' 3import { getSessionId } from '../../bootstrap/state.js' 4import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js' 5import { buildTool, type ToolDef } from '../../Tool.js' 6import { lazySchema } from '../../utils/lazySchema.js' 7import { isTodoV2Enabled } from '../../utils/tasks.js' 8import { TodoListSchema } from '../../utils/todo/types.js' 9import { VERIFICATION_AGENT_TYPE } from '../AgentTool/constants.js' 10import { TODO_WRITE_TOOL_NAME } from './constants.js' 11import { DESCRIPTION, PROMPT } from './prompt.js' 12 13const inputSchema = lazySchema(() => 14 z.strictObject({ 15 todos: TodoListSchema().describe('The updated todo list'), 16 }), 17) 18type InputSchema = ReturnType<typeof inputSchema> 19 20const outputSchema = lazySchema(() => 21 z.object({ 22 oldTodos: TodoListSchema().describe('The todo list before the update'), 23 newTodos: TodoListSchema().describe('The todo list after the update'), 24 verificationNudgeNeeded: z.boolean().optional(), 25 }), 26) 27type OutputSchema = ReturnType<typeof outputSchema> 28 29export type Output = z.infer<OutputSchema> 30 31export const TodoWriteTool = buildTool({ 32 name: TODO_WRITE_TOOL_NAME, 33 searchHint: 'manage the session task checklist', 34 maxResultSizeChars: 100_000, 35 strict: true, 36 async description() { 37 return DESCRIPTION 38 }, 39 async prompt() { 40 return PROMPT 41 }, 42 get inputSchema(): InputSchema { 43 return inputSchema() 44 }, 45 get outputSchema(): OutputSchema { 46 return outputSchema() 47 }, 48 userFacingName() { 49 return '' 50 }, 51 shouldDefer: true, 52 isEnabled() { 53 return !isTodoV2Enabled() 54 }, 55 toAutoClassifierInput(input) { 56 return `${input.todos.length} items` 57 }, 58 async checkPermissions(input) { 59 // No permission checks required for todo operations 60 return { behavior: 'allow', updatedInput: input } 61 }, 62 renderToolUseMessage() { 63 return null 64 }, 65 async call({ todos }, context) { 66 const appState = context.getAppState() 67 const todoKey = context.agentId ?? getSessionId() 68 const oldTodos = appState.todos[todoKey] ?? [] 69 const allDone = todos.every(_ => _.status === 'completed') 70 const newTodos = allDone ? [] : todos 71 72 // Structural nudge: if the main-thread agent is closing out a 3+ item 73 // list and none of those items was a verification step, append a reminder 74 // to the tool result. Fires at the exact loop-exit moment where skips 75 // happen ("when the last task closed, the loop exited"). 76 let verificationNudgeNeeded = false 77 if ( 78 feature('VERIFICATION_AGENT') && 79 getFeatureValue_CACHED_MAY_BE_STALE('tengu_hive_evidence', false) && 80 !context.agentId && 81 allDone && 82 todos.length >= 3 && 83 !todos.some(t => /verif/i.test(t.content)) 84 ) { 85 verificationNudgeNeeded = true 86 } 87 88 context.setAppState(prev => ({ 89 ...prev, 90 todos: { 91 ...prev.todos, 92 [todoKey]: newTodos, 93 }, 94 })) 95 96 return { 97 data: { 98 oldTodos, 99 newTodos: todos, 100 verificationNudgeNeeded, 101 }, 102 } 103 }, 104 mapToolResultToToolResultBlockParam({ verificationNudgeNeeded }, toolUseID) { 105 const base = `Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable` 106 const nudge = verificationNudgeNeeded 107 ? `\n\nNOTE: You just closed out 3+ tasks and none of them was a verification step. Before writing your final summary, spawn the verification agent (subagent_type="${VERIFICATION_AGENT_TYPE}"). You cannot self-assign PARTIAL by listing caveats in your summary \u2014 only the verifier issues a verdict.` 108 : '' 109 return { 110 tool_use_id: toolUseID, 111 type: 'tool_result', 112 content: base + nudge, 113 } 114 }, 115} satisfies ToolDef<InputSchema, Output>)