17#include "absl/strings/match.h"
18#include "absl/strings/str_format.h"
19#include "absl/time/clock.h"
20#include "absl/time/time.h"
30#include "imgui/misc/cpp/imgui_stdlib.h"
41#if defined(YAZE_WITH_JSON)
42#include "nlohmann/json.hpp"
84 {
"Persona",
"Define persona and goals",
false},
85 {
"Tool Stack",
"Select the agent's tools",
false},
86 {
"Automation",
"Configure automation hooks",
false},
87 {
"Validation",
"Describe E2E validation",
false},
88 {
"E2E Checklist",
"Track readiness for end-to-end runs",
false}};
90 "Describe the persona, tone, and constraints for this agent.";
109 panel_manager->RegisterEditorPanel(std::make_unique<AgentStatusPanel>(
111 panel_manager->RegisterEditorPanel(std::make_unique<AgentPromptEditorPanel>(
113 panel_manager->RegisterEditorPanel(std::make_unique<AgentBotProfilesPanel>(
115 panel_manager->RegisterEditorPanel(std::make_unique<AgentChatHistoryPanel>(
117 panel_manager->RegisterEditorPanel(std::make_unique<AgentMetricsDashboardPanel>(
119 panel_manager->RegisterEditorPanel(std::make_unique<AgentBuilderPanel>(
121 panel_manager->RegisterEditorPanel(
122 std::make_unique<AgentChatPanel>(
agent_chat_.get()));
125 panel_manager->RegisterEditorPanel(std::make_unique<AgentKnowledgeBasePanel>(
130 ImGui::TextDisabled(
"Knowledge service not available");
132 "Build with Z3ED_AI=ON to enable the knowledge service.");
148 if (std::filesystem::exists(profiles_dir)) {
149 for (
const auto& entry :
150 std::filesystem::directory_iterator(profiles_dir)) {
151 if (entry.path().extension() ==
".json") {
152 std::ifstream file(entry.path());
153 if (file.is_open()) {
154 std::string json_content((std::istreambuf_iterator<char>(file)),
155 std::istreambuf_iterator<char>());
157 if (profile_or.ok()) {
164 return absl::OkStatus();
175 return absl::OkStatus();
182 return absl::OkStatus();
193 if (
const char* gemini_key = std::getenv(
"GEMINI_API_KEY")) {
205 if (
const char* openai_key = std::getenv(
"OPENAI_API_KEY")) {
219 agent_chat_->Initialize(toast_manager, proposal_drawer);
230 harness_telemetry_bridge_.SetAgentChat(
agent_chat_.get());
252 ImGuiIO& imgui_io = ImGui::GetIO();
267 ImGui::TextDisabled(
"(?)");
268 if (ImGui::IsItemHovered()) {
269 ImGui::BeginTooltip();
270 ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
271 ImGui::TextUnformatted(desc);
272 ImGui::PopTextWrapPos();
280 float avail_width = ImGui::GetContentRegionAvail().x;
281 ImVec2 button_size(avail_width / 2 - 8, 46);
283 auto ProviderButton = [&](
const char* label,
const char* provider_id,
284 const ImVec4& color) {
286 ImVec4 base_color = selected ? color : theme.panel_bg_darker;
294 ProviderButton(
ICON_MD_CLOUD " Ollama",
"ollama", theme.provider_ollama);
296 theme.provider_gemini);
299 theme.provider_gemini);
302 theme.provider_openai);
312 ImGui::Text(
"Model:");
313 ImGui::SetNextItemWidth(-1);
314 static char model_buf[128] =
"qwen2.5-coder:7b";
317 sizeof(model_buf) - 1);
319 if (ImGui::InputTextWithHint(
"##ollama_model",
320 "e.g., qwen2.5-coder:7b, llama3.2",
321 model_buf,
sizeof(model_buf))) {
325 ImGui::Text(
"Host URL:");
326 ImGui::SetNextItemWidth(-1);
327 static char host_buf[256] =
"http://localhost:11434";
329 sizeof(host_buf) - 1);
330 if (ImGui::InputText(
"##ollama_host", host_buf,
sizeof(host_buf))) {
338 if (ImGui::Button(
ICON_MD_REFRESH " Load from Env (GEMINI_API_KEY)")) {
339 const char* gemini_key = std::getenv(
"GEMINI_API_KEY");
350 HelpMarker(
"Loads GEMINI_API_KEY from your environment");
353 ImGui::Text(
"Model:");
354 ImGui::SetNextItemWidth(-1);
355 static char model_buf[128] =
"gemini-2.5-flash";
358 sizeof(model_buf) - 1);
360 if (ImGui::InputTextWithHint(
"##gemini_model",
"e.g., gemini-2.5-flash",
361 model_buf,
sizeof(model_buf))) {
365 ImGui::Text(
"API Key:");
366 ImGui::SetNextItemWidth(-1);
367 static char key_buf[256] =
"";
370 sizeof(key_buf) - 1);
372 if (ImGui::InputText(
"##gemini_key", key_buf,
sizeof(key_buf),
373 ImGuiInputTextFlags_Password)) {
377 ImGui::TextColored(theme.status_success,
384 if (ImGui::Button(
ICON_MD_REFRESH " Load from Env (OPENAI_API_KEY)")) {
385 const char* openai_key = std::getenv(
"OPENAI_API_KEY");
396 HelpMarker(
"Loads OPENAI_API_KEY from your environment");
399 ImGui::Text(
"Model:");
400 ImGui::SetNextItemWidth(-1);
401 static char openai_model_buf[128] =
"gpt-4o-mini";
404 sizeof(openai_model_buf) - 1);
406 if (ImGui::InputTextWithHint(
"##openai_model",
"e.g., gpt-4o-mini",
407 openai_model_buf,
sizeof(openai_model_buf))) {
411 ImGui::Text(
"API Key:");
412 ImGui::SetNextItemWidth(-1);
413 static char openai_key_buf[256] =
"";
415 openai_key_buf[0] ==
'\0') {
417 sizeof(openai_key_buf) - 1);
419 if (ImGui::InputText(
"##openai_key", openai_key_buf,
420 sizeof(openai_key_buf),
421 ImGuiInputTextFlags_Password)) {
425 ImGui::TextColored(theme.status_success,
435 HelpMarker(
"Logs provider requests/responses to console");
446 "Maximum number of tool calls the agent can make while solving a single "
450 HelpMarker(
"Number of times to retry API calls on failure.");
454 theme.text_secondary_gray);
456 static char name_buf[128];
458 if (ImGui::InputText(
"Name", name_buf,
sizeof(name_buf))) {
462 static char desc_buf[256];
464 sizeof(desc_buf) - 1);
465 if (ImGui::InputTextMultiline(
"Description", desc_buf,
sizeof(desc_buf),
470 ImGui::Text(
"Tags (comma-separated)");
471 static char tags_buf[256];
473 std::string tags_str;
479 strncpy(tags_buf, tags_str.c_str(),
sizeof(tags_buf) - 1);
481 if (ImGui::InputText(
"##profile_tags", tags_buf,
sizeof(tags_buf))) {
483 std::string tags_str(tags_buf);
485 while ((pos = tags_str.find(
',')) != std::string::npos) {
486 std::string tag = tags_str.substr(0, pos);
487 tag.erase(0, tag.find_first_not_of(
" \t"));
488 tag.erase(tag.find_last_not_of(
" \t") + 1);
492 tags_str.erase(0, pos + 1);
494 tags_str.erase(0, tags_str.find_first_not_of(
" \t"));
495 tags_str.erase(tags_str.find_last_not_of(
" \t") + 1);
496 if (!tags_str.empty()) {
502 ImGui::PushStyleColor(ImGuiCol_Button, theme.status_success);
503 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, theme.status_success);
504 if (ImGui::Button(
ICON_MD_CHECK " Apply & Save Configuration",
528 ImGui::PopStyleColor(2);
537 if (ImGui::BeginChild(
"ChatStatusCard", ImVec2(0, 140),
true)) {
545 if (!chat_active && ImGui::SmallButton(
"Open")) {
550 ImGui::Text(
"Provider:");
561 if (ImGui::BeginChild(
"RomStatusCard", ImVec2(0, 110),
true)) {
566 ImGui::TextDisabled(
"Tools: Ready");
568 ImGui::TextColored(theme.status_warning,
ICON_MD_WARNING " Not Loaded");
569 ImGui::TextDisabled(
"Load a ROM to enable tool calls.");
576 if (ImGui::BeginChild(
"QuickTipsCard", ImVec2(0, 130),
true)) {
579 ImGui::BulletText(
"Ctrl+H: Toggle chat popup");
580 ImGui::BulletText(
"Ctrl+P: View proposals");
581 ImGui::BulletText(
"Edit prompts in Prompt Editor");
582 ImGui::BulletText(
"Create and save custom bots");
590 ImGui::TextDisabled(
"View detailed metrics in the Metrics tab");
599 ImGui::Text(
"File:");
600 ImGui::SetNextItemWidth(-45);
602 const char* options[] = {
"system_prompt.txt",
"system_prompt_v2.txt",
603 "system_prompt_v3.txt"};
604 for (
const char* option : options) {
606 if (ImGui::Selectable(option, selected)) {
618 if (ImGui::IsItemHovered()) {
619 ImGui::SetTooltip(
"Reload from disk");
625 if (content_result.ok()) {
636 std::string placeholder = absl::StrFormat(
637 "# System prompt file not found: %s\n"
639 "# Ensure the file exists in assets/agent/%s\n",
649 ImVec2 editor_size(ImGui::GetContentRegionAvail().x,
650 ImGui::GetContentRegionAvail().y - 60);
654 if (ImGui::Button(
ICON_MD_SAVE " Save Prompt to Profile", ImVec2(-1, 0))) {
665 "Edit the system prompt that guides the agent's behavior. Changes are "
666 "stored on the active bot profile.");
675 ImGui::BeginChild(
"CurrentProfile", ImVec2(0, 150),
true);
692 if (ImGui::Button(
ICON_MD_ADD " Create New Profile", ImVec2(-1, 0))) {
694 new_profile.
name =
"New Profile";
708 ImGui::BeginChild(
"ProfilesList", ImVec2(0, 0),
true);
711 "No saved profiles. Create and save a profile to see it here.");
715 ImGui::PushID(
static_cast<int>(i));
718 ImVec2 button_size(ImGui::GetContentRegionAvail().x - 80, 0);
719 ImVec4 button_color =
720 is_current ? theme.accent_color : theme.panel_bg_darker;
726 absl::StrFormat(
"Loaded profile: %s", profile.name),
736 ImGui::PushStyleColor(ImGuiCol_Button, theme.status_warning);
741 absl::StrFormat(
"Deleted profile: %s", profile.name),
749 ImGui::PopStyleColor();
751 ImGui::TextDisabled(
" %s | %s", profile.provider.c_str(),
752 profile.description.empty()
754 : profile.description.c_str());
786 ImGui::BeginChild(
"HistoryList", ImVec2(0, 0),
true);
789 "No chat history. Start a conversation in the chat window.");
794 from_user ? theme.user_message_color : theme.agent_message_color;
796 ImGui::PushStyleColor(ImGuiCol_Text, color);
797 ImGui::Text(
"%s:", from_user ?
"User" :
"Agent");
798 ImGui::PopStyleColor();
801 ImGui::TextDisabled(
"%s",
802 absl::FormatTime(
"%H:%M:%S", msg.timestamp,
803 absl::LocalTimeZone())
806 ImGui::TextWrapped(
"%s", msg.message.c_str());
821 auto metrics =
agent_chat_->GetAgentService()->GetMetrics();
822 if (ImGui::BeginTable(
"MetricsTable", 2,
823 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
824 ImGui::TableSetupColumn(
"Metric", ImGuiTableColumnFlags_WidthFixed,
826 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthStretch);
827 ImGui::TableHeadersRow();
829 auto Row = [](
const char* label,
const std::string& value) {
830 ImGui::TableNextRow();
831 ImGui::TableSetColumnIndex(0);
832 ImGui::Text(
"%s", label);
833 ImGui::TableSetColumnIndex(1);
834 ImGui::TextDisabled(
"%s", value.c_str());
837 Row(
"Total Messages",
838 absl::StrFormat(
"%d user / %d agent", metrics.total_user_messages,
839 metrics.total_agent_messages));
840 Row(
"Tool Calls", absl::StrFormat(
"%d", metrics.total_tool_calls));
841 Row(
"Commands", absl::StrFormat(
"%d", metrics.total_commands));
842 Row(
"Proposals", absl::StrFormat(
"%d", metrics.total_proposals));
843 Row(
"Average Latency (s)",
844 absl::StrFormat(
"%.2f", metrics.average_latency_seconds));
846 absl::StrFormat(
"%.2f", metrics.total_elapsed_seconds));
851 ImGui::TextDisabled(
"Initialize the chat system to see metrics.");
862 "Customize the tile reference file that AI uses for tile placement. "
863 "Organize tiles by category and provide hex IDs with descriptions.");
880 if (ImGui::Button(
ICON_MD_SAVE " Save", ImVec2(100, 0))) {
883 ICON_MD_INFO " Save to project directory (coming soon)",
892 if (ImGui::IsItemHovered()) {
893 ImGui::SetTooltip(
"Reload from disk");
901 std::string default_tiles =
902 "# Common Tile16 Reference\n"
903 "# Format: 0xHEX = Description\n\n"
905 "0x020 = Grass (standard)\n\n"
907 "0x02E = Tree (oak)\n"
910 "0x14C = Water (top edge)\n"
911 "0x14D = Water (middle)\n";
921 ImVec2 editor_size(ImGui::GetContentRegionAvail().x,
922 ImGui::GetContentRegionAvail().y);
934 "Create a custom system prompt from scratch or start from a template.");
937 ImGui::Text(
"Prompt Name:");
938 ImGui::SetNextItemWidth(-1);
939 ImGui::InputTextWithHint(
"##new_prompt_name",
"e.g., custom_prompt.txt",
943 ImGui::Text(
"Start from template:");
945 auto LoadTemplate = [&](
const char* path,
const char* label) {
946 if (ImGui::Button(label, ImVec2(-1, 0))) {
963 std::string blank_template =
964 "# Custom System Prompt\n\n"
965 "You are an AI assistant for ROM hacking.\n\n"
967 "- Help users understand ROM data\n"
968 "- Provide accurate information\n"
969 "- Use tools when needed\n\n"
971 "1. Always provide text_response after tool calls\n"
972 "2. Be helpful and accurate\n"
973 "3. Explain your reasoning\n";
985 ImGui::PushStyleColor(ImGuiCol_Button, theme.status_success);
986 if (ImGui::Button(
ICON_MD_SAVE " Save New Prompt", ImVec2(-1, 40))) {
989 if (!absl::EndsWith(filename,
".txt")) {
994 absl::StrFormat(
ICON_MD_SAVE " Prompt saved as %s", filename),
1003 ImGui::PopStyleColor();
1007 "Note: New prompts are saved to your project. Use the Prompt Editor to "
1008 "edit existing prompts.");
1014 theme.accent_color);
1017 ImGui::TextDisabled(
"Chat system not initialized.");
1021 ImGui::BeginChild(
"AgentBuilderPanel", ImVec2(0, 0),
false);
1022 ImGui::Columns(2,
nullptr,
false);
1023 ImGui::TextColored(theme.accent_color,
"Stages");
1028 ImGui::PushID(
static_cast<int>(i));
1030 if (ImGui::Selectable(stage.name.c_str(), selected)) {
1033 ImGui::SameLine(ImGui::GetContentRegionAvail().x - 24.0f);
1034 ImGui::Checkbox(
"##stage_done", &stage.completed);
1038 ImGui::NextColumn();
1039 ImGui::TextColored(theme.text_info,
"Stage Details");
1045 int completed_stages = 0;
1047 if (stage.completed) {
1052 switch (stage_index) {
1054 static std::string new_goal;
1055 ImGui::Text(
"Persona + Goals");
1056 ImGui::InputTextMultiline(
"##persona_notes",
1059 ImGui::TextDisabled(
"Add Goal");
1060 ImGui::InputTextWithHint(
"##goal_input",
"e.g. Document dungeon plan",
1063 if (ImGui::Button(
ICON_MD_ADD) && !new_goal.empty()) {
1070 ImGui::PushID(
static_cast<int>(i));
1081 ImGui::Text(
"Tool Stack");
1082 auto tool_checkbox = [&](
const char* label,
bool* value) {
1083 ImGui::Checkbox(label, value);
1096 ImGui::Text(
"Automation");
1099 ImGui::Checkbox(
"Auto-focus proposal drawer",
1102 "Enable these options to push harness dashboards/test plans when "
1103 "executing plans.");
1107 ImGui::Text(
"Validation Criteria");
1108 ImGui::InputTextMultiline(
"##validation_notes",
1114 ImGui::Text(
"E2E Checklist");
1118 :
static_cast<float>(completed_stages) /
1120 ImGui::ProgressBar(progress, ImVec2(-1, 0),
1121 absl::StrFormat(
"%d/%zu complete", completed_stages,
1124 ImGui::Checkbox(
"Ready for automation handoff",
1126 ImGui::TextDisabled(
"Auto-sync ROM: %s",
1128 ImGui::TextDisabled(
"Auto-focus proposals: %s",
1137 float completion_ratio =
1140 :
static_cast<float>(completed_stages) /
1142 ImGui::TextDisabled(
"Overall Progress");
1143 ImGui::ProgressBar(completion_ratio, ImVec2(-1, 0));
1144 ImGui::TextDisabled(
"E2E Ready: %s",
1158#ifdef YAZE_WITH_GRPC
1161 service->SetToolPreferences(prefs);
1163 auto agent_cfg = service->GetConfig();
1168 service->SetConfig(agent_cfg);
1180 ImGui::InputTextWithHint(
"##blueprint_path",
"Path to blueprint...",
1182 std::filesystem::path blueprint_path =
1184 ? (std::filesystem::temp_directory_path() /
"agent_builder.json")
1217#if defined(YAZE_WITH_JSON)
1218 nlohmann::json json;
1235 json[
"stages"] = nlohmann::json::array();
1237 json[
"stages"].push_back({{
"name", stage.name},
1238 {
"summary", stage.summary},
1239 {
"completed", stage.completed}});
1243 std::filesystem::create_directories(path.parent_path(), ec);
1244 std::ofstream file(path);
1245 if (!file.is_open()) {
1246 return absl::InternalError(
1247 absl::StrFormat(
"Failed to open blueprint: %s", path.string()));
1249 file << json.dump(2);
1251 return absl::OkStatus();
1254 return absl::UnimplementedError(
"Blueprint export requires JSON support");
1259#if defined(YAZE_WITH_JSON)
1260 std::ifstream file(path);
1261 if (!file.is_open()) {
1262 return absl::NotFoundError(
1263 absl::StrFormat(
"Blueprint not found: %s", path.string()));
1266 nlohmann::json json;
1271 if (json.contains(
"goals") && json[
"goals"].is_array()) {
1272 for (
const auto& goal : json[
"goals"]) {
1273 if (goal.is_string()) {
1278 if (json.contains(
"tools") && json[
"tools"].is_object()) {
1279 auto tools = json[
"tools"];
1292 json.value(
"auto_focus_proposals",
true);
1294 if (json.contains(
"stages") && json[
"stages"].is_array()) {
1296 for (
const auto& stage : json[
"stages"]) {
1298 builder_stage.
name = stage.value(
"name", std::string{});
1299 builder_stage.
summary = stage.value(
"summary", std::string{});
1300 builder_stage.
completed = stage.value(
"completed",
false);
1305 return absl::OkStatus();
1308 return absl::UnimplementedError(
"Blueprint import requires JSON support");
1313#if defined(YAZE_WITH_JSON)
1315 if (!dir_status.ok())
return dir_status;
1317 std::filesystem::path profile_path =
1319 std::ofstream file(profile_path);
1320 if (!file.is_open()) {
1321 return absl::InternalError(
"Failed to open profile file for writing");
1328 return absl::UnimplementedError(
1329 "JSON support required for profile management");
1334#if defined(YAZE_WITH_JSON)
1335 std::filesystem::path profile_path =
1337 if (!std::filesystem::exists(profile_path)) {
1338 return absl::NotFoundError(absl::StrFormat(
"Profile '%s' not found", name));
1341 std::ifstream file(profile_path);
1342 if (!file.is_open()) {
1343 return absl::InternalError(
"Failed to open profile file");
1346 std::string json_content((std::istreambuf_iterator<char>(file)),
1347 std::istreambuf_iterator<char>());
1350 if (!profile_or.ok()) {
1351 return profile_or.status();
1370 return absl::OkStatus();
1372 return absl::UnimplementedError(
1373 "JSON support required for profile management");
1378 std::filesystem::path profile_path =
1380 if (!std::filesystem::exists(profile_path)) {
1381 return absl::NotFoundError(absl::StrFormat(
"Profile '%s' not found", name));
1384 std::filesystem::remove(profile_path);
1412#if defined(YAZE_WITH_JSON)
1414 if (!status.ok())
return status;
1416 std::ofstream file(path);
1417 if (!file.is_open()) {
1418 return absl::InternalError(
"Failed to open file for export");
1421 return absl::OkStatus();
1425 return absl::UnimplementedError(
"JSON support required");
1430#if defined(YAZE_WITH_JSON)
1431 if (!std::filesystem::exists(path)) {
1432 return absl::NotFoundError(
"Import file not found");
1435 std::ifstream file(path);
1436 if (!file.is_open()) {
1437 return absl::InternalError(
"Failed to open import file");
1440 std::string json_content((std::istreambuf_iterator<char>(file)),
1441 std::istreambuf_iterator<char>());
1444 if (!profile_or.ok()) {
1445 return profile_or.status();
1451 return absl::UnimplementedError(
"JSON support required");
1457 if (!config_dir.ok()) {
1458 return std::filesystem::current_path() /
".yaze" /
"agent" /
"profiles";
1460 return *config_dir /
"agent" /
"profiles";
1466 std::filesystem::create_directories(dir, ec);
1468 return absl::InternalError(
1469 absl::StrFormat(
"Failed to create profiles directory: %s",
1472 return absl::OkStatus();
1476#if defined(YAZE_WITH_JSON)
1477 nlohmann::json json;
1478 json[
"name"] = profile.
name;
1480 json[
"provider"] = profile.
provider;
1481 json[
"model"] = profile.
model;
1486 json[
"verbose"] = profile.
verbose;
1491 json[
"top_p"] = profile.
top_p;
1494 json[
"tags"] = profile.
tags;
1495 json[
"created_at"] = absl::FormatTime(absl::RFC3339_full, profile.
created_at,
1496 absl::UTCTimeZone());
1497 json[
"modified_at"] = absl::FormatTime(
1498 absl::RFC3339_full, profile.
modified_at, absl::UTCTimeZone());
1500 return json.dump(2);
1507#if defined(YAZE_WITH_JSON)
1509 nlohmann::json json = nlohmann::json::parse(json_str);
1512 profile.
name = json.value(
"name",
"Unnamed Profile");
1513 profile.
description = json.value(
"description",
"");
1514 profile.
provider = json.value(
"provider",
"mock");
1515 profile.
model = json.value(
"model",
"");
1516 profile.
ollama_host = json.value(
"ollama_host",
"http://localhost:11434");
1520 profile.
verbose = json.value(
"verbose",
false);
1524 profile.
temperature = json.value(
"temperature", 0.25f);
1525 profile.
top_p = json.value(
"top_p", 0.95f);
1529 if (json.contains(
"tags") && json[
"tags"].is_array()) {
1530 for (
const auto& tag : json[
"tags"]) {
1531 profile.
tags.push_back(tag.get<std::string>());
1535 if (json.contains(
"created_at")) {
1537 if (absl::ParseTime(absl::RFC3339_full,
1538 json[
"created_at"].get<std::string>(), &created,
1544 if (json.contains(
"modified_at")) {
1545 absl::Time modified;
1546 if (absl::ParseTime(absl::RFC3339_full,
1547 json[
"modified_at"].get<std::string>(), &modified,
1554 }
catch (
const std::exception& e) {
1555 return absl::InternalError(
1556 absl::StrFormat(
"Failed to parse profile JSON: %s", e.what()));
1559 return absl::UnimplementedError(
"JSON support required");
1582 auto status = service->ConfigureProvider(provider_config);
1587 auto agent_cfg = service->GetConfig();
1590 agent_cfg.verbose = config.
verbose;
1592 service->SetConfig(agent_cfg);
1622 if (!session_or.ok())
return session_or.status();
1636 absl::StrFormat(
"Hosting local session: %s", session_name),
1642#ifdef YAZE_WITH_GRPC
1644 if (!network_coordinator_) {
1645 return absl::FailedPreconditionError(
1646 "Network coordinator not initialized. Connect to a server first.");
1649 const char* username = std::getenv(
"USER");
1651 username = std::getenv(
"USERNAME");
1654 username =
"unknown";
1658 network_coordinator_->HostSession(session_name, username);
1659 if (!session_or.ok())
return session_or.status();
1673 absl::StrFormat(
"Hosting network session: %s", session_name),
1681 return absl::InvalidArgumentError(
"Unsupported collaboration mode");
1689 if (!session_or.ok())
return session_or.status();
1703 absl::StrFormat(
"Joined local session: %s", session_code),
1710#ifdef YAZE_WITH_GRPC
1712 if (!network_coordinator_) {
1713 return absl::FailedPreconditionError(
1714 "Network coordinator not initialized. Connect to a server first.");
1717 const char* username = std::getenv(
"USER");
1719 username = std::getenv(
"USERNAME");
1722 username =
"unknown";
1725 auto session_or = network_coordinator_->JoinSession(session_code, username);
1726 if (!session_or.ok())
return session_or.status();
1740 absl::StrFormat(
"Joined network session: %s", session_code),
1748 return absl::InvalidArgumentError(
"Unsupported collaboration mode");
1753 return absl::FailedPreconditionError(
"Not in a session");
1758 if (!status.ok())
return status;
1760#ifdef YAZE_WITH_GRPC
1762 if (network_coordinator_) {
1763 auto status = network_coordinator_->LeaveSession();
1764 if (!status.ok())
return status;
1778 return absl::OkStatus();
1783 return absl::FailedPreconditionError(
"Not in a session");
1788 if (!session_or.ok())
return session_or.status();
1806#ifdef YAZE_WITH_GRPC
1807 using yaze::test::CaptureActiveWindow;
1808 using yaze::test::CaptureHarnessScreenshot;
1809 using yaze::test::CaptureWindowByName;
1811 absl::StatusOr<yaze::test::ScreenshotArtifact> result;
1812 switch (config.
mode) {
1814 result = CaptureHarnessScreenshot(
"");
1817 result = CaptureActiveWindow(
"");
1819 result = CaptureHarnessScreenshot(
"");
1826 result = CaptureActiveWindow(
"");
1829 result = CaptureHarnessScreenshot(
"");
1836 return result.status();
1838 *output_path = result->file_path;
1839 return absl::OkStatus();
1843 return absl::UnimplementedError(
"Screenshot capture requires YAZE_WITH_GRPC");
1848#ifdef YAZE_WITH_GRPC
1849 const char* api_key =
1851 ? std::getenv(
"GEMINI_API_KEY")
1853 if (!api_key || std::strlen(api_key) == 0) {
1854 return absl::FailedPreconditionError(
1855 "Gemini API key not configured (set GEMINI_API_KEY)");
1867 if (!response.ok()) {
1868 return response.status();
1874 auto history = service->GetHistory();
1877 agent_msg.
message = response->text_response;
1879 history.push_back(agent_msg);
1880 service->ReplaceHistory(history);
1888 return absl::OkStatus();
1892 return absl::UnimplementedError(
"Gemini integration requires YAZE_WITH_GRPC");
1896#ifdef YAZE_WITH_GRPC
1897absl::Status AgentEditor::ConnectToServer(
const std::string& server_url) {
1899 network_coordinator_ =
1900 std::make_unique<NetworkCollaborationCoordinator>(server_url);
1904 absl::StrFormat(
"Connected to server: %s", server_url),
1908 return absl::OkStatus();
1909 }
catch (
const std::exception& e) {
1910 return absl::InternalError(
1911 absl::StrFormat(
"Failed to connect to server: %s", e.what()));
1915void AgentEditor::DisconnectFromServer() {
1919 network_coordinator_.reset();
1926bool AgentEditor::IsConnectedToServer()
const {
1927 return network_coordinator_ && network_coordinator_->IsConnected();
static absl::StatusOr< std::string > LoadTextFile(const std::string &relative_path)
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::StatusOr< AgentResponse > GenerateMultimodalResponse(const std::string &, const std::string &)
std::string current_session_id_
void SetCurrentProfile(const BotProfile &profile)
void ApplyConfig(const AgentConfig &config)
void InitializeWithDependencies(ToastManager *toast_manager, ProposalDrawer *proposal_drawer, Rom *rom)
absl::Status SaveBuilderBlueprint(const std::filesystem::path &path)
void DrawAdvancedMetricsPanel()
absl::StatusOr< SessionInfo > JoinSession(const std::string &session_code, CollaborationMode mode=CollaborationMode::kLocal)
void SetRomContext(Rom *rom)
void SetChatActive(bool active)
void SetupAutomationCallbacks()
KnowledgePanelCallback knowledge_panel_callback_
absl::StatusOr< BotProfile > JsonToProfile(const std::string &json) const
absl::StatusOr< SessionInfo > RefreshSession()
absl::Status ImportProfile(const std::filesystem::path &path)
bool IsChatActive() const
void SetupMultimodalCallbacks()
bool prompt_editor_initialized_
void Initialize() override
ProposalDrawer * proposal_drawer_
std::vector< cli::agent::ChatMessage > cached_history_
AgentBuilderState builder_state_
AgentConfig current_config_
absl::Status Save() override
bool common_tiles_initialized_
std::string current_session_name_
CollaborationMode GetCurrentMode() const
void DrawPromptEditorPanel()
absl::Status SendToGemini(const std::filesystem::path &image_path, const std::string &prompt)
absl::Status EnsureProfilesDirectory()
absl::Status LoadBuilderBlueprint(const std::filesystem::path &path)
bool history_needs_refresh_
absl::Status ExportProfile(const BotProfile &profile, const std::filesystem::path &path)
void DrawNewPromptCreator()
CollaborationMode current_mode_
absl::Status SaveBotProfile(const BotProfile &profile)
std::string active_prompt_file_
absl::Status DeleteBotProfile(const std::string &name)
std::filesystem::path GetProfilesDirectory() const
ToastManager * toast_manager_
AgentConfig GetCurrentConfig() const
std::unique_ptr< TextEditor > common_tiles_editor_
absl::StatusOr< SessionInfo > HostSession(const std::string &session_name, CollaborationMode mode=CollaborationMode::kLocal)
void DrawCommonTilesEditor()
char new_prompt_name_[128]
std::string ProfileToJson(const BotProfile &profile) const
absl::Status LeaveSession()
std::unique_ptr< TextEditor > prompt_editor_
std::unique_ptr< AgentCollaborationCoordinator > local_coordinator_
absl::Status CaptureSnapshot(std::filesystem::path *output_path, const CaptureConfig &config)
absl::Status Load() override
BotProfile current_profile_
absl::Status LoadBotProfile(const std::string &name)
std::vector< BotProfile > GetAllProfiles() const
void DrawChatHistoryViewer()
std::vector< std::string > current_participants_
void DrawBotProfilesPanel()
std::unique_ptr< AgentChat > agent_chat_
absl::Status Update() override
void DrawConfigurationPanel()
std::vector< BotProfile > loaded_profiles_
std::optional< SessionInfo > GetCurrentSession() const
void DrawAgentBuilderPanel()
EditorDependencies dependencies_
void RegisterEditorPanel(std::unique_ptr< EditorPanel > panel)
Register an EditorPanel instance for central drawing.
ImGui drawer for displaying and managing agent proposals.
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
static TestManager & Get()
#define ICON_MD_FOLDER_OPEN
#define ICON_MD_AUTO_AWESOME
#define ICON_MD_TIPS_AND_UPDATES
#define ICON_MD_AUTO_FIX_HIGH
#define ICON_MD_FILE_COPY
#define ICON_MD_CHECK_CIRCLE
#define ICON_MD_DELETE_FOREVER
#define ICON_MD_ANALYTICS
#define ICON_MD_SMART_TOY
void RenderStatusIndicator(const char *label, bool active)
bool StyledButton(const char *label, const ImVec4 &color, const ImVec2 &size)
const AgentUITheme & GetTheme()
void RenderSectionHeader(const char *icon, const char *label, const ImVec4 &color)
void RenderProviderBadge(const char *provider)
void HelpMarker(const char *desc)
static const LanguageDefinition & CPlusPlus()
std::string gemini_api_key
std::string openai_api_key
struct yaze::editor::AgentEditor::AgentBuilderState::ToolPlan tools
std::string persona_notes
std::vector< std::string > goals
std::vector< Stage > stages
bool auto_focus_proposals
std::string blueprint_path
std::string gemini_api_key
std::string openai_api_key
std::vector< std::string > tags
std::string openai_api_key
std::string gemini_api_key
std::string system_prompt
std::string specific_window_name
std::vector< std::string > participants
PanelManager * panel_manager