|
| 1 | +import { publisher } from '../.agents/constants' |
| 2 | +import { type SecretAgentDefinition } from '../.agents/types/secret-agent-definition' |
| 3 | + |
| 4 | +import type { Message } from '../.agents/types/util-types' |
| 5 | + |
| 6 | +const editor: SecretAgentDefinition = { |
| 7 | + id: 'editor', |
| 8 | + publisher, |
| 9 | + model: 'anthropic/claude-sonnet-4.5', |
| 10 | + displayName: 'Code Editor', |
| 11 | + spawnerPrompt: |
| 12 | + 'Expert code editor with access to tools to find and edit files, run terminal commands, and search the web. Can handle small to medium sized tasks, or work off of a plan for more complex tasks. For easy tasks, you can spawn this agent directly rather than invoking a researcher or planner first. Spawn mulitple in parallel if needed, but only on totally distinct tasks.', |
| 13 | + inputSchema: { |
| 14 | + prompt: { |
| 15 | + type: 'string', |
| 16 | + description: 'The coding task to implement', |
| 17 | + }, |
| 18 | + params: { |
| 19 | + type: 'object', |
| 20 | + properties: { |
| 21 | + maxContextLength: { |
| 22 | + type: 'number', |
| 23 | + }, |
| 24 | + }, |
| 25 | + required: [], |
| 26 | + }, |
| 27 | + }, |
| 28 | + outputMode: 'structured_output', |
| 29 | + toolNames: [ |
| 30 | + 'read_files', |
| 31 | + 'write_file', |
| 32 | + 'str_replace', |
| 33 | + 'run_terminal_command', |
| 34 | + 'code_search', |
| 35 | + 'spawn_agents', |
| 36 | + 'add_message', |
| 37 | + 'set_output', |
| 38 | + 'end_turn', |
| 39 | + ], |
| 40 | + spawnableAgents: ['file-explorer', 'researcher-web', 'researcher-docs'], |
| 41 | + |
| 42 | + includeMessageHistory: true, |
| 43 | + inheritParentSystemPrompt: true, |
| 44 | + |
| 45 | + instructionsPrompt: `You are an expert code editor with deep understanding of software engineering principles. |
| 46 | +
|
| 47 | +Implement the requested changes, using your judgment as needed, but referring to the original <user_message> as the most important source of information. |
| 48 | +
|
| 49 | +# Instructions |
| 50 | +
|
| 51 | +- Read any relevant files that have not already been read. Or, spawn a file-explorer to find any other relevant parts of the codebase. |
| 52 | +- Implement changes using str_replace or write_file. |
| 53 | +- Verify your changes by running tests, typechecking, etc. Keep going until you are sure the changes are correct. |
| 54 | +- You must use the set_output tool before finishing and include the following in your summary: |
| 55 | + - An answer to the user prompt (if they asked a question). |
| 56 | + - An explanation of the changes made. |
| 57 | + - A note on any checks you ran to verify the changes, such as tests, typechecking, etc., and the results of those checks. |
| 58 | + - Do not include a section on the benefits of the changes, as we're most interested in the changes themselves and what still needs to be done. |
| 59 | +- Do not write a summary outside of the one that you include in the set_output tool. |
| 60 | +- As soon as you use set_output, you must end your turn using the end_turn tool. |
| 61 | +`, |
| 62 | + |
| 63 | + handleSteps: function* ({ agentState: initialAgentState }) { |
| 64 | + const stepLimit = 25 |
| 65 | + let stepCount = 0 |
| 66 | + let agentState = initialAgentState |
| 67 | + let accumulatedEditToolResults: any[] = [] |
| 68 | + |
| 69 | + while (true) { |
| 70 | + stepCount++ |
| 71 | + |
| 72 | + const stepResult = yield 'STEP' |
| 73 | + agentState = stepResult.agentState // Capture the latest state |
| 74 | + |
| 75 | + // Accumulate new tool messages from this step |
| 76 | + const { messageHistory } = agentState |
| 77 | + |
| 78 | + // Extract and accumulate new edit tool results using helper function |
| 79 | + accumulatedEditToolResults.push( |
| 80 | + ...getLatestEditToolResults(messageHistory), |
| 81 | + ) |
| 82 | + |
| 83 | + if (stepResult.stepsComplete) { |
| 84 | + break |
| 85 | + } |
| 86 | + |
| 87 | + // If we've reached within one of the step limit, ask LLM to summarize progress |
| 88 | + if (stepCount === stepLimit - 1) { |
| 89 | + yield { |
| 90 | + toolName: 'add_message', |
| 91 | + input: { |
| 92 | + role: 'user', |
| 93 | + content: |
| 94 | + 'You have reached the step limit. Please use the set_output tool now to summarize your progress so far including all specific actions you took (note that any file changes will be included automatically in the output), what you still need to solve, and provide any insights that could help complete the remaining work. Please end your turn after using the set_output tool with the end_turn tool.', |
| 95 | + }, |
| 96 | + includeToolCall: false, |
| 97 | + } |
| 98 | + |
| 99 | + // One final step to produce the summary |
| 100 | + const finalStepResult = yield 'STEP' |
| 101 | + agentState = finalStepResult.agentState |
| 102 | + |
| 103 | + // Extract and accumulate final edit tool results using helper function |
| 104 | + accumulatedEditToolResults.push( |
| 105 | + ...getLatestEditToolResults(agentState.messageHistory), |
| 106 | + ) |
| 107 | + break |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + yield { |
| 112 | + toolName: 'set_output', |
| 113 | + input: { |
| 114 | + ...agentState.output, |
| 115 | + edits: accumulatedEditToolResults, |
| 116 | + }, |
| 117 | + includeToolCall: false, |
| 118 | + } |
| 119 | + |
| 120 | + function getLatestEditToolResults(messageHistory: Message[]) { |
| 121 | + const lastAssistantMessageIndex = messageHistory.findLastIndex( |
| 122 | + (message) => message.role === 'assistant', |
| 123 | + ) |
| 124 | + |
| 125 | + // Get all edit tool messages after the last assistant message |
| 126 | + const newToolMessages = messageHistory |
| 127 | + .slice(lastAssistantMessageIndex + 1) |
| 128 | + .filter((message) => message.role === 'tool') |
| 129 | + .filter( |
| 130 | + (message) => |
| 131 | + message.toolName === 'write_file' || |
| 132 | + message.toolName === 'str_replace', |
| 133 | + ) |
| 134 | + |
| 135 | + // Extract and return new edit tool results |
| 136 | + return ( |
| 137 | + newToolMessages |
| 138 | + .flatMap((message) => message.content) |
| 139 | + .filter((output) => output.type === 'json') |
| 140 | + .map((output) => output.value) |
| 141 | + // Only successful edits! |
| 142 | + .filter( |
| 143 | + (toolResult) => |
| 144 | + toolResult && !('errorMessage' in (toolResult as any)), |
| 145 | + ) |
| 146 | + ) |
| 147 | + } |
| 148 | + }, |
| 149 | +} |
| 150 | + |
| 151 | +export default editor |
0 commit comments