| 114 | } |
| 115 | |
| 116 | func appendComputerUseProviderTool( |
| 117 | providerTools []chatloop.ProviderTool, |
| 118 | opts computerUseProviderToolOptions, |
| 119 | ) ([]chatloop.ProviderTool, error) { |
| 120 | // This helper is called for every chat turn. Only chats created by the |
| 121 | // computer_use subagent definition have ChatModeComputerUse, which filters |
| 122 | // out root, general, and explore chats. Plan mode is separate from Mode, so |
| 123 | // planning turns stay gated even for computer-use chats. |
| 124 | if opts.isPlanModeTurn || !opts.isComputerUse { |
| 125 | return providerTools, nil |
| 126 | } |
| 127 | |
| 128 | desktopGeometry := chattool.DefaultComputerUseDesktopGeometry(opts.provider) |
| 129 | definition, err := chattool.ComputerUseProviderTool( |
| 130 | opts.provider, |
| 131 | desktopGeometry.DeclaredWidth, |
| 132 | desktopGeometry.DeclaredHeight, |
| 133 | ) |
| 134 | if err != nil { |
| 135 | return providerTools, xerrors.Errorf( |
| 136 | "build computer use provider tool for provider %q: %w", |
| 137 | opts.provider, |
| 138 | err, |
| 139 | ) |
| 140 | } |
| 141 | |
| 142 | clock := opts.clock |
| 143 | if clock == nil { |
| 144 | clock = quartz.NewReal() |
| 145 | } |
| 146 | providerTool := chatloop.ProviderTool{ |
| 147 | Definition: definition, |
| 148 | Runner: chattool.NewComputerUseTool( |
| 149 | opts.provider, |
| 150 | desktopGeometry.DeclaredWidth, |
| 151 | desktopGeometry.DeclaredHeight, |
| 152 | opts.getWorkspaceConn, |
| 153 | opts.storeFile, |
| 154 | clock, |
| 155 | opts.logger, |
| 156 | ), |
| 157 | } |
| 158 | if opts.provider == chattool.ComputerUseProviderOpenAI { |
| 159 | // OpenAI computer-use image results need detail metadata so the model receives |
| 160 | // the screenshot at original detail when the chat loop sends the tool result. |
| 161 | providerTool.ResultProviderMetadata = openaicomputeruse.ResultProviderMetadata |
| 162 | } |
| 163 | |
| 164 | return append(providerTools, providerTool), nil |
| 165 | } |