yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
browser_agent.cc
Go to the documentation of this file.
1#include <functional>
2#include <iostream>
3#include <mutex>
4#include <string>
5#include <thread>
6#include <vector>
7
8#include "absl/status/status.h"
9#include "absl/strings/str_cat.h"
10#include "absl/strings/str_join.h"
11#include "absl/time/clock.h"
12#include "cli/cli.h"
18#include "rom/rom.h"
19
20namespace yaze {
21namespace cli {
22
23// Accessor for the global AI service provided by wasm_terminal_bridge
24BrowserAIService* GetGlobalBrowserAIService();
25
26// Accessor for the global ROM from wasm_terminal_bridge.cc
27extern Rom* GetGlobalRom();
28
29namespace handlers {
30
31namespace {
32
33// Static history for the session
34std::vector<agent::ChatMessage> g_chat_history;
35std::mutex g_chat_mutex;
36
37// Simple in-memory storage for the last generated plan
38// In a full implementation, this would be a full ProposalRegistry
39std::string g_pending_plan;
40std::mutex g_plan_mutex;
41
42absl::Status HandleChatCommand(const std::vector<std::string>& args) {
43 auto* service = GetGlobalBrowserAIService();
44 if (!service) {
45 return absl::FailedPreconditionError(
46 "AI Service not initialized. Please set an API key using the settings "
47 "menu.");
48 }
49
50 if (args.empty()) {
51 return absl::InvalidArgumentError(
52 "Please provide a message. Usage: agent chat <message>");
53 }
54
55 std::string prompt = absl::StrJoin(args, " ");
56
57 {
58 std::lock_guard<std::mutex> lock(g_chat_mutex);
59 // Add user message to history
60 agent::ChatMessage user_msg;
62 user_msg.message = prompt;
63 user_msg.timestamp = absl::Now();
64 g_chat_history.push_back(user_msg);
65 }
66
67 std::cout << "Thinking... (This may take a few seconds)" << std::endl;
68
69 // Run AI request in a background thread
70 std::thread([service]() {
71 std::vector<agent::ChatMessage> history_copy;
72 {
73 std::lock_guard<std::mutex> lock(g_chat_mutex);
74 history_copy = g_chat_history;
75 }
76
77 // Generate response using history
78 auto response_or = service->GenerateResponse(history_copy);
79
80 if (!response_or.ok()) {
81 std::cerr << "\nError: " << response_or.status().message() << "\n"
82 << std::endl;
83 // Remove the failed user message so we don't get stuck in a bad state
84 std::lock_guard<std::mutex> lock(g_chat_mutex);
85 if (!g_chat_history.empty() &&
87 g_chat_history.pop_back();
88 }
89 return;
90 }
91
92 auto response = response_or.value();
93
94 {
95 std::lock_guard<std::mutex> lock(g_chat_mutex);
96 // Add agent response to history
97 agent::ChatMessage agent_msg;
99 agent_msg.message = response.text_response;
100 agent_msg.timestamp = absl::Now();
101 g_chat_history.push_back(agent_msg);
102 }
103
104 // Print response safely
105 // Note: In Emscripten, printf/cout from threads is proxied to main thread
106 std::cout << "\nAgent: " << response.text_response << "\n" << std::endl;
107 }).detach();
108
109 return absl::OkStatus();
110}
111
112absl::Status HandlePlanCommand(const std::vector<std::string>& args) {
113 auto* service = GetGlobalBrowserAIService();
114 if (!service) {
115 return absl::FailedPreconditionError("AI Service not initialized.");
116 }
117
118 if (args.empty()) {
119 return absl::InvalidArgumentError("Usage: agent plan <task description>");
120 }
121
122 std::string task = absl::StrJoin(args, " ");
123 std::string prompt =
124 "Create a detailed step-by-step implementation plan for the following "
125 "ROM hacking task. "
126 "Do not execute it yet, just plan it.\nTask: " +
127 task;
128
129 std::cout << "Generating plan... (Check back in a moment)" << std::endl;
130
131 std::thread([service, prompt]() {
132 auto response_or = service->GenerateResponse(prompt);
133 if (!response_or.ok()) {
134 std::cerr << "\nPlan Error: " << response_or.status().message() << "\n"
135 << std::endl;
136 return;
137 }
138
139 {
140 std::lock_guard<std::mutex> lock(g_plan_mutex);
141 g_pending_plan = response_or.value().text_response;
142 }
143
144 std::cout << "\nProposed Plan Ready:\n" << std::string(40, '-') << "\n";
145 std::cout << response_or.value().text_response << "\n"
146 << std::string(40, '-') << "\n";
147 std::cout << "Run 'agent diff' to review this plan again.\n" << std::endl;
148 }).detach();
149
150 return absl::OkStatus();
151}
152
153absl::Status HandleDiffCommand(Rom&, const std::vector<std::string>&) {
154 std::lock_guard<std::mutex> lock(g_plan_mutex);
155 if (g_pending_plan.empty()) {
156 return absl::NotFoundError(
157 "No pending plan found. Run 'agent plan <task>' first.");
158 }
159
160 std::cout << "\nPending Plan (Conceptual Diff):\n"
161 << std::string(40, '-') << "\n";
162 std::cout << g_pending_plan << "\n" << std::string(40, '-') << "\n";
163
164 return absl::OkStatus();
165}
166
167absl::Status HandleListCommand(const std::vector<std::string>& /*args*/) {
168 const auto& catalog = ResourceCatalog::Instance();
169 std::ostringstream oss;
170
171 oss << "Available Resources:\n";
172 for (const auto& resource : catalog.AllResources()) {
173 oss << " - " << resource.resource << ": " << resource.description << "\n";
174 }
175
176 std::cout << oss.str() << std::endl;
177 return absl::OkStatus();
178}
179
180absl::Status HandleDescribeCommand(const std::vector<std::string>& args) {
181 if (args.empty()) {
182 return absl::InvalidArgumentError("Usage: agent describe <resource_name>");
183 }
184
185 const std::string& name = args[0];
186 const auto& catalog = ResourceCatalog::Instance();
187 auto resource_or = catalog.GetResource(name);
188
189 if (!resource_or.ok()) {
190 return resource_or.status();
191 }
192
193 const auto& resource = resource_or.value();
194 std::string json_output = catalog.SerializeResource(resource);
195
196 std::cout << "Description of " << name << ":\n";
197 std::cout << json_output << std::endl;
198
199 return absl::OkStatus();
200}
201
202} // namespace
203
204// Forward declarations if needed
205namespace agent {
206// Stubs for unsupported commands
207absl::Status HandleRunCommand(const std::vector<std::string>&, Rom&) {
208 return absl::UnimplementedError(
209 "Agent run command not available in browser yet");
210}
211// HandlePlanCommand and HandleDiffCommand are now implemented in the anonymous namespace above
212// and dispatched below.
213
214absl::Status HandleCommitCommand(Rom&) {
215 return absl::UnimplementedError(
216 "Agent commit command not available in browser yet");
217}
218absl::Status HandleRevertCommand(Rom&) {
219 return absl::UnimplementedError(
220 "Agent revert command not available in browser yet");
221}
222absl::Status HandleAcceptCommand(const std::vector<std::string>&, Rom&) {
223 return absl::UnimplementedError(
224 "Agent accept command not available in browser yet");
225}
226absl::Status HandleTestCommand(const std::vector<std::string>&) {
227 return absl::UnimplementedError(
228 "Agent test command not available in browser");
229}
230absl::Status HandleTestConversationCommand(const std::vector<std::string>&) {
231 return absl::UnimplementedError(
232 "Agent test-conversation command not available in browser");
233}
234absl::Status HandleLearnCommand(const std::vector<std::string>&) {
235 return absl::UnimplementedError(
236 "Agent learn command not available in browser");
237}
238} // namespace agent
239
240std::string GenerateAgentHelp() {
241 return "Available Agent Commands (Browser):\n"
242 " todo - Manage todo list\n"
243 " chat <message> - Chat with the AI agent\n"
244 " list - List available resources\n"
245 " describe <name> - Describe a specific resource\n"
246 " plan <task> - Generate an implementation plan\n"
247 " diff - View pending plan/changes\n";
248}
249
251 static Rom rom;
252 return rom;
253}
254
255absl::Status HandleAgentCommand(const std::vector<std::string>& arg_vec) {
256 if (arg_vec.empty()) {
257 std::cout << GenerateAgentHelp();
258 return absl::InvalidArgumentError("No subcommand specified");
259 }
260
261 const std::string& subcommand = arg_vec[0];
262 std::vector<std::string> subcommand_args(arg_vec.begin() + 1, arg_vec.end());
263
264 if (subcommand == "simple-chat" || subcommand == "chat") {
265 return HandleChatCommand(subcommand_args);
266 }
267
268 if (subcommand == "plan") {
269 return HandlePlanCommand(subcommand_args);
270 }
271
272 if (subcommand == "diff") {
273 // Diff usually takes args but we ignore them for this simple version
274 return HandleDiffCommand(AgentRom(), subcommand_args);
275 }
276
277 if (subcommand == "todo") {
278 return handlers::HandleTodoCommand(subcommand_args);
279 }
280
281 if (subcommand == "list") {
282 return HandleListCommand(subcommand_args);
283 }
284
285 if (subcommand == "describe") {
286 return HandleDescribeCommand(subcommand_args);
287 }
288
289 std::cout << GenerateAgentHelp();
290 return absl::InvalidArgumentError(
291 absl::StrCat("Unknown subcommand: ", subcommand));
292}
293
294} // namespace handlers
295} // namespace cli
296} // namespace yaze
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:24
static const ResourceCatalog & Instance()
absl::Status HandleDescribeCommand(const std::vector< std::string > &args)
absl::Status HandleDiffCommand(Rom &rom, const std::vector< std::string > &args)
absl::Status HandleListCommand()
absl::Status HandleTestConversationCommand(const std::vector< std::string > &)
absl::Status HandleAcceptCommand(const std::vector< std::string > &, Rom &)
absl::Status HandleLearnCommand(const std::vector< std::string > &)
absl::Status HandleRunCommand(const std::vector< std::string > &, Rom &)
absl::Status HandleCommitCommand(Rom &)
absl::Status HandleRevertCommand(Rom &)
absl::Status HandleTestCommand(const std::vector< std::string > &)
absl::Status HandleChatCommand(const std::vector< std::string > &args)
std::string GenerateAgentHelp()
absl::Status HandleAgentCommand(const std::vector< std::string > &args)
Unified agent command handler using CommandRegistry.
Definition agent.cc:112
absl::Status HandleTodoCommand(const std::vector< std::string > &args)
Handle z3ed agent todo commands.
Rom * GetGlobalRom()
BrowserAIService * GetGlobalBrowserAIService()