#include "InternalTools.hpp" #include namespace celuna { InternalTools::InternalTools(grove::IIO* io) : m_io(io) { m_logger = spdlog::get("InternalTools"); if (!m_logger) { m_logger = spdlog::stdout_color_mt("InternalTools"); } m_bridge = std::make_unique(io); m_bridge->subscribeToResponses(); registerTools(); m_logger->info("InternalTools initialized with {} tools", m_tools.size()); } void InternalTools::registerTools() { // ======================================================================== // SCHEDULER TOOLS // ======================================================================== m_tools.push_back({ "get_current_task", "Get the task currently being worked on. Returns task name, duration, and status.", { {"type", "object"}, {"properties", json::object()}, {"required", json::array()} }, [this](const json& input) { return getCurrentTask(input); } }); m_tools.push_back({ "list_tasks", "List all planned tasks for today. Returns task names, estimated durations, and completion status.", { {"type", "object"}, {"properties", { {"include_completed", { {"type", "boolean"}, {"description", "Whether to include completed tasks"}, {"default", false} }} }}, {"required", json::array()} }, [this](const json& input) { return listTasks(input); } }); m_tools.push_back({ "start_task", "Start working on a specific task by its ID or name.", { {"type", "object"}, {"properties", { {"task_id", { {"type", "string"}, {"description", "Task ID or name to start"} }} }}, {"required", {"task_id"}} }, [this](const json& input) { return startTask(input); } }); m_tools.push_back({ "complete_task", "Mark the current task or a specific task as completed.", { {"type", "object"}, {"properties", { {"task_id", { {"type", "string"}, {"description", "Task ID to complete. If not provided, completes current task."} }} }}, {"required", json::array()} }, [this](const json& input) { return completeTask(input); } }); m_tools.push_back({ "start_break", "Start a break period. The user will be reminded when break is over.", { {"type", "object"}, {"properties", { {"duration_minutes", { {"type", "integer"}, {"description", "Break duration in minutes"}, {"default", 10} }}, {"reason", { {"type", "string"}, {"description", "Reason for break (coffee, stretch, etc.)"} }} }}, {"required", json::array()} }, [this](const json& input) { return startBreak(input); } }); // ======================================================================== // MONITORING TOOLS // ======================================================================== m_tools.push_back({ "get_focus_stats", "Get focus statistics: time spent on productive vs distracting apps, current session duration.", { {"type", "object"}, {"properties", { {"period", { {"type", "string"}, {"enum", {"today", "week", "month"}}, {"description", "Time period for stats"}, {"default", "today"} }} }}, {"required", json::array()} }, [this](const json& input) { return getFocusStats(input); } }); m_tools.push_back({ "get_current_app", "Get the currently active application name and window title.", { {"type", "object"}, {"properties", json::object()}, {"required", json::array()} }, [this](const json& input) { return getCurrentApp(input); } }); // ======================================================================== // STORAGE TOOLS // ======================================================================== m_tools.push_back({ "save_note", "Save a note or reminder for the user. Notes are searchable and timestamped.", { {"type", "object"}, {"properties", { {"content", { {"type", "string"}, {"description", "Note content to save"} }}, {"tags", { {"type", "array"}, {"items", {{"type", "string"}}}, {"description", "Optional tags for categorization"} }} }}, {"required", {"content"}} }, [this](const json& input) { return saveNote(input); } }); m_tools.push_back({ "query_notes", "Search through saved notes by keyword or tag.", { {"type", "object"}, {"properties", { {"query", { {"type", "string"}, {"description", "Search query"} }}, {"tag", { {"type", "string"}, {"description", "Filter by tag"} }}, {"limit", { {"type", "integer"}, {"description", "Maximum number of notes to return"}, {"default", 10} }} }}, {"required", json::array()} }, [this](const json& input) { return queryNotes(input); } }); m_tools.push_back({ "get_session_history", "Get history of work sessions with durations and focus scores.", { {"type", "object"}, {"properties", { {"days", { {"type", "integer"}, {"description", "Number of days to look back"}, {"default", 7} }} }}, {"required", json::array()} }, [this](const json& input) { return getSessionHistory(input); } }); // ======================================================================== // VOICE TOOLS // ======================================================================== m_tools.push_back({ "speak", "Make the assistant speak a message out loud using text-to-speech.", { {"type", "object"}, {"properties", { {"message", { {"type", "string"}, {"description", "Message to speak"} }}, {"priority", { {"type", "string"}, {"enum", {"low", "normal", "high"}}, {"description", "Speech priority"}, {"default", "normal"} }} }}, {"required", {"message"}} }, [this](const json& input) { return speak(input); } }); } std::vector InternalTools::getTools() const { return m_tools; } void InternalTools::processResponses() { if (!m_io) return; while (m_io->hasMessages() > 0) { auto msg = m_io->pullMessage(); // Check if this is a response message if (msg.topic.find(":response") != std::string::npos && msg.data) { m_bridge->handleResponse(msg.topic, *msg.data); } } } // ============================================================================ // TOOL IMPLEMENTATIONS // ============================================================================ json InternalTools::getCurrentTask(const json& input) { auto response = m_bridge->request("scheduler:query", { {"action", "get_current_task"} }); if (response.contains("error")) { return response; } return { {"task_id", response.value("task_id", "")}, {"task_name", response.value("task_name", "No active task")}, {"duration_minutes", response.value("duration_minutes", 0)}, {"started_at", response.value("started_at", "")} }; } json InternalTools::listTasks(const json& input) { bool includeCompleted = input.value("include_completed", false); auto response = m_bridge->request("scheduler:query", { {"action", "list_tasks"}, {"include_completed", includeCompleted} }); return response; } json InternalTools::startTask(const json& input) { std::string taskId = input.value("task_id", ""); if (taskId.empty()) { return {{"error", "missing_parameter"}, {"message", "task_id is required"}}; } auto response = m_bridge->request("scheduler:command", { {"action", "start_task"}, {"task_id", taskId} }); return response; } json InternalTools::completeTask(const json& input) { std::string taskId = input.value("task_id", ""); auto response = m_bridge->request("scheduler:command", { {"action", "complete_task"}, {"task_id", taskId} // Empty = complete current task }); return response; } json InternalTools::startBreak(const json& input) { int duration = input.value("duration_minutes", 10); std::string reason = input.value("reason", "break"); auto response = m_bridge->request("scheduler:command", { {"action", "start_break"}, {"duration_minutes", duration}, {"reason", reason} }); return { {"success", true}, {"message", "Break started for " + std::to_string(duration) + " minutes"}, {"reason", reason} }; } json InternalTools::getFocusStats(const json& input) { std::string period = input.value("period", "today"); auto response = m_bridge->request("monitoring:query", { {"action", "get_focus_stats"}, {"period", period} }); return response; } json InternalTools::getCurrentApp(const json& input) { auto response = m_bridge->request("monitoring:query", { {"action", "get_current_app"} }); return response; } json InternalTools::saveNote(const json& input) { std::string content = input.value("content", ""); if (content.empty()) { return {{"error", "missing_parameter"}, {"message", "content is required"}}; } json tags = input.value("tags", json::array()); auto response = m_bridge->request("storage:command", { {"action", "save_note"}, {"content", content}, {"tags", tags} }); return { {"success", true}, {"message", "Note saved"}, {"note_id", response.value("note_id", "")} }; } json InternalTools::queryNotes(const json& input) { std::string query = input.value("query", ""); std::string tag = input.value("tag", ""); int limit = input.value("limit", 10); auto response = m_bridge->request("storage:query", { {"action", "query_notes"}, {"query", query}, {"tag", tag}, {"limit", limit} }); return response; } json InternalTools::getSessionHistory(const json& input) { int days = input.value("days", 7); auto response = m_bridge->request("storage:query", { {"action", "get_session_history"}, {"days", days} }); return response; } json InternalTools::speak(const json& input) { std::string message = input.value("message", ""); if (message.empty()) { return {{"error", "missing_parameter"}, {"message", "message is required"}}; } std::string priority = input.value("priority", "normal"); // For speak, we don't wait for response - it's fire and forget auto requestNode = std::make_unique("request"); requestNode->setString("action", "speak"); requestNode->setString("text", message); requestNode->setString("priority", priority); m_io->publish("voice:command", std::move(requestNode)); return { {"success", true}, {"message", "Speech queued"} }; } } // namespace celuna