|
| 1 | +import { publisher } from '../../constants' |
| 2 | + |
| 3 | +import type { SecretAgentDefinition } from '../../types/secret-agent-definition' |
| 4 | + |
| 5 | +export const createBestOfNImplementor2 = (options: { |
| 6 | + model: 'gpt-5' | 'opus' | 'sonnet' |
| 7 | +}): Omit<SecretAgentDefinition, 'id'> => { |
| 8 | + const { model } = options |
| 9 | + const isGpt5 = model === 'gpt-5' |
| 10 | + const isOpus = model === 'opus' |
| 11 | + return { |
| 12 | + publisher, |
| 13 | + model: isGpt5 |
| 14 | + ? 'openai/gpt-5.2' |
| 15 | + : isOpus |
| 16 | + ? 'anthropic/claude-opus-4.5' |
| 17 | + : 'anthropic/claude-sonnet-4.5', |
| 18 | + displayName: isGpt5 |
| 19 | + ? 'GPT-5 Implementation Generator v2' |
| 20 | + : isOpus |
| 21 | + ? 'Opus Implementation Generator v2' |
| 22 | + : 'Sonnet Implementation Generator v2', |
| 23 | + spawnerPrompt: |
| 24 | + 'Generates a complete implementation using propose_* tools that draft changes without applying them', |
| 25 | + |
| 26 | + includeMessageHistory: true, |
| 27 | + inheritParentSystemPrompt: true, |
| 28 | + |
| 29 | + toolNames: ['propose_write_file', 'propose_str_replace'], |
| 30 | + spawnableAgents: [], |
| 31 | + |
| 32 | + inputSchema: {}, |
| 33 | + outputMode: 'structured_output', |
| 34 | + |
| 35 | + instructionsPrompt: `You are an expert code editor with deep understanding of software engineering principles. You were spawned to generate an implementation for the user's request. |
| 36 | + |
| 37 | +Your task is to write out ALL the code changes needed to complete the user's request. |
| 38 | +
|
| 39 | +IMPORTANT: Use propose_str_replace and propose_write_file tools to make your edits. These tools draft changes without actually applying them - they will be reviewed first. |
| 40 | +
|
| 41 | +You can make multiple tool calls across multiple steps to complete the implementation. |
| 42 | +
|
| 43 | +After your edit tool calls, you can optionally mention any follow-up steps to take, like deleting a file, or a specific way to validate the changes. |
| 44 | +
|
| 45 | +Your implementation should: |
| 46 | +- Be complete and comprehensive |
| 47 | +- Include all necessary changes to fulfill the user's request |
| 48 | +- Follow the project's conventions and patterns |
| 49 | +- Be as simple and maintainable as possible |
| 50 | +- Reuse existing code wherever possible |
| 51 | +- Be well-structured and organized |
| 52 | +
|
| 53 | +More style notes: |
| 54 | +- Extra try/catch blocks clutter the code -- use them sparingly. |
| 55 | +- Optional arguments are code smell and worse than required arguments. |
| 56 | +- New components often should be added to a new file, not added to an existing file. |
| 57 | +
|
| 58 | +Write out your complete implementation now.`, |
| 59 | + |
| 60 | + handleSteps: function* ({ agentState: initialAgentState }) { |
| 61 | + const initialMessageHistoryLength = |
| 62 | + initialAgentState.messageHistory.length |
| 63 | + |
| 64 | + // Helper to check if a message is empty (no tool calls and empty/no text) |
| 65 | + const isEmptyAssistantMessage = (message: any): boolean => { |
| 66 | + if (message.role !== 'assistant' || !Array.isArray(message.content)) { |
| 67 | + return false |
| 68 | + } |
| 69 | + const hasToolCalls = message.content.some( |
| 70 | + (part: any) => part.type === 'tool-call', |
| 71 | + ) |
| 72 | + if (hasToolCalls) { |
| 73 | + return false |
| 74 | + } |
| 75 | + // Check if all text parts are empty or there are no text parts |
| 76 | + const textParts = message.content.filter( |
| 77 | + (part: any) => part.type === 'text', |
| 78 | + ) |
| 79 | + if (textParts.length === 0) { |
| 80 | + return true |
| 81 | + } |
| 82 | + return textParts.every((part: any) => !part.text || !part.text.trim()) |
| 83 | + } |
| 84 | + |
| 85 | + const { agentState } = yield 'STEP_ALL' |
| 86 | + |
| 87 | + let postMessages = agentState.messageHistory.slice( |
| 88 | + initialMessageHistoryLength, |
| 89 | + ) |
| 90 | + |
| 91 | + // Retry if no messages or if the only message is empty (no tool calls and empty text) |
| 92 | + if (postMessages.length === 0) { |
| 93 | + const { agentState: postMessagesAgentState } = yield 'STEP_ALL' |
| 94 | + postMessages = postMessagesAgentState.messageHistory.slice( |
| 95 | + initialMessageHistoryLength, |
| 96 | + ) |
| 97 | + } else if ( |
| 98 | + postMessages.length === 1 && |
| 99 | + isEmptyAssistantMessage(postMessages[0]) |
| 100 | + ) { |
| 101 | + const { agentState: postMessagesAgentState } = yield 'STEP_ALL' |
| 102 | + postMessages = postMessagesAgentState.messageHistory.slice( |
| 103 | + initialMessageHistoryLength, |
| 104 | + ) |
| 105 | + } |
| 106 | + |
| 107 | + // Extract tool calls from assistant messages |
| 108 | + // Handle both 'input' and 'args' property names for compatibility |
| 109 | + const toolCalls: { toolName: string; input: any }[] = [] |
| 110 | + for (const message of postMessages) { |
| 111 | + if (message.role !== 'assistant' || !Array.isArray(message.content)) |
| 112 | + continue |
| 113 | + for (const part of message.content) { |
| 114 | + if (part.type === 'tool-call') { |
| 115 | + toolCalls.push({ |
| 116 | + toolName: part.toolName, |
| 117 | + input: part.input ?? (part as any).args ?? {}, |
| 118 | + }) |
| 119 | + } |
| 120 | + } |
| 121 | + } |
| 122 | + |
| 123 | + // Extract tool results (unified diffs) from tool messages |
| 124 | + const toolResults: any[] = [] |
| 125 | + for (const message of postMessages) { |
| 126 | + if (message.role !== 'tool' || !Array.isArray(message.content)) continue |
| 127 | + for (const part of message.content) { |
| 128 | + if (part.type === 'json' && part.value) { |
| 129 | + toolResults.push(part.value) |
| 130 | + } |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | + // Concatenate all unified diffs for the selector to review |
| 135 | + const unifiedDiffs = toolResults |
| 136 | + .filter((result: any) => result.unifiedDiff) |
| 137 | + .map((result: any) => `--- ${result.file} ---\n${result.unifiedDiff}`) |
| 138 | + .join('\n\n') |
| 139 | + |
| 140 | + yield { |
| 141 | + toolName: 'set_output', |
| 142 | + input: { |
| 143 | + toolCalls, |
| 144 | + toolResults, |
| 145 | + unifiedDiffs, |
| 146 | + }, |
| 147 | + includeToolCall: false, |
| 148 | + } |
| 149 | + }, |
| 150 | + } |
| 151 | +} |
| 152 | +const definition = { |
| 153 | + ...createBestOfNImplementor2({ model: 'opus' }), |
| 154 | + id: 'editor-implementor2', |
| 155 | +} |
| 156 | +export default definition |
0 commit comments