- Renamed project from Celuna to AISSIA - Updated all documentation and configuration files - Codebase improvements and fixes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
413 lines
13 KiB
C++
413 lines
13 KiB
C++
#include "InternalTools.hpp"
|
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
|
|
|
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<IOBridge>(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<ToolDefinition> 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<grove::JsonDataNode>("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
|