This commit is contained in:
retoor 2026-01-29 02:21:11 +01:00
parent c641d0835a
commit 66f17da391
44 changed files with 1792 additions and 4779 deletions

381
agent.h
View File

@ -1,381 +0,0 @@
// retoor <retoor@molodetz.nl>
#ifndef R_AGENT_H
#define R_AGENT_H
#include "chat.h"
#include "http_curl.h"
#include "messages.h"
#include "r.h"
#include "tools.h"
#include <stdbool.h>
#include <string.h>
#include <time.h>
#define AGENT_MAX_ITERATIONS 300
#define AGENT_MAX_TOOL_RETRIES 3
typedef enum {
AGENT_STATUS_RUNNING,
AGENT_STATUS_COMPLETED,
AGENT_STATUS_MAX_ITERATIONS,
AGENT_STATUS_ERROR,
AGENT_STATUS_TOOL_ERROR
} agent_status_t;
typedef struct {
char *goal;
int iteration_count;
int max_iterations;
int tool_retry_count;
int max_tool_retries;
agent_status_t status;
time_t start_time;
char *last_error;
bool verbose;
} agent_state_t;
static agent_state_t *current_agent = NULL;
agent_state_t *agent_create(const char *goal) {
agent_state_t *agent = (agent_state_t *)malloc(sizeof(agent_state_t));
if (agent == NULL) {
return NULL;
}
agent->goal = goal ? strdup(goal) : NULL;
agent->iteration_count = 0;
agent->max_iterations = AGENT_MAX_ITERATIONS;
agent->tool_retry_count = 0;
agent->max_tool_retries = AGENT_MAX_TOOL_RETRIES;
agent->status = AGENT_STATUS_RUNNING;
agent->start_time = time(NULL);
agent->last_error = NULL;
agent->verbose = is_verbose;
return agent;
}
void agent_destroy(agent_state_t *agent) {
if (agent == NULL) {
return;
}
if (agent->goal) {
free(agent->goal);
}
if (agent->last_error) {
free(agent->last_error);
}
free(agent);
}
void agent_set_error(agent_state_t *agent, const char *error) {
if (agent == NULL) {
return;
}
if (agent->last_error) {
free(agent->last_error);
}
agent->last_error = error ? strdup(error) : NULL;
}
static struct json_object *agent_process_response(const char *api_url,
const char *json_data) {
char *response = curl_post(api_url, json_data);
if (!response) {
return NULL;
}
struct json_object *parsed_json = json_tokener_parse(response);
free(response);
if (!parsed_json) {
return NULL;
}
struct json_object *error_object;
if (json_object_object_get_ex(parsed_json, "error", &error_object)) {
const char *err_str = json_object_to_json_string(error_object);
fprintf(stderr, "API Error: %s\n", err_str);
json_object_put(parsed_json);
return NULL;
}
struct json_object *choices_array;
if (!json_object_object_get_ex(parsed_json, "choices", &choices_array)) {
json_object_put(parsed_json);
return NULL;
}
struct json_object *first_choice = json_object_array_get_idx(choices_array, 0);
if (!first_choice) {
json_object_put(parsed_json);
return NULL;
}
return first_choice;
}
static bool agent_has_tool_calls(struct json_object *choice) {
struct json_object *message_obj;
if (!json_object_object_get_ex(choice, "message", &message_obj)) {
return false;
}
struct json_object *tool_calls;
if (!json_object_object_get_ex(message_obj, "tool_calls", &tool_calls)) {
return false;
}
return json_object_array_length(tool_calls) > 0;
}
static const char *agent_get_finish_reason(struct json_object *choice) {
struct json_object *finish_reason_obj;
if (json_object_object_get_ex(choice, "finish_reason", &finish_reason_obj)) {
return json_object_get_string(finish_reason_obj);
}
return NULL;
}
static char *agent_get_content(struct json_object *choice) {
struct json_object *message_obj;
if (!json_object_object_get_ex(choice, "message", &message_obj)) {
return NULL;
}
struct json_object *content_obj;
if (!json_object_object_get_ex(message_obj, "content", &content_obj)) {
return NULL;
}
const char *content = json_object_get_string(content_obj);
return content ? strdup(content) : NULL;
}
static struct json_object *agent_get_tool_calls(struct json_object *choice) {
struct json_object *message_obj;
if (!json_object_object_get_ex(choice, "message", &message_obj)) {
return NULL;
}
struct json_object *tool_calls;
if (!json_object_object_get_ex(message_obj, "tool_calls", &tool_calls)) {
return NULL;
}
return tool_calls;
}
static struct json_object *agent_get_message(struct json_object *choice) {
struct json_object *message_obj;
if (json_object_object_get_ex(choice, "message", &message_obj)) {
return message_obj;
}
return NULL;
}
static bool agent_response_indicates_incomplete(const char *content) {
if (content == NULL) {
return false;
}
const char *incomplete_phrases[] = {
"I'll ", "I will ", "Let me ", "I'm going to ",
"Next, I", "Now I'll", "Now I will", "I'll now",
"I need to", "I should", "I can ", "Going to ",
"Will now", "Proceeding", "Starting to", "About to ",
"First, I", "Then I", "After that", "Following that",
NULL
};
for (int i = 0; incomplete_phrases[i] != NULL; i++) {
if (strstr(content, incomplete_phrases[i]) != NULL) {
return true;
}
}
const char *incomplete_endings[] = {
"...", ":", "files:", "content:", "implementation:",
NULL
};
size_t len = strlen(content);
if (len > 3) {
for (int i = 0; incomplete_endings[i] != NULL; i++) {
size_t end_len = strlen(incomplete_endings[i]);
if (len >= end_len && strcmp(content + len - end_len, incomplete_endings[i]) == 0) {
return true;
}
}
}
return false;
}
char *agent_run(agent_state_t *agent, const char *user_message) {
if (agent == NULL) {
return NULL;
}
current_agent = agent;
agent->status = AGENT_STATUS_RUNNING;
agent->iteration_count = 0;
agent->tool_retry_count = 0;
if (user_message == NULL || *user_message == '\0') {
agent->status = AGENT_STATUS_ERROR;
agent_set_error(agent, "Empty user message");
return NULL;
}
char *json_data = chat_json("user", user_message);
if (json_data == NULL) {
agent->status = AGENT_STATUS_ERROR;
agent_set_error(agent, "Failed to create chat JSON");
return NULL;
}
char *final_response = NULL;
while (agent->status == AGENT_STATUS_RUNNING) {
agent->iteration_count++;
if (agent->iteration_count > agent->max_iterations) {
agent->status = AGENT_STATUS_MAX_ITERATIONS;
agent_set_error(agent, "Maximum iterations reached");
if (agent->verbose) {
fprintf(stderr, "[Agent] Max iterations (%d) reached\n",
agent->max_iterations);
}
break;
}
if (agent->verbose) {
fprintf(stderr, "[Agent] Iteration %d/%d\n", agent->iteration_count,
agent->max_iterations);
}
struct json_object *choice =
agent_process_response(get_completions_api_url(), json_data);
if (choice == NULL) {
agent->tool_retry_count++;
if (agent->tool_retry_count >= agent->max_tool_retries) {
agent->status = AGENT_STATUS_ERROR;
agent_set_error(agent, "API request failed after retries");
break;
}
if (agent->verbose) {
fprintf(stderr, "[Agent] API error, retry %d/%d\n",
agent->tool_retry_count, agent->max_tool_retries);
}
continue;
}
agent->tool_retry_count = 0;
struct json_object *message_obj = agent_get_message(choice);
if (message_obj) {
message_add_object(json_object_get(message_obj));
}
const char *finish_reason = agent_get_finish_reason(choice);
bool has_tools = agent_has_tool_calls(choice);
if (agent->verbose) {
fprintf(stderr, "[Agent] finish_reason=%s, has_tool_calls=%s\n",
finish_reason ? finish_reason : "null",
has_tools ? "true" : "false");
}
if (has_tools) {
struct json_object *tool_calls = agent_get_tool_calls(choice);
if (agent->verbose) {
int num_tools = json_object_array_length(tool_calls);
fprintf(stderr, "[Agent] Executing %d tool(s)\n", num_tools);
}
struct json_object *tool_results = tools_execute(tool_calls);
int results_count = json_object_array_length(tool_results);
for (int i = 0; i < results_count; i++) {
struct json_object *tool_result =
json_object_array_get_idx(tool_results, i);
message_add_tool_call(json_object_get(tool_result));
}
json_data = chat_json(NULL, NULL);
if (json_data == NULL) {
agent->status = AGENT_STATUS_ERROR;
agent_set_error(agent, "Failed to create follow-up JSON");
break;
}
} else {
char *content = agent_get_content(choice);
if (content && agent_response_indicates_incomplete(content)) {
if (agent->verbose) {
fprintf(stderr, "[Agent] Response indicates incomplete work, auto-continuing\n");
}
free(content);
json_data = chat_json("user", "Continue. Execute the necessary actions to complete the task.");
if (json_data == NULL) {
agent->status = AGENT_STATUS_ERROR;
agent_set_error(agent, "Failed to create continue JSON");
break;
}
} else {
final_response = content;
agent->status = AGENT_STATUS_COMPLETED;
if (agent->verbose) {
fprintf(stderr, "[Agent] Completed in %d iteration(s)\n",
agent->iteration_count);
}
}
}
}
current_agent = NULL;
return final_response;
}
char *agent_chat(const char *user_message) {
agent_state_t *agent = agent_create(user_message);
if (agent == NULL) {
return NULL;
}
char *response = agent_run(agent, user_message);
if (agent->verbose && agent->status != AGENT_STATUS_COMPLETED && agent->last_error) {
fprintf(stderr, "[Agent] Error: %s\n", agent->last_error);
}
agent_destroy(agent);
return response;
}
char *agent_chat_with_limit(const char *user_message, int max_iterations) {
agent_state_t *agent = agent_create(user_message);
if (agent == NULL) {
return NULL;
}
agent->max_iterations = max_iterations;
char *response = agent_run(agent, user_message);
if (agent->verbose && agent->status != AGENT_STATUS_COMPLETED && agent->last_error) {
fprintf(stderr, "[Agent] Error: %s\n", agent->last_error);
}
agent_destroy(agent);
return response;
}
#endif

75
auth.h
View File

@ -1,75 +0,0 @@
// Written by retoor@molodetz.nl
// This source code retrieves an API key from environment variables or defaults
// to a hardcoded key if none are found.
// Uses the C standard library functions from stdlib.h for environment
// management and stdio.h for error handling.
// MIT License
#ifndef R_AUTH_H
#define R_AUTH_H
#include <stdio.h>
#include <stdlib.h>
enum AUTH_TYPE { AUTH_TYPE_NONE, AUTH_TYPE_API_KEY, AUTH_TYPE_FREE };
int auth_type = AUTH_TYPE_NONE;
void auth_free() { auth_type = AUTH_TYPE_FREE; }
void auth_init() {
char *api_key = NULL;
if (auth_type != AUTH_TYPE_FREE) {
api_key = getenv("R_KEY");
if (api_key) {
auth_type = AUTH_TYPE_API_KEY;
return;
}
api_key = getenv("OPENROUTER_API_KEY");
if (api_key) {
auth_type = AUTH_TYPE_API_KEY;
return;
}
api_key = getenv("OPENAI_API_KEY");
if (api_key) {
auth_type = AUTH_TYPE_API_KEY;
return;
}
}
auth_type = AUTH_TYPE_FREE;
return;
}
const char *resolve_api_key() {
static char *api_key = NULL;
if (auth_type != AUTH_TYPE_FREE) {
api_key = getenv("R_KEY");
if (api_key) {
auth_type = AUTH_TYPE_API_KEY;
return api_key;
}
api_key = getenv("OPENROUTER_API_KEY");
if (api_key) {
auth_type = AUTH_TYPE_API_KEY;
return api_key;
}
api_key = getenv("OPENAI_API_KEY");
if (api_key) {
auth_type = AUTH_TYPE_API_KEY;
return api_key;
}
}
auth_type = AUTH_TYPE_FREE;
api_key = "sk-proj-d798HLfWYBeB9HT_o7isaY0s88631IaYhhOR5IVAd4D_fF-"
"SQ5z46BCr8iDi1ang1rUmlagw55T3BlbkFJ6IOsqhAxNN9Zt6ERDBnv2p2HCc2fDgc"
"5DsNhPxdOzYb009J6CNd4wILPsFGEoUdWo4QrZ1eOkA";
return api_key;
}
#endif

View File

@ -1,17 +0,0 @@
#include "browse.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
char *query = "example";
char *result = get_news(query);
if (result) {
printf("Result: %s\n", result);
free(result); // Free the allocated memory for the result
} else {
fprintf(stderr, "Error: Failed to fetch news.\n");
}
return 0;
}

View File

@ -1,23 +0,0 @@
#include "http_curl.h"
#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *url_encode(char *s) { return curl_easy_escape(NULL, s, 0); }
char *web_search(char *q) {
char url[4096];
char *q_encoded = url_encode(q);
if (!q_encoded) {
return NULL;
}
snprintf(url, sizeof(url), "https://static.molodetz.nl/search.cgi?query=%s", q_encoded);
free(q_encoded);
char *ret = curl_get(url);
return ret;
}
char *web_search_news(char *q) {
return web_search(q);
}

71
chat.h
View File

@ -1,71 +0,0 @@
// Written by retoor@molodetz.nl
// This code defines functionality for creating and managing JSON-based chat
// prompts using a specific AI model configuration, providing easy integration
// with message handling and HTTP communication for dynamic applications.
// Non-standard imports: json-c library for handling JSON objects.
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef R_PROMPT_H
#define R_PROMPT_H
#include "auth.h"
#include "messages.h"
#include "r.h"
#include "tools.h"
#include <json-c/json.h>
static json_object *_prompt = NULL;
void chat_free() {
if (_prompt == NULL)
return;
json_object_put(_prompt);
_prompt = NULL;
}
char *chat_json(const char *role, const char *message) {
chat_free();
json_object *root_object = json_object_new_object();
if (root_object == NULL) {
return NULL;
}
json_object_object_add(root_object, "model",
json_object_new_string(get_prompt_model()));
if (role != NULL && message != NULL) {
message_add(role, message);
if (use_tools()) {
json_object_object_add(root_object, "tools", tools_descriptions());
}
}
json_object_object_add(root_object, "messages", json_object_get(message_list()));
json_object_object_add(root_object, "temperature",
json_object_new_double(PROMPT_TEMPERATURE));
_prompt = root_object;
return (char *)json_object_to_json_string_ext(root_object,
JSON_C_TO_STRING_PRETTY);
}
#endif

View File

@ -1,64 +0,0 @@
#include "db_utils.h"
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
void db_initialize() {
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("database.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Error: Cannot open database: %s\n", sqlite3_errmsg(db));
return;
}
const char *sql =
"CREATE TABLE IF NOT EXISTS kv_store (key TEXT PRIMARY KEY, value TEXT);";
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
if (rc != SQLITE_OK) {
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
}
sqlite3_close(db);
}
void test_db_set() {
json_object *result = db_set("test_key", "test_value");
if (result) {
printf("db_set: %s\n", json_object_get_string(result));
json_object_put(result);
} else {
printf("db_set failed\n");
}
}
void test_db_get() {
json_object *result = db_get("test_key");
if (result) {
printf("db_get: %s\n", json_object_to_json_string(result));
json_object_put(result);
} else {
printf("db_get failed\n");
}
}
void test_db_query() {
json_object *result = db_query("SELECT * FROM kv_store");
if (result) {
printf("db_query: %s\n", json_object_to_json_string(result));
json_object_put(result);
} else {
printf("db_query failed\n");
}
}
int main() {
db_initialize();
test_db_set();
test_db_get();
test_db_query();
return 0;
}

View File

@ -1,236 +0,0 @@
#ifndef DB_UTILS_H
#define DB_UTILS_H
#include "r.h"
#include "utils.h"
#include <json-c/json.h>
#include <sqlite3.h>
json_object *db_execute(const char *query);
char *db_file_expanded() {
char *expanded = expand_home_directory(DB_FILE);
static char result[4096];
result[0] = 0;
strcpy(result, expanded);
free(expanded);
return result;
}
void db_initialize();
json_object *db_set(const char *key, const char *value);
json_object *db_get(const char *key);
json_object *db_query(const char *query);
void db_initialize() {
sqlite3 *db;
int rc = sqlite3_open(db_file_expanded(), &db);
if (rc != SQLITE_OK) {
return;
}
db_execute("CREATE TABLE IF NOT EXISTS kv_store (key TEXT PRIMARY KEY, value "
"TEXT);");
db_execute("CREATE TABLE IF NOT EXISTS file_version_history ( id INTEGER "
"PRIMARY KEY AUTOINCREMENT,"
"path TEXT NOT NULL,"
"content TEXT,"
"date DATETIME DEFAULT CURRENT_TIMESTAMP"
");");
sqlite3_close(db);
}
json_object *db_set(const char *key, const char *value) {
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open(db_file_expanded(), &db);
if (rc != SQLITE_OK) {
return NULL;
}
char *sql =
sqlite3_mprintf("INSERT INTO kv_store (key, value) VALUES (%Q, %Q) ON "
"CONFLICT(key) DO UPDATE SET value = %Q WHERE key = %Q",
key, value, value, key);
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
sqlite3_free(sql);
if (rc != SQLITE_OK) {
sqlite3_free(err_msg);
sqlite3_close(db);
return NULL;
}
sqlite3_close(db);
return json_object_new_string("Success");
}
json_object *db_get(const char *key) {
sqlite3 *db;
sqlite3_stmt *stmt;
json_object *result = json_object_new_object();
if (result == NULL) {
return NULL;
}
int rc = sqlite3_open(db_file_expanded(), &db);
if (rc != SQLITE_OK) {
json_object_put(result);
return NULL;
}
const char *sql = "SELECT value FROM kv_store WHERE key = ?";
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
sqlite3_close(db);
json_object_put(result);
return NULL;
}
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
if (sqlite3_step(stmt) == SQLITE_ROW) {
const char *value = (const char *)sqlite3_column_text(stmt, 0);
if (value) {
json_object_object_add(result, "value", json_object_new_string(value));
} else {
json_object_object_add(result, "error",
json_object_new_string("Key not found"));
}
} else {
json_object_object_add(result, "error",
json_object_new_string("Key not found"));
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return result;
}
json_object *db_query(const char *query) {
sqlite3 *db;
sqlite3_stmt *stmt;
if (strncmp(query, "SELECT", 6)) {
return db_execute(query);
}
json_object *result = json_object_new_array();
if (result == NULL) {
return NULL;
}
int rc = sqlite3_open(db_file_expanded(), &db);
if (rc != SQLITE_OK) {
json_object_put(result);
return NULL;
}
rc = sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
sqlite3_close(db);
json_object_put(result);
return NULL;
}
while (sqlite3_step(stmt) == SQLITE_ROW) {
json_object *row = json_object_new_object();
for (int i = 0; i < sqlite3_column_count(stmt); i++) {
const char *col_name = sqlite3_column_name(stmt, i);
switch (sqlite3_column_type(stmt, i)) {
case SQLITE_INTEGER:
json_object_object_add(
row, col_name,
json_object_new_int64(sqlite3_column_int64(stmt, i)));
break;
case SQLITE_FLOAT:
json_object_object_add(
row, col_name,
json_object_new_double(sqlite3_column_double(stmt, i)));
break;
case SQLITE_TEXT:
json_object_object_add(
row, col_name,
json_object_new_string((const char *)sqlite3_column_text(stmt, i)));
break;
case SQLITE_BLOB:
json_object_object_add(row, col_name,
json_object_new_string_len(
(const char *)sqlite3_column_blob(stmt, i),
sqlite3_column_bytes(stmt, i)));
break;
case SQLITE_NULL:
default:
json_object_object_add(row, col_name, json_object_new_string("NULL"));
break;
}
}
json_object_array_add(result, row);
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return result;
}
json_object *db_execute(const char *query) {
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open(db_file_expanded(), &db);
json_object *result = json_object_new_object();
if (rc != SQLITE_OK) {
json_object_object_add(result, "error",
json_object_new_string("Cannot open database"));
return result;
}
rc = sqlite3_exec(db, query, 0, 0, &err_msg);
if (rc != SQLITE_OK) {
json_object_object_add(result, "error", json_object_new_string(err_msg));
sqlite3_free(err_msg);
} else {
json_object_object_add(
result, "success",
json_object_new_string("Query executed successfully"));
}
sqlite3_close(db);
return result;
}
void db_store_file_version(const char *path) {
char *expanded = expand_home_directory(path);
if (expanded == NULL) {
return;
}
char *content = read_file(expanded);
if (!content) {
free(expanded);
return;
}
fprintf(stderr, "Creating backup:: %s\n", expanded);
char *formatted = sqlite3_mprintf(
"INSERT INTO file_version_history (path, content) VALUES (%Q, %Q)",
expanded, content);
if (formatted) {
db_execute(formatted);
sqlite3_free(formatted);
}
free(content);
free(expanded);
}
char *db_get_schema() {
json_object *tables =
db_query("SELECT * FROM sqlite_master WHERE type='table'");
if (tables == NULL) {
return strdup("[]");
}
const char *str = json_object_to_json_string(tables);
char *result = str ? strdup(str) : strdup("[]");
json_object_put(tables);
return result;
}
#endif

View File

@ -1,232 +0,0 @@
// Written by retoor@molodetz.nl
// This code defines a simple HTTP client using libcurl in C. It provides
// functions for executing POST and GET HTTP requests with JSON data, including
// authorization via a bearer token. The functions `curl_post` and `curl_get`
// handle these operations and return the server's response as a string.
// Uses libcurl for HTTP requests and includes a custom "auth.h" for API key
// resolution.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: the above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software. The Software is provided "as is",
// without warranty of any kind, express or implied, including but not limited
// to the warranties of merchantability, fitness for a particular purpose and
// noninfringement. In no event shall the authors or copyright holders be liable
// for any claim, damages or other liability, whether in an action of contract,
// tort or otherwise, arising from, out of or in connection with the software or
// the use or other dealings in the Software.
#ifndef HTTP_CURL
#define HTTP_CURL
#include "auth.h"
#include <curl/curl.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
struct ResponseBuffer {
char *data;
size_t size;
};
static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
void *userp) {
size_t total_size = size * nmemb;
struct ResponseBuffer *response = (struct ResponseBuffer *)userp;
if (total_size > SIZE_MAX - response->size - 1) {
fprintf(stderr, "Response too large\n");
return 0;
}
char *ptr = realloc(response->data, response->size + total_size + 1);
if (ptr == NULL) {
fprintf(stderr, "Failed to allocate memory for response\n");
return 0;
}
response->data = ptr;
memcpy(&(response->data[response->size]), contents, total_size);
response->size += total_size;
response->data[response->size] = '\0';
return total_size;
}
#define HTTP_MAX_RETRIES 3
#define HTTP_RETRY_DELAY_MS 2000
static struct timespec spinner_start_time = {0, 0};
static volatile int spinner_running = 0;
static double get_elapsed_seconds() {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return (now.tv_sec - spinner_start_time.tv_sec) +
(now.tv_nsec - spinner_start_time.tv_nsec) / 1e9;
}
static void *spinner_thread(void *arg) {
(void)arg;
const char *frames[] = {"", "", "", "", "", "", "", "", "", ""};
int frame = 0;
while (spinner_running) {
double elapsed = get_elapsed_seconds();
fprintf(stderr, "\r%s Querying AI... (%.1fs) ", frames[frame % 10], elapsed);
fflush(stderr);
frame++;
usleep(80000);
}
return NULL;
}
char *curl_post(const char *url, const char *data) {
CURL *curl;
CURLcode res;
struct ResponseBuffer response = {NULL, 0};
int retry_count = 0;
pthread_t spinner_tid;
clock_gettime(CLOCK_MONOTONIC, &spinner_start_time);
spinner_running = 1;
pthread_create(&spinner_tid, NULL, spinner_thread, NULL);
while (retry_count < HTTP_MAX_RETRIES) {
if (response.data) {
free(response.data);
}
response.data = malloc(1);
response.size = 0;
if (!response.data) {
spinner_running = 0;
pthread_join(spinner_tid, NULL);
return NULL;
}
response.data[0] = '\0';
curl = curl_easy_init();
if (!curl) {
free(response.data);
spinner_running = 0;
pthread_join(spinner_tid, NULL);
return NULL;
}
struct curl_slist *headers = NULL;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 300L);
headers = curl_slist_append(headers, "Content-Type: application/json");
char bearer_header[1337];
snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s",
resolve_api_key());
headers = curl_slist_append(headers, bearer_header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
res = curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if (res == CURLE_OK) {
spinner_running = 0;
pthread_join(spinner_tid, NULL);
fprintf(stderr, "\r \r");
fflush(stderr);
return response.data;
}
retry_count++;
spinner_running = 0;
pthread_join(spinner_tid, NULL);
fprintf(stderr, "\r \r");
fprintf(stderr, "Network error: %s (attempt %d/%d)\n",
curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES);
if (retry_count < HTTP_MAX_RETRIES) {
fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000);
usleep(HTTP_RETRY_DELAY_MS * 1000);
clock_gettime(CLOCK_MONOTONIC, &spinner_start_time);
spinner_running = 1;
pthread_create(&spinner_tid, NULL, spinner_thread, NULL);
}
}
fprintf(stderr, "Failed after %d attempts.\n", HTTP_MAX_RETRIES);
free(response.data);
return NULL;
}
char *curl_get(const char *url) {
CURL *curl;
CURLcode res;
struct ResponseBuffer response = {NULL, 0};
int retry_count = 0;
while (retry_count < HTTP_MAX_RETRIES) {
if (response.data) {
free(response.data);
}
response.data = malloc(1);
response.size = 0;
if (!response.data) {
return NULL;
}
response.data[0] = '\0';
curl = curl_easy_init();
if (!curl) {
free(response.data);
return NULL;
}
struct curl_slist *headers = NULL;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L);
headers = curl_slist_append(headers, "Content-Type: application/json");
char bearer_header[1337];
snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s",
resolve_api_key());
headers = curl_slist_append(headers, bearer_header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
res = curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if (res == CURLE_OK) {
return response.data;
}
retry_count++;
fprintf(stderr, "Network error: %s (attempt %d/%d)\n",
curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES);
if (retry_count < HTTP_MAX_RETRIES) {
fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000);
usleep(HTTP_RETRY_DELAY_MS * 1000);
}
}
fprintf(stderr, "Failed after %d attempts.\n", HTTP_MAX_RETRIES);
free(response.data);
return NULL;
}
#endif

147
indexer.h
View File

@ -1,147 +0,0 @@
#include <dirent.h>
#include <json-c/json.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#define MAX_FILES 20000
#define MAX_PATH 4096
static const char *extensions[] = {
".c", ".cpp", ".h", ".py", ".java", ".js",
".mk", ".html", "Makefile", ".css", ".json", ".cs",
".csproj", ".sln", ".toml", ".rs", ".go", ".rb",
".swift", ".php", ".pl", ".sh", ".bash", ".sql",
".xml", ".yaml", ".yml", ".kt", ".dart", ".scala",
".clj", ".asm", ".m", ".r", ".lua", ".groovy",
".v", ".pas", ".d", ".f90", ".f95", ".for",
".s", ".tcl", ".vhdl", ".verilog", ".coffee", ".less",
".scss", ".ps1", ".psm1", ".cmd", ".bat", ".json5",
".cxx", ".cc", ".hpp", ".hxx", ".inc", ".nsi",
".ninja", ".cmake", ".cmake.in", ".mk.in", ".make", ".makefile",
".gyp", ".gypi", ".pro", ".qml", ".ui", ".wxs",
".wxl", ".wxi", ".wxl", ".wxs", ".wxi", ".wxl",
".wxs", ".wxi"};
static const size_t ext_count = sizeof(extensions) / sizeof(extensions[0]);
typedef struct {
char name[MAX_PATH];
char modification_date[20];
char creation_date[20];
char type[10];
size_t size_bytes;
} FileInfo;
static FileInfo file_list[MAX_FILES];
static size_t file_count = 0;
static int is_valid_extension(const char *filename) {
const char *dot = strrchr(filename, '.');
if (!dot)
dot = filename;
for (size_t i = 0; i < ext_count; i++) {
if (strcmp(dot, extensions[i]) == 0)
return 1;
}
return 0;
}
static int is_ignored_directory(const char *dir_name) {
const char *ignored_dirs[] = {"env", ".venv", "node_modules", "venv",
"virtualenv"};
for (size_t i = 0; i < sizeof(ignored_dirs) / sizeof(ignored_dirs[0]); i++) {
if (strcmp(dir_name, ignored_dirs[i]) == 0)
return 1;
}
return 0;
}
static int get_file_info(const char *path) {
if (file_count >= MAX_FILES) {
return -1;
}
struct stat file_stat;
if (stat(path, &file_stat) == 0) {
FileInfo info;
strncpy(info.name, path, MAX_PATH - 1);
info.name[MAX_PATH - 1] = '\0';
strftime(info.modification_date, sizeof(info.modification_date),
"%Y-%m-%d %H:%M:%S", localtime(&file_stat.st_mtime));
strftime(info.creation_date, sizeof(info.creation_date),
"%Y-%m-%d %H:%M:%S", localtime(&file_stat.st_ctime));
strncpy(info.type, S_ISDIR(file_stat.st_mode) ? "directory" : "file",
sizeof(info.type) - 1);
info.type[sizeof(info.type) - 1] = '\0';
info.size_bytes = file_stat.st_size;
file_list[file_count++] = info;
return 0;
}
return -1;
}
char *index_directory(const char *dir_path) {
DIR *dir = opendir(dir_path);
if (!dir) {
perror("Failed to open directory");
return NULL;
}
struct dirent *entry;
json_object *jarray = json_object_new_array();
if (jarray == NULL) {
closedir(dir);
return NULL;
}
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (entry->d_name[0] == '.' || is_ignored_directory(entry->d_name))
continue;
char full_path[MAX_PATH];
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);
if (entry->d_type == DT_DIR) {
char *subdir_json = index_directory(full_path);
if (subdir_json) {
json_object *jsubdir = json_object_new_string(subdir_json);
json_object_array_add(jarray, jsubdir);
free(subdir_json);
}
} else if (is_valid_extension(entry->d_name)) {
if (get_file_info(full_path) == 0 && file_count > 0) {
json_object *jfile = json_object_new_object();
if (jfile == NULL) {
continue;
}
json_object_object_add(
jfile, "file_name",
json_object_new_string(file_list[file_count - 1].name));
json_object_object_add(
jfile, "modification_date",
json_object_new_string(file_list[file_count - 1].modification_date));
json_object_object_add(
jfile, "creation_date",
json_object_new_string(file_list[file_count - 1].creation_date));
json_object_object_add(
jfile, "type",
json_object_new_string(file_list[file_count - 1].type));
json_object_object_add(
jfile, "size_bytes",
json_object_new_int64(file_list[file_count - 1].size_bytes));
json_object_array_add(jarray, jfile);
}
}
}
closedir(dir);
char *result = strdup(json_object_to_json_string(jarray));
json_object_put(jarray);
return result;
}

123
line.h
View File

@ -1,123 +0,0 @@
// Written by retoor@molodetz.nl
// This source code provides command-line input functionalities with
// autocomplete and history features using the readline library. It allows users
// to complete commands and manage input history.
// External includes:
// - <readline/readline.h>
// - <readline/history.h>
// - <glob.h>
// MIT License: Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction.
#include "utils.h"
#include <glob.h>
#include <readline/history.h>
#include <readline/readline.h>
#include <stdbool.h>
#include <string.h>
#define HISTORY_FILE "~/.r_history"
bool line_initialized = false;
char *get_history_file() {
static char result[4096];
result[0] = '\0';
char *expanded = expand_home_directory(HISTORY_FILE);
if (expanded == NULL) {
return result;
}
strncpy(result, expanded, sizeof(result) - 1);
result[sizeof(result) - 1] = '\0';
free(expanded);
return result;
}
char *line_command_generator(const char *text, int state) {
static int list_index, len = 0;
const char *commands[] = {"help", "exit", "list", "review",
"refactor", "obfuscate", "!verbose", "!dump",
"!model", "!debug", NULL};
if (!state) {
list_index = 0;
len = strlen(text);
}
while (commands[list_index]) {
const char *command = commands[list_index++];
if (strncmp(command, text, len) == 0) {
return strdup(command);
}
}
return NULL;
}
char *line_file_generator(const char *text, int state) {
static int list_index;
static glob_t glob_result;
static int glob_valid = 0;
char pattern[1024];
if (!state) {
if (glob_valid) {
globfree(&glob_result);
glob_valid = 0;
}
list_index = 0;
snprintf(pattern, sizeof(pattern), "%s*", text);
if (glob(pattern, GLOB_NOSORT, NULL, &glob_result) == 0) {
glob_valid = 1;
} else {
return NULL;
}
}
if (glob_valid && (size_t)list_index < glob_result.gl_pathc) {
return strdup(glob_result.gl_pathv[list_index++]);
}
if (glob_valid) {
globfree(&glob_result);
glob_valid = 0;
}
return NULL;
}
char **line_command_completion(const char *text, int start, int end) {
rl_attempted_completion_over = 1;
// Check if the input is a file path
if (start > 0 && text[0] != ' ') {
return rl_completion_matches(text, line_file_generator);
}
return rl_completion_matches(text, line_command_generator);
}
void line_init() {
if (!line_initialized) {
rl_attempted_completion_function = line_command_completion;
line_initialized = true;
read_history(get_history_file());
}
}
char *line_read(char *prefix) {
char *data = readline(prefix);
if (!(data && *data)) {
free(data);
return NULL;
}
return data;
}
void line_add_history(char *data) {
add_history(data);
write_history(get_history_file());
}

387
main.c
View File

@ -1,387 +0,0 @@
#include "agent.h"
#include "db_utils.h"
#include "line.h"
#include "markdown.h"
#include "openai.h"
#include "r.h"
#include "tools.h"
#include "utils.h"
#include <locale.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
volatile sig_atomic_t sigint_count = 0;
time_t first_sigint_time = 0;
bool SYNTAX_HIGHLIGHT_ENABLED = true;
bool API_MODE = false;
static void render(const char *content);
static bool openai_include(const char *path);
static char *get_prompt_from_stdin(char *prompt);
static char *get_prompt_from_args(int argc, char **argv);
static bool try_prompt(int argc, char *argv[]);
static void repl(void);
static void init(void);
static void handle_sigint(int sig);
char *get_env_string() {
FILE *fp = popen("env", "r");
if (fp == NULL) {
perror("popen failed");
return NULL;
}
size_t buffer_size = 1024;
size_t total_size = 0;
char *output = malloc(buffer_size);
if (output == NULL) {
perror("malloc failed");
pclose(fp);
return NULL;
}
size_t bytes_read;
while ((bytes_read = fread(output + total_size, 1, buffer_size - total_size,
fp)) > 0) {
total_size += bytes_read;
if (total_size >= buffer_size) {
buffer_size *= 2;
char *temp = realloc(output, buffer_size);
if (temp == NULL) {
perror("realloc failed");
free(output);
pclose(fp);
return NULL;
}
output = temp;
}
}
// Null-terminate the output
output[total_size] = '\0';
pclose(fp);
return output;
}
static char *get_prompt_from_stdin(char *prompt) {
int index = 0;
int c;
while ((c = getchar()) != EOF) {
prompt[index++] = (char)c;
}
prompt[index] = '\0';
return prompt;
}
static char *get_prompt_from_args(int argc, char **argv) {
char *prompt = malloc(10 * 1024 * 1024 + 1);
char *system = malloc(1024 * 1024);
if (!prompt || !system) {
fprintf(stderr, "Error: Memory allocation failed.\n");
free(prompt);
free(system);
return NULL;
}
bool get_from_std_in = false;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--stdin") == 0) {
fprintf(stderr, "Reading from stdin.\n");
get_from_std_in = true;
} else if (strcmp(argv[i], "--verbose") == 0) {
is_verbose = true;
} else if (strcmp(argv[i], "--py") == 0 && i + 1 < argc) {
char *py_file_path = expand_home_directory(argv[++i]);
fprintf(stderr, "Including \"%s\".\n", py_file_path);
openai_include(py_file_path);
free(py_file_path);
} else if (strcmp(argv[i], "--free") == 0) {
auth_free();
} else if (strcmp(argv[i], "--context") == 0 && i + 1 < argc) {
char *context_file_path = argv[++i];
fprintf(stderr, "Including \"%s\".\n", context_file_path);
openai_include(context_file_path);
} else if (strcmp(argv[i], "--api") == 0) {
API_MODE = true;
} else if (strcmp(argv[i], "--nh") == 0) {
SYNTAX_HIGHLIGHT_ENABLED = false;
fprintf(stderr, "Syntax highlighting disabled.\n");
} else if (strncmp(argv[i], "--session=", 10) == 0) {
continue;
} else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) {
i++;
continue;
} else {
strcat(system, argv[i]);
strcat(system, (i < argc - 1) ? " " : ".");
}
}
if (get_from_std_in) {
if (*system)
openai_system(system);
prompt = get_prompt_from_stdin(prompt);
} else {
free(prompt);
prompt = system;
}
if (!*prompt) {
free(prompt);
return NULL;
}
return prompt;
}
static bool try_prompt(int argc, char *argv[]) {
char *prompt = get_prompt_from_args(argc, argv);
if (prompt) {
char *response = agent_chat(prompt);
if (!response) {
printf("Could not get response from server\n");
free(prompt);
return false;
}
render(response);
free(response);
free(prompt);
return true;
}
return false;
}
static bool openai_include(const char *path) {
char *file_content = read_file(path);
if (!file_content)
return false;
openai_system(file_content);
free(file_content);
return true;
}
static void render(const char *content) {
if (SYNTAX_HIGHLIGHT_ENABLED) {
parse_markdown_to_ansi(content);
} else {
printf("%s", content);
}
}
static void repl(void) {
line_init();
char *line = NULL;
while (true) {
line = line_read("> ");
if (!line || !*line)
continue;
if (!strncmp(line, "!dump", 5)) {
printf("%s\n", message_json());
continue;
}
if(!strncmp(line,"!debug",6))
{
printf("%s\n", R_BASE_URL);
continue;
}
if (!strncmp(line, "!clear", 6)) {
messages_remove();
fprintf(stderr, "Session cleared.\n");
continue;
}
if (!strncmp(line, "!session", 8)) {
init_session_id();
printf("Session: %s\n", get_session_id());
printf("DB Key: %s\n", get_session_db_key());
continue;
}
if (!strncmp(line, "!verbose", 8)) {
is_verbose = !is_verbose;
fprintf(stderr, "%s\n",
is_verbose ? "Verbose mode enabled" : "Verbose mode disabled");
continue;
}
if (line && *line != '\n')
line_add_history(line);
if (!strncmp(line, "!tools", 6)) {
printf("Available tools: %s\n",
json_object_to_json_string(tools_descriptions()));
continue;
}
if (!strncmp(line, "!models", 7)) {
printf("Current model: %s\n", openai_fetch_models());
continue;
}
if (!strncmp(line, "!model", 6)) {
if (line[6] == ' ') {
set_prompt_model(line + 7);
}
printf("Current model: %s\n", get_prompt_model());
continue;
}
if (!strncmp(line, "exit", 4))
exit(0);
while (line && *line != '\n') {
char *response = agent_chat(line);
if (response) {
render(response);
printf("\n");
if (strstr(response, "_STEP_")) {
line = "continue";
} else {
line = NULL;
}
free(response);
} else {
fprintf(stderr, "Agent returned no response\n");
line = NULL;
}
}
}
}
static void init(void) {
setbuf(stdout, NULL);
line_init();
auth_init();
db_initialize();
char *schema = db_get_schema();
char payload[1024 * 1024] = {0};
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char datetime[64];
strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S %Z", tm_info);
char cwd[4096];
if (!getcwd(cwd, sizeof(cwd))) {
strcpy(cwd, "unknown");
}
snprintf(
payload, sizeof(payload),
"# AUTONOMOUS AGENT INSTRUCTIONS\n"
"Current date/time: %s\n"
"Working directory: %s\n\n"
"You are an autonomous AI agent. You operate in a loop: reason about the task, "
"select and execute tools when needed, observe results, and continue until the goal is achieved.\n\n"
"## Reasoning Pattern (ReAct)\n"
"For complex tasks, think step-by-step:\n"
"1. Thought: What do I need to accomplish? What information do I have?\n"
"2. Action: Which tool should I use? With what parameters?\n"
"3. Observation: What did the tool return? What does this tell me?\n"
"4. Repeat until the goal is complete.\n\n"
"## Tool Usage\n"
"- Use tools proactively to gather information and take actions\n"
"- If a tool fails, analyze the error and try a different approach\n"
"- You can call multiple tools in sequence to accomplish complex tasks\n\n"
"## CRITICAL OUTPUT RULES\n"
"- You MUST include the actual content/data from tool results in your response\n"
"- When you search the web, QUOTE the relevant information found\n"
"- When you run a command, SHOW the output\n"
"- NEVER say 'I found information' without showing what you found\n"
"- NEVER say 'task complete' or 'report provided' - SHOW THE ACTUAL DATA\n"
"- The user cannot see tool results - only YOUR response. Include everything relevant.\n\n"
"## Local Database\n"
"You have a local SQLite database accessible via db_query, db_get, and db_set tools.\n"
"Use stemmed, lowercase keys to prevent duplicates.\n"
"Schema: %s\n\n"
"## Response Format\n"
"Your response IS the only thing the user sees. Tool outputs are hidden from them.\n"
"You MUST copy/paste relevant data from tool results into your response.\n"
"Bad: 'I searched and found information about X.'\n"
"Good: 'Here is what I found: [actual content from search results]'\n",
datetime, cwd, schema);
free(schema);
fprintf(stderr, "Loading...");
openai_system(payload);
char *env_system_message = get_env_system_message();
if (env_system_message && *env_system_message) {
openai_system(env_system_message);
free(env_system_message);
}
if (!openai_include(".rcontext.txt")) {
openai_include("~/.rcontext.txt");
}
fprintf(stderr, "\r \r");
}
static void handle_sigint(int sig) {
time_t current_time = time(NULL);
printf("\n");
if (sigint_count == 0) {
first_sigint_time = current_time;
sigint_count++;
} else {
if (difftime(current_time, first_sigint_time) <= 1) {
exit(0);
} else {
sigint_count = 1;
first_sigint_time = current_time;
}
}
}
static void parse_session_arg(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
if (strncmp(argv[i], "--session=", 10) == 0) {
const char *name = argv[i] + 10;
if (!set_session_id(name)) {
fprintf(stderr, "Error: Invalid session name '%s'\n", name);
exit(1);
}
return;
}
if ((strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) && i + 1 < argc) {
const char *name = argv[++i];
if (!set_session_id(name)) {
fprintf(stderr, "Error: Invalid session name '%s'\n", name);
exit(1);
}
return;
}
}
char *env_session = get_env_session();
if (env_session) {
if (!set_session_id(env_session)) {
fprintf(stderr, "Warning: Invalid R_SESSION '%s', using default\n", env_session);
}
}
}
int main(int argc, char *argv[]) {
signal(SIGINT, handle_sigint);
parse_session_arg(argc, argv);
init();
char *env_string = get_env_string();
if (env_string && *env_string) {
openai_system(env_string);
free(env_string);
}
messages_load_conversation();
if (try_prompt(argc, argv))
return 0;
repl();
return 0;
}

View File

@ -1,351 +0,0 @@
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
// --- ANSI Escape Codes ---
#define RESET "\033[0m"
#define BOLD "\033[1m"
#define ITALIC "\033[3m"
#define STRIKETHROUGH "\033[9m"
#define FG_YELLOW "\033[33m"
#define FG_BLUE "\033[34m"
#define FG_CYAN "\033[36m"
#define FG_MAGENTA "\033[35m"
#define BG_YELLOW_FG_BLACK "\033[43;30m"
/**
* @brief Checks if a given word is a programming language keyword.
* * @param word The word to check.
* @return int 1 if it's a keyword, 0 otherwise.
*/
int is_keyword(const char *word) {
// A comprehensive list of keywords from various popular languages.
const char *keywords[] = {
// C keywords
"int", "float", "double", "char", "void", "if", "else", "while", "for",
"return", "struct", "printf",
// Rust keywords
"let", "fn", "impl", "match", "enum", "trait", "use", "mod", "pub",
"const", "static",
// Python keywords
"def", "class", "import", "from", "as", "with", "try", "except",
"finally", "lambda", "async", "await",
// Java keywords
"public", "private", "protected", "class", "interface", "extends",
"implements", "new", "static", "final", "synchronized",
// JavaScript keywords
"var", "let", "const", "function", "async", "await", "if", "else",
"switch", "case", "break", "continue", "return",
// C++ keywords
"namespace", "template", "typename", "class", "public", "private",
"protected", "virtual", "override", "friend", "new",
// Go keywords
"package", "import", "func", "var", "const", "type", "interface",
"struct", "go", "defer", "select",
// Bash keywords
"if", "then", "else", "elif", "fi", "case", "esac", "for", "while",
"until", "do", "done", "function",
// C# keywords
"namespace", "using", "class", "interface", "public", "private",
"protected", "static", "void", "new", "override"};
for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
if (strcmp(word, keywords[i]) == 0) {
return 1;
}
}
return 0;
}
/**
* @brief Applies basic syntax highlighting to a string of code.
* * @param code The code string to highlight.
*/
void highlight_code(const char *code) {
const char *ptr = code;
char buffer[4096];
size_t index = 0;
while (*ptr) {
// Highlight keywords
if (isalpha((unsigned char)*ptr) || *ptr == '_') {
while (isalnum((unsigned char)*ptr) || *ptr == '_') {
buffer[index++] = *ptr++;
}
buffer[index] = '\0';
if (is_keyword(buffer)) {
printf(FG_BLUE "%s" RESET FG_YELLOW, buffer);
} else {
printf("%s", buffer);
}
index = 0;
// Highlight numbers
} else if (isdigit((unsigned char)*ptr)) {
while (isdigit((unsigned char)*ptr)) {
buffer[index++] = *ptr++;
}
buffer[index] = '\0';
printf(FG_CYAN "%s" RESET FG_YELLOW, buffer);
index = 0;
// Print other characters as-is
} else {
putchar(*ptr);
ptr++;
}
}
}
/**
* @brief Parses a Markdown string and prints it to the console with ANSI color
* codes.
*
* This version supports a wide range of Markdown features, including:
* - Headers (H1-H6)
* - Bold (**, __) and Italic (*, _) text
* - Strikethrough (~~) and Highlight (==)
* - Blockquotes (>), Nested Ordered (1.) and Unordered lists (*, -, +)
* - Inline code (`) and full code blocks (```) with syntax highlighting
* - Links ([text](url)) and Horizontal rules (---, ***)
* * @param markdown The raw Markdown string to parse.
*/
void parse_markdown_to_ansi(const char *markdown) {
const char *ptr = markdown;
bool is_start_of_line = true;
while (*ptr) {
// --- Code Blocks (```) ---
if (is_start_of_line && strncmp(ptr, "```", 3) == 0) {
ptr += 3;
while (*ptr && *ptr != '\n')
ptr++;
if (*ptr)
ptr++;
const char *code_start = ptr;
const char *code_end = strstr(code_start, "```");
if (code_end) {
char block_buffer[code_end - code_start + 1];
strncpy(block_buffer, code_start, code_end - code_start);
block_buffer[code_end - code_start] = '\0';
printf(FG_YELLOW);
highlight_code(block_buffer);
printf(RESET);
ptr = code_end + 3;
if (*ptr == '\n')
ptr++;
is_start_of_line = true;
continue;
} else {
printf(FG_YELLOW);
highlight_code(code_start);
printf(RESET);
break;
}
}
// --- Block-level Elements (checked at the start of a line) ---
if (is_start_of_line) {
const char *line_start_ptr = ptr;
int indent_level = 0;
while (*ptr == ' ') {
indent_level++;
ptr++;
}
bool block_processed = true;
if (strncmp(ptr, "###### ", 7) == 0) {
printf(BOLD FG_YELLOW);
ptr += 7;
} else if (strncmp(ptr, "##### ", 6) == 0) {
printf(BOLD FG_YELLOW);
ptr += 6;
} else if (strncmp(ptr, "#### ", 5) == 0) {
printf(BOLD FG_YELLOW);
ptr += 5;
} else if (strncmp(ptr, "### ", 4) == 0) {
printf(BOLD FG_YELLOW);
ptr += 4;
} else if (strncmp(ptr, "## ", 3) == 0) {
printf(BOLD FG_YELLOW);
ptr += 3;
} else if (strncmp(ptr, "# ", 2) == 0) {
printf(BOLD FG_YELLOW);
ptr += 2;
} else if ((strncmp(ptr, "---", 3) == 0 || strncmp(ptr, "***", 3) == 0) &&
(*(ptr + 3) == '\n' || *(ptr + 3) == '\0')) {
printf(FG_CYAN "───────────────────────────────────────────────────────"
"──────────" RESET "\n");
ptr += 3;
if (*ptr == '\n')
ptr++;
is_start_of_line = true;
continue;
} else if (strncmp(ptr, "> ", 2) == 0) {
for (int i = 0; i < indent_level; i++)
putchar(' ');
printf(ITALIC FG_CYAN "" RESET);
ptr += 2;
is_start_of_line = false;
continue;
} else if ((*ptr == '*' || *ptr == '-' || *ptr == '+') &&
*(ptr + 1) == ' ') {
for (int i = 0; i < indent_level; i++)
putchar(' ');
printf(FG_MAGENTA "" RESET);
ptr += 2;
is_start_of_line = false;
continue;
} else {
const char *temp_ptr = ptr;
while (isdigit((unsigned char)*temp_ptr))
temp_ptr++;
if (temp_ptr > ptr && *temp_ptr == '.' && *(temp_ptr + 1) == ' ') {
for (int i = 0; i < indent_level; i++)
putchar(' ');
printf(FG_MAGENTA);
fwrite(ptr, 1, (temp_ptr - ptr) + 1, stdout);
printf(" " RESET);
ptr = temp_ptr + 2;
is_start_of_line = false;
continue;
} else {
block_processed = false;
ptr = line_start_ptr;
}
}
if (block_processed) {
while (*ptr && *ptr != '\n')
putchar(*ptr++);
printf(RESET "\n");
if (*ptr == '\n')
ptr++;
is_start_of_line = true;
continue;
}
}
// --- Inline Elements (order is important) ---
if (strncmp(ptr, "***", 3) == 0 || strncmp(ptr, "___", 3) == 0) {
const char *marker = strncmp(ptr, "***", 3) == 0 ? "***" : "___";
printf(BOLD ITALIC);
ptr += 3;
const char *end = strstr(ptr, marker);
if (end) {
fwrite(ptr, 1, end - ptr, stdout);
ptr = end + 3;
} else {
fputs(ptr, stdout);
ptr += strlen(ptr);
}
printf(RESET);
continue;
}
if (strncmp(ptr, "**", 2) == 0 || strncmp(ptr, "__", 2) == 0) {
const char *marker = strncmp(ptr, "**", 2) == 0 ? "**" : "__";
printf(BOLD);
ptr += 2;
const char *end = strstr(ptr, marker);
if (end) {
fwrite(ptr, 1, end - ptr, stdout);
ptr = end + 2;
} else {
fputs(ptr, stdout);
ptr += strlen(ptr);
}
printf(RESET);
continue;
}
if ((*ptr == '*' || *ptr == '_') && !isspace(*(ptr - 1)) &&
!isspace(*(ptr + 1))) {
char marker = *ptr;
printf(ITALIC);
ptr++;
const char *start = ptr;
while (*ptr && *ptr != marker)
ptr++;
if (*ptr == marker) {
fwrite(start, 1, ptr - start, stdout);
ptr++;
} else {
putchar(marker);
ptr = start;
}
printf(RESET);
continue;
}
if (strncmp(ptr, "~~", 2) == 0) {
printf(STRIKETHROUGH);
ptr += 2;
const char *end = strstr(ptr, "~~");
if (end) {
fwrite(ptr, 1, end - ptr, stdout);
ptr = end + 2;
} else {
fputs(ptr, stdout);
ptr += strlen(ptr);
}
printf(RESET);
continue;
}
if (strncmp(ptr, "==", 2) == 0) {
printf(BG_YELLOW_FG_BLACK);
ptr += 2;
const char *end = strstr(ptr, "==");
if (end) {
fwrite(ptr, 1, end - ptr, stdout);
ptr = end + 2;
} else {
fputs(ptr, stdout);
ptr += strlen(ptr);
}
printf(RESET);
continue;
}
if (*ptr == '`' && *(ptr + 1) != '`') {
printf(FG_YELLOW);
ptr++;
const char *start = ptr;
while (*ptr && *ptr != '`')
ptr++;
fwrite(start, 1, ptr - start, stdout);
if (*ptr == '`')
ptr++;
printf(RESET);
continue;
}
if (*ptr == '[') {
const char *text_start = ptr + 1;
const char *text_end = strchr(text_start, ']');
if (text_end && *(text_end + 1) == '(') {
const char *url_start = text_end + 2;
const char *url_end = strchr(url_start, ')');
if (url_end) {
printf(FG_BLUE);
fwrite(text_start, 1, text_end - text_start, stdout);
printf(RESET " (");
printf(ITALIC FG_CYAN);
fwrite(url_start, 1, url_end - url_start, stdout);
printf(RESET ")");
ptr = url_end + 1;
continue;
}
}
}
// --- Default Character ---
if (*ptr == '\n') {
is_start_of_line = true;
} else if (!isspace((unsigned char)*ptr)) {
is_start_of_line = false;
}
putchar(*ptr);
ptr++;
}
}

View File

@ -1,260 +0,0 @@
// Written by retoor@molodetz.nl
// This code manages a collection of messages using JSON objects. It provides
// functions to retrieve all messages as a JSON array, add a new message with a
// specified role and content, and free the allocated resources.
// Uses the external library <json-c/json.h> for JSON manipulation
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS",
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
// THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef R_MESSAGES_H
#define R_MESSAGES_H
#include "db_utils.h"
#include "json-c/json.h"
#include "tools.h"
#include "utils.h"
#include <string.h>
#include <unistd.h>
struct json_object *message_array = NULL;
static char session_id[256] = {0};
static char override_session_id[256] = {0};
struct json_object *message_list();
bool set_session_id(const char *name) {
if (name == NULL || *name == '\0') {
return false;
}
size_t len = strlen(name);
if (len > 200) {
return false;
}
for (const char *p = name; *p; p++) {
if (*p == '/' || *p == '\\') {
return false;
}
}
strncpy(override_session_id, name, sizeof(override_session_id) - 1);
override_session_id[sizeof(override_session_id) - 1] = '\0';
return true;
}
const char *get_session_id() {
if (session_id[0] == '\0') {
if (override_session_id[0] != '\0') {
strncpy(session_id, override_session_id, sizeof(session_id) - 1);
session_id[sizeof(session_id) - 1] = '\0';
}
}
return session_id;
}
void init_session_id() {
if (session_id[0] != '\0') {
return;
}
if (override_session_id[0] != '\0') {
strncpy(session_id, override_session_id, sizeof(session_id) - 1);
session_id[sizeof(session_id) - 1] = '\0';
return;
}
char *tty = ttyname(STDIN_FILENO);
if (tty) {
unsigned long h = 5381;
for (char *p = tty; *p; p++) {
h = ((h << 5) + h) + (unsigned char)*p;
}
snprintf(session_id, sizeof(session_id), "%lu", h);
} else {
snprintf(session_id, sizeof(session_id), "%d", getppid());
}
}
char *get_session_db_key() {
static char key[512];
init_session_id();
snprintf(key, sizeof(key), "session:%s", session_id);
return key;
}
bool messages_save() {
char *key = get_session_db_key();
const char *json_str = json_object_to_json_string_ext(message_list(), JSON_C_TO_STRING_PLAIN);
if (!json_str) {
return false;
}
json_object *result = db_set(key, json_str);
if (result) {
json_object_put(result);
return true;
}
return false;
}
bool messages_load_conversation() {
char *key = get_session_db_key();
json_object *result = db_get(key);
if (!result) {
return false;
}
json_object *value_obj;
if (!json_object_object_get_ex(result, "value", &value_obj)) {
json_object_put(result);
return false;
}
const char *json_str = json_object_get_string(value_obj);
if (!json_str) {
json_object_put(result);
return false;
}
struct json_object *loaded = json_tokener_parse(json_str);
json_object_put(result);
if (!loaded || !json_object_is_type(loaded, json_type_array)) {
if (loaded) {
json_object_put(loaded);
}
return false;
}
int len = json_object_array_length(loaded);
int added = 0;
for (int i = 0; i < len; i++) {
struct json_object *msg = json_object_array_get_idx(loaded, i);
struct json_object *role_obj;
if (json_object_object_get_ex(msg, "role", &role_obj)) {
const char *role = json_object_get_string(role_obj);
if (role && strcmp(role, "system") != 0) {
json_object_array_add(message_list(), json_object_get(msg));
added++;
}
} else {
json_object_array_add(message_list(), json_object_get(msg));
added++;
}
}
json_object_put(loaded);
return added > 0;
}
struct json_object *message_list() {
if (!message_array) {
message_array = json_object_new_array();
}
return message_array;
}
bool messages_remove_last() {
struct json_object *messages = message_list();
int size = json_object_array_length(messages);
if (size) {
json_object_array_del_idx(messages, size - 1, 1);
messages_save();
return true;
}
return false;
}
void messages_remove() {
if (message_array) {
json_object_put(message_array);
message_array = NULL;
}
message_list();
messages_save();
}
struct json_object *message_add_tool_call(struct json_object *message) {
struct json_object *messages = message_list();
json_object_array_add(messages, message);
messages_save();
return message;
}
struct json_object *message_add_tool_result(const char *tool_call_id,
char *tool_result) {
struct json_object *messages = message_list();
struct json_object *message = json_object_new_object();
if (message == NULL) {
return NULL;
}
json_object_object_add(message, "tool_call_id",
json_object_new_string(tool_call_id));
size_t result_len = strlen(tool_result);
if (result_len > 104000) {
result_len = 104000;
}
json_object_object_add(message, "tool_result",
json_object_new_string_len(tool_result, (int)result_len));
json_object_array_add(messages, message);
messages_save();
return message;
}
void message_add_object(json_object *message) {
struct json_object *messages = message_list();
json_object_array_add(messages, message);
messages_save();
}
struct json_object *message_add(const char *role, const char *content);
struct json_object *message_add(const char *role, const char *content) {
struct json_object *messages = message_list();
struct json_object *message = json_object_new_object();
if (message == NULL) {
return NULL;
}
json_object_object_add(message, "role", json_object_new_string(role));
if (content) {
size_t content_len = strlen(content);
if (content_len > 1048570) {
content_len = 1048570;
}
json_object_object_add(message, "content",
json_object_new_string_len(content, (int)content_len));
}
if (!strcmp(role, "user")) {
json_object_object_add(message, "tools", tools_descriptions());
json_object_object_add(message, "parallel_tool_calls",
json_object_new_boolean(true));
}
json_object_array_add(messages, message);
if (strcmp(role, "system") != 0) {
messages_save();
}
return message;
}
char *message_json() {
return (char *)json_object_to_json_string_ext(message_list(),
JSON_C_TO_STRING_PRETTY);
}
void message_free() {
if (message_array) {
json_object_put(message_array);
message_array = NULL;
}
}
#endif

144
openai.h
View File

@ -1,144 +0,0 @@
// Written by retoor@molodetz.nl
// This code interacts with OpenAI's API to perform various tasks such as
// fetching models, sending chat messages, and processing responses.
// Uncommon imports include "http.h", "chat.h", and "http_curl.h". These may be
// internal or external libraries providing HTTP and JSON communication
// capabilities required to interact with APIs.
// MIT License
//
// Copyright (c) 2023
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef R_OPENAI_H
#define R_OPENAI_H
#include "chat.h"
#include "http_curl.h"
#include "r.h"
#include <stdbool.h>
#include <string.h>
char *openai_fetch_models() { return curl_get(get_models_api_url()); }
bool openai_system(char *message_content) {
chat_json("system", message_content);
return true;
}
struct json_object *openai_process_chat_message(const char *api_url,
const char *json_data) {
char *response = curl_post(api_url, json_data);
if (!response) {
fprintf(stderr, "Failed to get response.\n");
return NULL;
}
struct json_object *parsed_json = json_tokener_parse(response);
if (!parsed_json) {
fprintf(stderr, "Failed to parse JSON.\nContent: \"%s\"\n", response);
return NULL;
}
struct json_object *error_object;
if (json_object_object_get_ex(parsed_json, "error", &error_object) &&
message_array) {
const char *all_messages = json_object_to_json_string(message_array);
fprintf(stderr, "Messages: ");
if (all_messages) {
fwrite(all_messages, strlen(all_messages), 1, stderr);
}
fprintf(stderr, "\n");
fprintf(stderr, "%s\n", json_object_to_json_string(parsed_json));
json_object_put(parsed_json);
messages_remove_last();
messages_remove_last();
return NULL;
}
struct json_object *choices_array;
if (!json_object_object_get_ex(parsed_json, "choices", &choices_array)) {
fprintf(stderr, "Failed to get 'choices' array.\n%s\n", response);
fprintf(stderr, "%s\n", json_object_to_json_string(parsed_json));
json_object_put(parsed_json);
return NULL;
}
struct json_object *first_choice =
json_object_array_get_idx(choices_array, 0);
if (!first_choice) {
fprintf(stderr, "Failed to get the first element of 'choices'.\n");
json_object_put(parsed_json);
return NULL;
}
struct json_object *message_object;
if (!json_object_object_get_ex(first_choice, "message", &message_object)) {
fprintf(stderr, "Failed to get 'message' object.\n");
json_object_put(parsed_json);
return NULL;
}
return message_object;
}
char *openai_chat(const char *user_role, const char *message_content) {
if (message_content == NULL || *message_content == '\0' ||
*message_content == '\n') {
return NULL;
}
char *json_data = chat_json(user_role, message_content);
struct json_object *message_object =
openai_process_chat_message(get_completions_api_url(), json_data);
if (message_object == NULL) {
return NULL;
}
message_add_object(message_object);
struct json_object *tool_calls;
json_object_object_get_ex(message_object, "tool_calls", &tool_calls);
if (tool_calls) {
// message_add_tool_call(message_object);
struct json_object *tool_call_results = tools_execute(tool_calls);
int results_count = json_object_array_length(tool_call_results);
for (int i = 0; i < results_count; i++) {
struct json_object *tool_call_result =
json_object_array_get_idx(tool_call_results, i);
message_add_tool_call(tool_call_result);
}
char *tool_calls_result_str = chat_json(NULL, NULL);
message_object = openai_process_chat_message(get_completions_api_url(),
tool_calls_result_str);
if (message_object == NULL) {
return NULL;
}
message_add_object(message_object);
// message_add_tool_call(message_object);
}
const char *content_str =
json_object_get_string(json_object_object_get(message_object, "content"));
return strdup(content_str);
}
#endif

View File

@ -1,62 +0,0 @@
// Written by retoor@molodetz.nl
// This source code initializes a Python interpreter within a plugin, executes a
// provided Python script with some basic imports, and finalizes the Python
// environment when done.
// This code does not use any non-standard imports or includes aside from
// Python.h and structmember.h which are part of Python's C API.
// MIT License
#include <Python.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <structmember.h>
bool plugin_initialized = false;
bool plugin_construct() {
if (plugin_initialized)
return true;
Py_Initialize();
if (!Py_IsInitialized()) {
fprintf(stderr, "Failed to initialize the Python interpreter\n");
return false;
}
plugin_initialized = true;
return true;
}
void plugin_run(char *src) {
plugin_construct();
/*const char *basics =
"import sys\n"
"import os\n"
"from os import *\n"
"import math\n"
"import pathlib\n"
"from pathlib import Path\n"
"import re\n"
"import subprocess\n"
"from subprocess import *\n"
"import time\n"
"from datetime import datetime\n"
"%s";
*/
const char *basics = "\n\n";
size_t length = strlen(basics) + strlen(src);
char *script = malloc(length + 1);
sprintf(script, basics, src);
script[length] = '\0';
PyRun_SimpleString(script);
free(script);
}
void plugin_destruct() {
if (plugin_initialized)
Py_Finalize();
}

126
r.h
View File

@ -1,126 +0,0 @@
#ifndef R_H
#define R_H
#include "auth.h"
#include "utils.h"
#include <stdbool.h>
#include <string.h>
bool is_verbose = false;
char *models_api_url = "https://api.openai.com/v1/models";
char *completions_api_url = "https://api.openai.com/v1/chat/completions";
char *advanced_model = "gpt-4o-mini";
char *fast_model = "gpt-3.5-turbo";
// char *models_api_url = "https://api.openai.com/v1/models";
// char *completions_api_url = "https://api.anthropic.com/v1/chat/completions";
// char *advanced_model = "claude-3-5-haiku-20241022";
// char *advanced_model = "meta-llama/Meta-Llama-3.1-8B-Instruct";
// char *advanced_model = "google/gemini-1.5-flash";
// char *fast_model = "claude-3-5-haiku-20241022";
// #endif
// #ifdef OLLAMA
// char *models_api_url = "https://ollama.molodetz.nl/v1/models";
// char *completions_api_url = "https://ollama.molodetz.nl/v1/chat/completions";
// char *advanced_model = "qwen2.5:3b";
// char *advanced_model = "qwen2.5-coder:0.5b";
// char *fast_model = "qwen2.5:0.5b";
// #endif
char *_model = NULL;
#define DB_FILE "~/.r.db"
#define PROMPT_TEMPERATURE 0.1
bool use_tools() {
if (getenv("R_USE_TOOLS") != NULL) {
const char *value = getenv("R_USE_TOOLS");
if (!strcmp(value, "true")) {
return true;
}
if (!strcmp(value, "false")) {
return false;
}
if (!strcmp(value, "1")) {
return true;
}
if (!strcmp(value, "0")) {
return false;
}
}
return true;
}
char *get_env_system_message() {
if (getenv("R_SYSTEM_MESSAGE") != NULL) {
return strdup(getenv("R_SYSTEM_MESSAGE"));
}
return NULL;
}
char *get_env_session() {
char *session = getenv("R_SESSION");
return (session && *session) ? session : NULL;
}
bool get_use_strict() {
if (getenv("R_USE_STRICT") != NULL) {
const char *value = getenv("R_USE_STRICT");
if (!strcmp(value, "true")) {
return true;
}
if (!strcmp(value, "false")) {
return false;
}
if (!strcmp(value, "1")) {
return true;
}
if (!strcmp(value, "0")) {
return false;
}
}
return true;
}
char *get_completions_api_url() {
if (getenv("R_BASE_URL") != NULL) {
char * path = joinpath(getenv("R_BASE_URL"), "v1/chat/completions");
printf("%s\n",path);
return path;
}
printf("%s\n",completions_api_url);
return completions_api_url;
}
char *get_models_api_url() {
if (getenv("R_BASE_URL") != NULL) {
return joinpath(getenv("R_BASE_URL"), "v1/models");
}
return models_api_url;
}
void set_prompt_model(const char *model) {
if (_model != NULL) {
free(_model);
}
_model = strdup(model);
}
const char *get_prompt_model() {
if (_model == NULL && getenv("R_MODEL") != NULL) {
_model = strdup(getenv("R_MODEL"));
}
if (_model) {
return _model;
}
if (auth_type != AUTH_TYPE_API_KEY) {
if (_model == NULL) {
_model = strdup(fast_model);
}
} else if (_model == NULL) {
_model = strdup(advanced_model);
}
return _model;
}
#endif

112
rpylib.c
View File

@ -1,112 +0,0 @@
/* Written by retoor@molodetz.nl */
/*
This C extension for Python provides a simple API for communication with an
OpenAI service. It includes functions to return a "Hello World" string, conduct
a chat session through OpenAI, and reset the message history.
*/
/*
Summary of used imports:
- <Python.h>: Includes necessary Python headers to create a C extension.
- "openai.h": Assumes an external library for OpenAI interaction.
*/
/*
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#define PY_SSIZE_T_CLEAN
#include "auth.h"
#include "openai.h"
#include <Python.h>
static PyObject *rpylib_reset(PyObject *self, PyObject *args) {
return PyUnicode_FromString("True");
}
static PyObject *rpylib_chat(PyObject *self, PyObject *args) {
const char *role, *message;
if (!PyArg_ParseTuple(args, "ss", &role, &message)) {
return NULL;
}
char *result = openai_chat(role, message);
if (!result) {
PyErr_SetString(PyExc_RuntimeError, "Failed to get response from OpenAI.");
return NULL;
}
PyObject *py_result = PyUnicode_FromString(result);
free(result);
return py_result;
}
static PyObject *rpylib_prompt(PyObject *self, PyObject *args) {
const char *role = "user";
const char *message;
if (!PyArg_ParseTuple(args, "s", &message)) {
return NULL;
}
char *result = openai_chat(role, message);
if (!result) {
PyErr_SetString(PyExc_RuntimeError, "Failed to get response from OpenAI.");
return NULL;
}
PyObject *py_result = PyUnicode_FromString(result);
free(result);
return py_result;
}
static PyObject *rpylib_system(PyObject *self, PyObject *args) {
const char *role = "system";
const char *message;
if (!PyArg_ParseTuple(args, "s", &message)) {
return NULL;
}
char *result = openai_chat(role, message);
if (!result) {
PyErr_SetString(PyExc_RuntimeError, "Failed to get response from OpenAI.");
return NULL;
}
PyObject *py_result = PyUnicode_FromString(result);
free(result);
return py_result;
}
static PyMethodDef MyModuleMethods[] = {
{"chat", rpylib_chat, METH_VARARGS, "Chat with OpenAI."},
{"reset", rpylib_reset, METH_NOARGS, "Reset message history."},
{NULL, NULL, 0, NULL}};
static struct PyModuleDef rpylib = {
PyModuleDef_HEAD_INIT, "rpylib",
"R - the power of R in Python, made by retoor.", -1, MyModuleMethods};
PyMODINIT_FUNC PyInit_rpylib(void) {
auth_init();
return PyModule_Create(&rpylib);
}

View File

@ -112,6 +112,10 @@ void agent_set_verbose(agent_handle agent, bool verbose) {
if (agent) agent->verbose = verbose;
}
void agent_set_tool_registry(agent_handle agent, tool_registry_t *registry) {
if (agent && registry) agent->tools = registry;
}
agent_state_t agent_get_state(agent_handle agent) {
return agent ? agent->state : AGENT_STATE_ERROR;
}

46
src/context_summarizer.c Normal file
View File

@ -0,0 +1,46 @@
#include "context_summarizer.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
// Placeholder for LLM API call
// In a real implementation, this function would call the LLM API to get the summary.
static char* call_llm_to_summarize(const char* messages_concatenated) {
// For demonstration, just return a dummy summary.
const char* dummy_summary = "This is a summary of the oldest 20 messages.";
char* result = malloc(strlen(dummy_summary) + 1);
if (result) {
strcpy(result, dummy_summary);
}
return result;
}
char* summarize_oldest_messages(const char** messages, size_t message_count) {
// Concatenate the oldest 20 messages
size_t total_length = 0;
size_t start_index = 0;
if (message_count > 20) {
start_index = message_count - 20;
}
for (size_t i = start_index; i < message_count; ++i) {
total_length += strlen(messages[i]) + 1; // +1 for separator
}
char* concatenated = malloc(total_length + 1);
if (!concatenated) {
return NULL;
}
concatenated[0] = '\0';
for (size_t i = start_index; i < message_count; ++i) {
strcat(concatenated, messages[i]);
if (i < message_count - 1) {
strcat(concatenated, " "); // separator
}
}
// Call the LLM API to get the summary
char* summary = call_llm_to_summarize(concatenated);
free(concatenated);
return summary;
}

12
src/context_summarizer.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef CONTEXT_SUMMARIZER_H
#define CONTEXT_SUMMARIZER_H
#include <stddef.h>
// Summarizes the oldest 20 messages into a single summary.
// messages: array of message strings
// message_count: number of messages in the array
// Returns a newly allocated string containing the summary.
char* summarize_oldest_messages(const char** messages, size_t message_count);
#endif // CONTEXT_SUMMARIZER_H

146
src/core/buffer.c Normal file
View File

@ -0,0 +1,146 @@
// retoor <retoor@molodetz.nl>
#include "buffer.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct buffer_t {
char *data;
size_t size;
size_t capacity;
};
static const size_t DEFAULT_CAPACITY = 256;
buffer_handle buffer_create(size_t initial_capacity) {
if (initial_capacity == 0) initial_capacity = DEFAULT_CAPACITY;
struct buffer_t *b = malloc(sizeof(struct buffer_t));
if (!b) return NULL;
b->data = malloc(initial_capacity);
if (!b->data) {
free(b);
return NULL;
}
b->size = 0;
b->capacity = initial_capacity;
return b;
}
buffer_handle buffer_create_empty(void) {
return buffer_create(DEFAULT_CAPACITY);
}
void buffer_write(buffer_handle b, const void *data, size_t size) {
if (!b || !data || size == 0) return;
if (b->size + size > b->capacity) {
size_t new_capacity = b->capacity;
while (new_capacity < b->size + size) new_capacity *= 2;
char *new_data = realloc(b->data, new_capacity);
if (!new_data) return;
b->data = new_data;
b->capacity = new_capacity;
}
memcpy(b->data + b->size, data, size);
b->size += size;
}
void buffer_write_string(buffer_handle b, const char *str) {
if (!b || !str) return;
buffer_write(b, str, strlen(str));
}
void buffer_write_stringf(buffer_handle b, const char *fmt, ...) {
if (!b || !fmt) return;
va_list args, args_copy;
va_start(args, fmt);
va_copy(args_copy, args);
int len = vsnprintf(NULL, 0, fmt, args_copy);
va_end(args_copy);
if (len < 0) {
va_end(args);
return;
}
if ((size_t)len > 0) buffer_write(b, NULL, (size_t)len);
if (b->size + (size_t)len > b->capacity) {
size_t new_capacity = b->capacity;
while (new_capacity < b->size + (size_t)len) new_capacity *= 2;
char *new_data = realloc(b->data, new_capacity);
if (!new_data) {
va_end(args);
return;
}
b->data = new_data;
b->capacity = new_capacity;
}
vsnprintf(b->data + b->size, b->capacity - b->size, fmt, args);
b->size += (size_t)len;
va_end(args);
}
void buffer_write_buffer(buffer_handle dest, buffer_handle src) {
if (!dest || !src) return;
buffer_write(dest, src->data, src->size);
}
size_t buffer_size(buffer_handle b) {
return b ? b->size : 0;
}
size_t buffer_capacity(buffer_handle b) {
return b ? b->capacity : 0;
}
const void *buffer_data(buffer_handle b) {
return b ? b->data : NULL;
}
char *buffer_to_string(buffer_handle b) {
if (!b) return NULL;
char *str = malloc(b->size + 1);
if (!str) return NULL;
if (b->size > 0) memcpy(str, b->data, b->size);
str[b->size] = '\0';
return str;
}
void *buffer_release_data(buffer_handle b) {
if (!b) return NULL;
void *data = b->data;
b->data = NULL;
b->size = 0;
b->capacity = 0;
return data;
}
void buffer_clear(buffer_handle b) {
if (!b) return;
b->size = 0;
}
void buffer_destroy(buffer_handle b) {
if (!b) return;
if (b->data) free(b->data);
free(b);
}

28
src/core/buffer.h Normal file
View File

@ -0,0 +1,28 @@
// retoor <retoor@molodetz.nl>
#ifndef R_BUFFER_H
#define R_BUFFER_H
#include "r_error.h"
#include <stddef.h>
typedef struct buffer_t *buffer_handle;
buffer_handle buffer_create(size_t initial_capacity);
buffer_handle buffer_create_empty(void);
void buffer_write(buffer_handle b, const void *data, size_t size);
void buffer_write_string(buffer_handle b, const char *str);
void buffer_write_stringf(buffer_handle b, const char *fmt, ...);
void buffer_write_buffer(buffer_handle dest, buffer_handle src);
size_t buffer_size(buffer_handle b);
size_t buffer_capacity(buffer_handle b);
const void *buffer_data(buffer_handle b);
char *buffer_to_string(buffer_handle b);
void *buffer_release_data(buffer_handle b);
void buffer_clear(buffer_handle b);
void buffer_destroy(buffer_handle b);
#endif

107
src/core/memory.c Normal file
View File

@ -0,0 +1,107 @@
// retoor <retoor@molodetz.nl>
#include "memory.h"
#include <stdlib.h>
#include <string.h>
arena_t *arena_create(size_t chunk_size) {
if (chunk_size == 0) chunk_size = 4096;
arena_t *arena = calloc(1, sizeof(arena_t));
if (!arena) return NULL;
arena->block_size = chunk_size;
arena->current_block = malloc(chunk_size);
if (!arena->current_block) {
free(arena);
return NULL;
}
arena_block_t *block_info = calloc(1, sizeof(arena_block_t));
if (!block_info) {
free(arena->current_block);
free(arena);
return NULL;
}
block_info->data = arena->current_block;
block_info->size = chunk_size;
arena->blocks = block_info;
arena->block_count = 1;
return arena;
}
void *arena_alloc(arena_t *arena, size_t size) {
if (!arena || size == 0) return NULL;
size_t aligned_size = (size + 7) & ~7;
if (arena->block_used + aligned_size > arena->block_size) {
arena_block_t *new_block = calloc(1, sizeof(arena_block_t));
if (!new_block) return NULL;
char *new_data = malloc(arena->block_size);
if (!new_data) {
free(new_block);
return NULL;
}
new_block->data = new_data;
new_block->size = arena->block_size;
new_block->next = arena->blocks;
arena->blocks = new_block;
arena->current_block = new_data;
arena->block_used = 0;
arena->block_count++;
}
void *ptr = arena->current_block + arena->block_used;
arena->block_used += aligned_size;
return ptr;
}
char *arena_strdup(arena_t *arena, const char *str) {
if (!arena || !str) return NULL;
size_t len = strlen(str);
char *copy = arena_alloc(arena, len + 1);
if (!copy) return NULL;
memcpy(copy, str, len + 1);
return copy;
}
void arena_reset(arena_t *arena) {
if (!arena) return;
arena->block_used = 0;
arena_block_t *block = arena->blocks;
while (block) {
arena_block_t *next = block->next;
if (block != arena->blocks) {
free(block->data);
free(block);
}
block = next;
}
arena->blocks->next = NULL;
arena->current_block = arena->blocks->data;
arena->block_count = 1;
}
void arena_destroy(arena_t *arena) {
if (!arena) return;
arena_block_t *block = arena->blocks;
while (block) {
arena_block_t *next = block->next;
free(block->data);
free(block);
block = next;
}
free(arena);
}

31
src/core/memory.h Normal file
View File

@ -0,0 +1,31 @@
// retoor <retoor@molodetz.nl>
#ifndef R_MEMORY_H
#define R_MEMORY_H
#include <stddef.h>
typedef struct arena_t {
char *current_block;
size_t block_size;
size_t block_used;
size_t block_count;
struct arena_block *blocks;
} arena_t;
typedef struct arena_block {
char *data;
size_t size;
struct arena_block *next;
} arena_block_t;
arena_t *arena_create(size_t chunk_size);
void *arena_alloc(arena_t *arena, size_t size);
char *arena_strdup(arena_t *arena, const char *str);
void arena_reset(arena_t *arena);
void arena_destroy(arena_t *arena);
#define ARENA_ALLOC(arena, type) ((type *)arena_alloc(arena, sizeof(type)))
#define ARENA_ARRAY(arena, type, count) ((type *)arena_alloc(arena, sizeof(type) * (count)))
#endif

259
src/core/string.c Normal file
View File

@ -0,0 +1,259 @@
// retoor <retoor@molodetz.nl>
#include "string.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct string_t {
char *data;
size_t length;
size_t capacity;
};
static const size_t DEFAULT_CAPACITY = 32;
string_handle string_create(const char *str) {
if (!str) return string_create_empty();
return string_create_n(str, strlen(str));
}
string_handle string_create_empty(void) {
struct string_t *s = calloc(1, sizeof(struct string_t));
if (!s) return NULL;
s->data = malloc(DEFAULT_CAPACITY);
if (!s->data) {
free(s);
return NULL;
}
s->data[0] = '\0';
s->length = 0;
s->capacity = DEFAULT_CAPACITY;
return s;
}
string_handle string_create_n(const char *str, size_t len) {
if (!str) return string_create_empty();
if (len == 0) return string_create_empty();
struct string_t *s = malloc(sizeof(struct string_t));
if (!s) return NULL;
s->capacity = len + 1;
if (s->capacity < DEFAULT_CAPACITY) s->capacity = DEFAULT_CAPACITY;
s->data = malloc(s->capacity);
if (!s->data) {
free(s);
return NULL;
}
memcpy(s->data, str, len);
s->data[len] = '\0';
s->length = len;
return s;
}
string_handle string_format(const char *fmt, ...) {
if (!fmt) return string_create_empty();
va_list args, args_copy;
va_start(args, fmt);
va_copy(args_copy, args);
int len = vsnprintf(NULL, 0, fmt, args_copy);
va_end(args_copy);
if (len < 0) {
va_end(args);
return string_create_empty();
}
struct string_t *s = malloc(sizeof(struct string_t));
if (!s) {
va_end(args);
return NULL;
}
s->capacity = (size_t)len + 1;
s->data = malloc(s->capacity);
if (!s->data) {
free(s);
va_end(args);
return NULL;
}
vsnprintf(s->data, s->capacity, fmt, args);
s->length = (size_t)len;
va_end(args);
return s;
}
string_handle string_clone(const char *str) {
return string_create(str);
}
string_handle string_clone_handle(string_handle s) {
if (!s) return NULL;
return string_create_n(s->data, s->length);
}
string_handle string_concat(const char *a, const char *b) {
if (!a) return string_create(b);
if (!b) return string_create(a);
size_t len_a = strlen(a);
size_t len_b = strlen(b);
struct string_t *s = malloc(sizeof(struct string_t));
if (!s) return NULL;
s->length = len_a + len_b;
s->capacity = s->length + 1;
s->data = malloc(s->capacity);
if (!s->data) {
free(s);
return NULL;
}
memcpy(s->data, a, len_a);
memcpy(s->data + len_a, b, len_b);
s->data[s->length] = '\0';
return s;
}
string_handle string_concat_handle(string_handle a, const char *b) {
if (!a) return string_create(b);
if (!b) return string_clone_handle(a);
size_t len_b = strlen(b);
size_t new_len = a->length + len_b;
if (new_len + 1 > a->capacity) {
size_t new_capacity = a->capacity * 2;
while (new_capacity < new_len + 1) new_capacity *= 2;
char *new_data = realloc(a->data, new_capacity);
if (!new_data) return NULL;
a->data = new_data;
a->capacity = new_capacity;
}
memcpy(a->data + a->length, b, len_b);
a->length = new_len;
a->data[a->length] = '\0';
return a;
}
void string_append(string_handle s, const char *str) {
if (!s || !str) return;
string_append_n(s, str, strlen(str));
}
void string_append_n(string_handle s, const char *str, size_t len) {
if (!s || !str || len == 0) return;
if (s->length + len + 1 > s->capacity) {
size_t new_capacity = s->capacity;
while (new_capacity < s->length + len + 1) new_capacity *= 2;
char *new_data = realloc(s->data, new_capacity);
if (!new_data) return;
s->data = new_data;
s->capacity = new_capacity;
}
memcpy(s->data + s->length, str, len);
s->length += len;
s->data[s->length] = '\0';
}
void string_appendf(string_handle s, const char *fmt, ...) {
if (!s || !fmt) return;
va_list args;
va_start(args, fmt);
int len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
if (len < 0) return;
if (s->length + (size_t)len + 1 > s->capacity) {
size_t new_capacity = s->capacity;
while (new_capacity < s->length + (size_t)len + 1) new_capacity *= 2;
char *new_data = realloc(s->data, new_capacity);
if (!new_data) return;
s->data = new_data;
s->capacity = new_capacity;
}
va_start(args, fmt);
vsnprintf(s->data + s->length, s->capacity - s->length, fmt, args);
s->length += (size_t)len;
va_end(args);
}
void string_append_handle(string_handle dest, string_handle src) {
if (!dest || !src) return;
string_append_n(dest, src->data, src->length);
}
size_t string_length(string_handle s) {
return s ? s->length : 0;
}
size_t string_capacity(string_handle s) {
return s ? s->capacity : 0;
}
const char *string_c_str(string_handle s) {
return s ? s->data : "";
}
char *string_release(string_handle s) {
if (!s) return NULL;
char *data = s->data;
free(s);
return data;
}
bool string_equals(string_handle a, string_handle b) {
if (!a && !b) return true;
if (!a || !b) return false;
if (a->length != b->length) return false;
return memcmp(a->data, b->data, a->length) == 0;
}
bool string_equals_c_str(string_handle s, const char *str) {
if (!s && !str) return true;
if (!s || !str) return false;
return strcmp(s->data, str) == 0;
}
bool string_is_empty(string_handle s) {
return !s || s->length == 0;
}
void string_clear(string_handle s) {
if (!s) return;
s->length = 0;
if (s->data) s->data[0] = '\0';
}
void string_destroy(string_handle s) {
if (!s) return;
if (s->data) free(s->data);
free(s);
}

37
src/core/string.h Normal file
View File

@ -0,0 +1,37 @@
// retoor <retoor@molodetz.nl>
#ifndef R_STRING_H
#define R_STRING_H
#include "../include/r_error.h"
#include <stddef.h>
#include <stdbool.h>
typedef struct string_t *string_handle;
string_handle string_create(const char *str);
string_handle string_create_empty(void);
string_handle string_create_n(const char *str, size_t len);
string_handle string_format(const char *fmt, ...);
string_handle string_clone(const char *str);
string_handle string_clone_handle(string_handle s);
string_handle string_concat(const char *a, const char *b);
string_handle string_concat_handle(string_handle a, const char *b);
void string_append(string_handle s, const char *str);
void string_append_n(string_handle s, const char *str, size_t len);
void string_appendf(string_handle s, const char *fmt, ...);
void string_append_handle(string_handle dest, string_handle src);
size_t string_length(string_handle s);
size_t string_capacity(string_handle s);
const char *string_c_str(string_handle s);
char *string_release(string_handle s);
bool string_equals(string_handle a, string_handle b);
bool string_equals_c_str(string_handle s, const char *str);
bool string_is_empty(string_handle s);
void string_clear(string_handle s);
void string_destroy(string_handle s);
#endif

View File

@ -83,6 +83,16 @@ r_status_t db_init(db_handle db) {
" session_key TEXT PRIMARY KEY,"
" data TEXT NOT NULL,"
" updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
");"
"CREATE TABLE IF NOT EXISTS blackboard ("
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
" sender TEXT NOT NULL,"
" recipient TEXT,"
" topic TEXT NOT NULL,"
" message TEXT NOT NULL,"
" status TEXT DEFAULT 'pending',"
" created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,"
" updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
");";
char *err_msg = NULL;

167
src/impl/db_sqlite.c Normal file
View File

@ -0,0 +1,167 @@
#include "database.h"
#include "util/path.h"
#include <sqlite3.h>
#include <stdlib.h>
#include <string.h>
struct database_t {
sqlite3 *conn;
char *path;
};
database_handle database_open(const char *path) {
struct database_t *db = calloc(1, sizeof(struct database_t));
if (!db) return NULL;
char *expanded_path = path_expand_home(path);
if (!expanded_path) {
free(db);
return NULL;
}
if (sqlite3_open(expanded_path, &db->conn) != SQLITE_OK) {
free(expanded_path);
free(db);
return NULL;
}
db->path = expanded_path;
database_init(db);
return db;
}
void database_close(database_handle db) {
if (!db) return;
if (db->conn) sqlite3_close(db->conn);
if (db->path) free(db->path);
free(db);
}
r_status_t database_init(database_handle db) {
if (!db || !db->conn) return R_ERROR_INVALID_ARG;
const char *sql =
"CREATE TABLE IF NOT EXISTS kv ("
" key TEXT PRIMARY KEY,"
" value TEXT NOT NULL,"
" created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,"
" updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
");"
"CREATE TABLE IF NOT EXISTS file_versions ("
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
" path TEXT NOT NULL,"
" content TEXT NOT NULL,"
" created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
");"
"CREATE TABLE IF NOT EXISTS conversations ("
" session_key TEXT PRIMARY KEY,"
" data TEXT NOT NULL,"
" updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
");"
"CREATE TABLE IF NOT EXISTS blackboard ("
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
" sender TEXT NOT NULL,"
" recipient TEXT,"
" topic TEXT NOT NULL,"
" message TEXT NOT NULL,"
" status TEXT DEFAULT 'pending',"
" created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,"
" updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
");";
char *err_msg = NULL;
if (sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg) != SQLITE_OK) {
sqlite3_free(err_msg);
return R_ERROR_DB_CONNECTION;
}
return R_SUCCESS;
}
r_status_t database_execute(database_handle db, const char *sql, char **error) {
if (!db || !db->conn || !sql) return R_ERROR_INVALID_ARG;
char *err_msg = NULL;
if (sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg) != SQLITE_OK) {
if (error) *error = err_msg ? strdup(err_msg) : NULL;
return R_ERROR_DB_QUERY;
}
return R_SUCCESS;
}
r_status_t database_query(database_handle db, const char *sql,
database_callback_t callback, void *ctx) {
if (!db || !db->conn || !sql) return R_ERROR_INVALID_ARG;
char *err_msg = NULL;
if (sqlite3_exec(db->conn, sql,
(void (*)(void *, int, char **, char **))callback,
ctx, &err_msg) != SQLITE_OK) {
return R_ERROR_DB_QUERY;
}
return R_SUCCESS;
}
r_status_t database_set(database_handle db, const char *key, const char *value) {
if (!db || !key) return R_ERROR_INVALID_ARG;
const char *sql = "INSERT OR REPLACE INTO kv (key, value) VALUES (?, ?)";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) {
return R_ERROR_DB_QUERY;
}
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, value ? value : "", -1, SQLITE_TRANSIENT);
int result = sqlite3_step(stmt);
sqlite3_finalize(stmt);
return result == SQLITE_DONE ? R_SUCCESS : R_ERROR_DB_QUERY;
}
r_status_t database_get(database_handle db, const char *key, char **value) {
if (!db || !key) return R_ERROR_INVALID_ARG;
const char *sql = "SELECT value FROM kv WHERE key = ?";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) {
return R_ERROR_DB_QUERY;
}
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
if (sqlite3_step(stmt) == SQLITE_ROW) {
const char *result = (const char *)sqlite3_column_text(stmt, 0);
if (result) *value = strdup(result);
sqlite3_finalize(stmt);
return R_SUCCESS;
}
sqlite3_finalize(stmt);
return R_ERROR_DB_NOT_FOUND;
}
r_status_t database_delete(database_handle db, const char *key) {
if (!db || !key) return R_ERROR_INVALID_ARG;
const char *sql = "DELETE FROM kv WHERE key = ?";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) {
return R_ERROR_DB_QUERY;
}
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
int result = sqlite3_step(stmt);
sqlite3_finalize(stmt);
return result == SQLITE_DONE ? R_SUCCESS : R_ERROR_DB_QUERY;
}

164
src/impl/http_curl.c Normal file
View File

@ -0,0 +1,164 @@
#include "http.h"
#include <curl/curl.h>
#include <stdlib.h>
#include <string.h>
struct http_client_t {
CURL *curl;
char *base_url;
char *bearer_token;
long timeout;
long connect_timeout;
};
static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t total_size = size * nmemb;
struct {
char *data;
size_t size;
} *buffer = userp;
char *ptr = realloc(buffer->data, buffer->size + total_size + 1);
if (!ptr) return 0;
buffer->data = ptr;
memcpy(&(buffer->data[buffer->size]), contents, total_size);
buffer->size += total_size;
buffer->data[buffer->size] = '\0';
return total_size;
}
http_client_handle http_create(const char *base_url) {
struct http_client_t *client = calloc(1, sizeof(struct http_client_t));
if (!client) return NULL;
client->curl = curl_easy_init();
if (!client->curl) {
free(client);
return NULL;
}
client->base_url = base_url ? strdup(base_url) : NULL;
client->bearer_token = NULL;
client->timeout = 0;
client->connect_timeout = 0;
curl_easy_setopt(client->curl, CURLOPT_WRITEFUNCTION, write_callback);
return client;
}
void http_destroy(http_client_handle client) {
if (!client) return;
if (client->curl) curl_easy_cleanup(client->curl);
if (client->base_url) free(client->base_url);
if (client->bearer_token) free(client->bearer_token);
free(client);
}
void http_set_bearer_token(http_client_handle client, const char *token) {
if (!client) return;
if (client->bearer_token) free(client->bearer_token);
client->bearer_token = token ? strdup(token) : NULL;
}
void http_set_timeout(http_client_handle client, long timeout_seconds) {
if (!client) return;
client->timeout = timeout_seconds;
}
void http_set_connect_timeout(http_client_handle client, long timeout_seconds) {
if (!client) return;
client->connect_timeout = timeout_seconds;
}
static const char *method_string(http_method_t method) {
switch (method) {
case HTTP_METHOD_GET: return "GET";
case HTTP_METHOD_POST: return "POST";
case HTTP_METHOD_PUT: return "PUT";
case HTTP_METHOD_DELETE: return "DELETE";
default: return "GET";
}
}
r_status_t http_request(http_client_handle client, http_method_t method,
const char *path, const void *body, size_t body_size,
char **response, int *response_code) {
if (!client || !client->curl) return R_ERROR_INVALID_ARG;
struct {
char *data;
size_t size;
} buffer = {NULL, 0};
curl_easy_setopt(client->curl, CURLOPT_WRITEDATA, &buffer);
char url[4096];
if (client->base_url) {
snprintf(url, sizeof(url), "%s%s", client->base_url, path);
} else {
strncpy(url, path, sizeof(url) - 1);
url[sizeof(url) - 1] = '\0';
}
curl_easy_setopt(client->curl, CURLOPT_URL, url);
curl_easy_setopt(client->curl, CURLOPT_CUSTOMREQUEST, method_string(method));
if (client->bearer_token) {
char auth_header[512];
snprintf(auth_header, sizeof(auth_header), "Authorization: Bearer %s", client->bearer_token);
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, auth_header);
curl_easy_setopt(client->curl, CURLOPT_HTTPHEADER, headers);
}
if (client->timeout > 0) {
curl_easy_setopt(client->curl, CURLOPT_TIMEOUT, client->timeout);
}
if (client->connect_timeout > 0) {
curl_easy_setopt(client->curl, CURLOPT_CONNECTTIMEOUT, client->connect_timeout);
}
if (body && body_size > 0) {
curl_easy_setopt(client->curl, CURLOPT_POSTFIELDSIZE, (long)body_size);
curl_easy_setopt(client->curl, CURLOPT_POSTFIELDS, body);
}
CURLcode res = curl_easy_perform(client->curl);
if (res != CURLE_OK) {
if (buffer.data) free(buffer.data);
return R_ERROR_HTTP_CONNECTION;
}
long code;
curl_easy_getinfo(client->curl, CURLINFO_RESPONSE_CODE, &code);
if (response_code) *response_code = (int)code;
if (response) {
*response = buffer.data ? buffer.data : strdup("");
} else {
if (buffer.data) free(buffer.data);
}
return R_SUCCESS;
}
r_status_t http_get(http_client_handle client, const char *path, char **response) {
int code;
return http_request(client, HTTP_METHOD_GET, path, NULL, 0, response, &code);
}
r_status_t http_post(http_client_handle client, const char *path, const char *body, char **response) {
int code;
size_t body_size = body ? strlen(body) : 0;
return http_request(client, HTTP_METHOD_POST, path, body, body_size, response, &code);
}
r_status_t http_post_json(http_client_handle client, const char *path, const char *json, char **response) {
return http_post(client, path, json, response);
}

188
src/interfaces/config.c Normal file
View File

@ -0,0 +1,188 @@
#include "config.h"
#include "util/path.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct config_t {
char *api_url;
char *models_url;
char *model;
char *api_key;
char *db_path;
char *session_id;
char *system_message;
double temperature;
bool use_tools;
bool use_strict;
bool verbose;
};
static struct config_t *instance = NULL;
static char *strdup_safe(const char *s) {
return s ? strdup(s) : NULL;
}
static bool resolve_env_bool(const char *env_name, bool default_val) {
const char *val = getenv(env_name);
if (!val) return default_val;
if (!strcmp(val, "true") || !strcmp(val, "1")) return true;
if (!strcmp(val, "false") || !strcmp(val, "0")) return false;
return default_val;
}
static const char *resolve_api_key(void) {
const char *key = getenv("OPENROUTER_API_KEY");
if (key && *key) return key;
key = getenv("R_KEY");
if (key && *key) return key;
key = getenv("OPENAI_API_KEY");
if (key && *key) return key;
return "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";
}
static bool is_valid_session_id(const char *session_id) {
if (!session_id || !*session_id) return false;
if (strlen(session_id) > 255) return false;
for (const char *p = session_id; *p; p++) {
if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_' && *p != '.') {
return false;
}
}
return true;
}
config_handle config_create(void) {
if (instance) return instance;
struct config_t *cfg = calloc(1, sizeof(struct config_t));
if (!cfg) return NULL;
const char *base_url = getenv("R_BASE_URL");
if (base_url && *base_url) {
size_t len = strlen(base_url);
cfg->api_url = malloc(len + 32);
cfg->models_url = malloc(len + 32);
if (cfg->api_url && cfg->models_url) {
snprintf(cfg->api_url, len + 32, "%s/v1/chat/completions", base_url);
snprintf(cfg->models_url, len + 32, "%s/v1/models", base_url);
}
} else {
cfg->api_url = strdup("https://api.openai.com/v1/chat/completions");
cfg->models_url = strdup("https://api.openai.com/v1/models");
}
const char *model = getenv("R_MODEL");
cfg->model = strdup(model && *model ? model : "gpt-4o-mini");
cfg->api_key = strdup(resolve_api_key());
cfg->db_path = strdup("~/.r.db");
cfg->temperature = 0.1;
cfg->use_tools = resolve_env_bool("R_USE_TOOLS", true);
cfg->use_strict = resolve_env_bool("R_USE_STRICT", true);
cfg->verbose = false;
const char *session = getenv("R_SESSION");
if (session && is_valid_session_id(session)) {
cfg->session_id = strdup(session);
} else {
cfg->session_id = strdup("default");
}
const char *system_msg = getenv("R_SYSTEM_MESSAGE");
cfg->system_message = strdup_safe(system_msg);
instance = cfg;
return cfg;
}
void config_destroy(config_handle cfg) {
if (!cfg) return;
if (cfg != instance) {
if (cfg->api_url) free(cfg->api_url);
if (cfg->models_url) free(cfg->models_url);
if (cfg->model) free(cfg->model);
if (cfg->api_key) free(cfg->api_key);
if (cfg->db_path) free(cfg->db_path);
if (cfg->session_id) free(cfg->session_id);
if (cfg->system_message) free(cfg->system_message);
free(cfg);
}
}
const char *config_get_api_url(config_handle cfg) {
return cfg ? cfg->api_url : NULL;
}
const char *config_get_models_url(config_handle cfg) {
return cfg ? cfg->models_url : NULL;
}
const char *config_get_api_key(config_handle cfg) {
return cfg ? cfg->api_key : NULL;
}
const char *config_get_model(config_handle cfg) {
return cfg ? cfg->model : NULL;
}
const char *config_get_db_path(config_handle cfg) {
return cfg ? cfg->db_path : NULL;
}
const char *config_get_session_id(config_handle cfg) {
return cfg ? cfg->session_id : NULL;
}
const char *config_get_system_message(config_handle cfg) {
return cfg ? cfg->system_message : NULL;
}
double config_get_temperature(config_handle cfg) {
return cfg ? cfg->temperature : 0.0;
}
bool config_use_tools(config_handle cfg) {
return cfg ? cfg->use_tools : true;
}
bool config_use_strict(config_handle cfg) {
return cfg ? cfg->use_strict : true;
}
bool config_is_verbose(config_handle cfg) {
return cfg ? cfg->verbose : false;
}
r_status_t config_set_model(config_handle cfg, const char *model) {
if (!cfg || !model) return R_ERROR_INVALID_ARG;
if (cfg->model) free(cfg->model);
cfg->model = strdup(model);
return R_SUCCESS;
}
r_status_t config_set_session_id(config_handle cfg, const char *session_id) {
if (!cfg || !session_id) return R_ERROR_INVALID_ARG;
if (!is_valid_session_id(session_id)) {
return R_ERROR_SESSION_INVALID;
}
if (cfg->session_id) free(cfg->session_id);
cfg->session_id = strdup(session_id);
return R_SUCCESS;
}
void config_set_verbose(config_handle cfg, bool verbose) {
if (cfg) cfg->verbose = verbose;
}

30
src/interfaces/config.h Normal file
View File

@ -0,0 +1,30 @@
// retoor <retoor@molodetz.nl>
#ifndef R_INTERFACES_CONFIG_H
#define R_INTERFACES_CONFIG_H
#include <stdbool.h>
typedef struct config_t *config_handle;
config_handle config_create(void);
void config_destroy(config_handle cfg);
const char *config_get_api_url(config_handle cfg);
const char *config_get_models_url(config_handle cfg);
const char *config_get_api_key(config_handle cfg);
const char *config_get_model(config_handle cfg);
const char *config_get_db_path(config_handle cfg);
const char *config_get_session_id(config_handle cfg);
const char *config_get_system_message(config_handle cfg);
double config_get_temperature(config_handle cfg);
bool config_use_tools(config_handle cfg);
bool config_use_strict(config_handle cfg);
bool config_is_verbose(config_handle cfg);
r_status_t config_set_model(config_handle cfg, const char *model);
r_status_t config_set_session_id(config_handle cfg, const char *session_id);
void config_set_verbose(config_handle cfg, bool verbose);
#endif

27
src/interfaces/database.h Normal file
View File

@ -0,0 +1,27 @@
// retoor <retoor@molodetz.nl>
#ifndef R_INTERFACES_DATABASE_H
#define R_INTERFACES_DATABASE_H
#include "r_error.h"
#include <stdbool.h>
#include <stddef.h>
typedef struct database_t *database_handle;
database_handle database_open(const char *path);
void database_close(database_handle db);
r_status_t database_init(database_handle db);
r_status_t database_execute(database_handle db, const char *sql, char **error);
typedef void (*database_callback_t)(void *ctx, int col_count, char **values, char **names);
r_status_t database_query(database_handle db, const char *sql,
database_callback_t callback, void *ctx);
r_status_t database_set(database_handle db, const char *key, const char *value);
r_status_t database_get(database_handle db, const char *key, char **value);
r_status_t database_delete(database_handle db, const char *key);
#endif

40
src/interfaces/http.h Normal file
View File

@ -0,0 +1,40 @@
// retoor <retoor@molodetz.nl>
#ifndef R_INTERFACES_HTTP_H
#define R_INTERFACES_HTTP_H
#include "r_error.h"
#include <stdbool.h>
#include <stddef.h>
typedef struct http_client_t *http_client_handle;
typedef enum {
HTTP_METHOD_GET,
HTTP_METHOD_POST,
HTTP_METHOD_PUT,
HTTP_METHOD_DELETE
} http_method_t;
http_client_handle http_create(const char *base_url);
void http_destroy(http_client_handle client);
void http_set_bearer_token(http_client_handle client, const char *token);
void http_set_timeout(http_client_handle client, long timeout_seconds);
void http_set_connect_timeout(http_client_handle client, long timeout_seconds);
r_status_t http_request(
http_client_handle client,
http_method_t method,
const char *path,
const void *body,
size_t body_size,
char **response,
int *response_code
);
r_status_t http_get(http_client_handle client, const char *path, char **response);
r_status_t http_post(http_client_handle client, const char *path, const char *body, char **response);
r_status_t http_post_json(http_client_handle client, const char *path, const char *json, char **response);
#endif

66
src/interfaces/logger.c Normal file
View File

@ -0,0 +1,66 @@
// retoor <retoor@molodetz.nl>
#include "logger.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
struct logger_t {
log_level_t level;
};
static const char *level_strings[] = {
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
};
static const char *level_colors[] = {
"\033[90m", "\033[36m", "\033[32m", "\033[33m", "\033[31m", "\033[35m"
};
static const char *color_reset = "\033[0m";
logger_handle logger_create(log_level_t level) {
struct logger_t *logger = malloc(sizeof(struct logger_t));
if (!logger) return NULL;
logger->level = level;
return logger;
}
void logger_destroy(logger_handle logger) {
if (logger) free(logger);
}
void logger_set_level(logger_handle logger, log_level_t level) {
if (logger) logger->level = level;
}
log_level_t logger_get_level(logger_handle logger) {
return logger ? logger->level : LOG_LEVEL_INFO;
}
void logger_log(logger_handle logger, log_level_t level,
const char *file, int line, const char *fmt, ...) {
if (!logger || !fmt) return;
if (level < logger->level) return;
va_list args;
va_start(args, fmt);
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char time_buf[32];
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", tm_info);
const char *basename = strrchr(file, '/');
basename = basename ? basename + 1 : file;
fprintf(stderr, "%s[%s%s%s %s:%d] ",
level_colors[level], level_strings[level], color_reset,
basename, line);
vfprintf(stderr, fmt, args);
fprintf(stderr, "%s\n", color_reset);
va_end(args);
}

41
src/interfaces/logger.h Normal file
View File

@ -0,0 +1,41 @@
// retoor <retoor@molodetz.nl>
#ifndef R_INTERFACES_LOGGER_H
#define R_INTERFACES_LOGGER_H
#include <stdbool.h>
typedef enum {
LOG_LEVEL_TRACE,
LOG_LEVEL_DEBUG,
LOG_LEVEL_INFO,
LOG_LEVEL_WARN,
LOG_LEVEL_ERROR,
LOG_LEVEL_FATAL
} log_level_t;
typedef struct logger_t *logger_handle;
logger_handle logger_create(log_level_t level);
void logger_destroy(logger_handle logger);
void logger_set_level(logger_handle logger, log_level_t level);
log_level_t logger_get_level(logger_handle logger);
void logger_log(logger_handle logger, log_level_t level,
const char *file, int line, const char *fmt, ...);
#define LOG_TRACE(logger, fmt, ...) \
logger_log(logger, LOG_LEVEL_TRACE, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOG_DEBUG(logger, fmt, ...) \
logger_log(logger, LOG_LEVEL_DEBUG, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOG_INFO(logger, fmt, ...) \
logger_log(logger, LOG_LEVEL_INFO, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOG_WARN(logger, fmt, ...) \
logger_log(logger, LOG_LEVEL_WARN, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOG_ERROR(logger, fmt, ...) \
logger_log(logger, LOG_LEVEL_ERROR, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOG_FATAL(logger, fmt, ...) \
logger_log(logger, LOG_LEVEL_FATAL, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#endif

View File

@ -305,6 +305,13 @@ static void init(void) {
"- You can call multiple tools in sequence to accomplish complex "
"tasks\n\n"
"- Take decissions based on tool output yourself autonomously."
"## Multi-Agent Orchestration\n"
"You can delegate tasks to specialized agents using the `spawn_agent` tool.\n"
"Personas:\n"
"- researcher: Best for web search and data gathering.\n"
"- developer: Best for writing and testing code.\n"
"- security: Best for security audits and vulnerability analysis.\n\n"
"Use the `blackboard` table in the database to share state and pass detailed instructions or results between agents if they exceed tool parameter limits.\n\n"
"## CRITICAL OUTPUT RULES\n"
"- You MUST include the actual content/data from tool results in your "
"response\n"

146
src/tools/tool_agent.c Normal file
View File

@ -0,0 +1,146 @@
// retoor <retoor@molodetz.nl>
#include "tool.h"
#include "agent.h"
#include "messages.h"
#include "r_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct {
tool_t tool;
} tool_agent_t;
static struct json_object *tool_spawn_agent_get_description(void) {
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "name", json_object_new_string("spawn_agent"));
json_object_object_add(obj, "description", json_object_new_string("Spawn a specialized sub-agent to handle a specific task."));
struct json_object *params = json_object_new_object();
json_object_object_add(params, "type", json_object_new_string("object"));
struct json_object *props = json_object_new_object();
struct json_object *persona = json_object_new_object();
json_object_object_add(persona, "type", json_object_new_string("string"));
json_object_object_add(persona, "description", json_object_new_string("The persona of the agent (researcher, developer, security)."));
struct json_object *persona_enum = json_object_new_array();
json_object_array_add(persona_enum, json_object_new_string("researcher"));
json_object_array_add(persona_enum, json_object_new_string("developer"));
json_object_array_add(persona_enum, json_object_new_string("security"));
json_object_object_add(persona, "enum", persona_enum);
json_object_object_add(props, "persona", persona);
struct json_object *goal = json_object_new_object();
json_object_object_add(goal, "type", json_object_new_string("string"));
json_object_object_add(goal, "description", json_object_new_string("The specific task or goal for the sub-agent."));
json_object_object_add(props, "goal", goal);
json_object_object_add(params, "properties", props);
struct json_object *required = json_object_new_array();
json_object_array_add(required, json_object_new_string("persona"));
json_object_array_add(required, json_object_new_string("goal"));
json_object_object_add(params, "required", required);
json_object_object_add(obj, "parameters", params);
struct json_object *full_obj = json_object_new_object();
json_object_object_add(full_obj, "type", json_object_new_string("function"));
json_object_object_add(full_obj, "function", obj);
return full_obj;
}
static char *tool_spawn_agent_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *persona_obj, *goal_obj;
if (!json_object_object_get_ex(args, "persona", &persona_obj) ||
!json_object_object_get_ex(args, "goal", &goal_obj)) {
return strdup("Error: Missing persona or goal");
}
const char *persona_str = json_object_get_string(persona_obj);
const char *goal_str = json_object_get_string(goal_obj);
tool_registry_type_t type = TOOL_TYPE_ALL;
const char *system_prompt = NULL;
if (strcmp(persona_str, "researcher") == 0) {
type = TOOL_TYPE_RESEARCHER;
system_prompt = "You are a specialized Research Agent. Your goal is to find, extract, and summarize information. "
"Do not attempt to write or execute code unless it's for data analysis. "
"Focus on using web search, http fetch, and reading files.";
} else if (strcmp(persona_str, "developer") == 0) {
type = TOOL_TYPE_DEVELOPER;
system_prompt = "You are a specialized Developer Agent. Your goal is to write, test, and debug code. "
"Use the terminal, file editing tools, and python execution to fulfill your task. "
"Always verify your changes by running tests or the code itself.";
} else if (strcmp(persona_str, "security") == 0) {
type = TOOL_TYPE_SECURITY;
system_prompt = "You are a specialized Security Auditor Agent. Your goal is to find vulnerabilities and perform security analysis. "
"Be pedantic and thorough. Use fuzzing, port scanning, and code analysis tools. "
"Report any findings clearly with potential impact.";
} else {
return strdup("Error: Invalid persona");
}
char session_id[256];
snprintf(session_id, sizeof(session_id), "subagent-%s-%u", persona_str, (unsigned int)time(NULL));
messages_handle msgs = messages_create(session_id);
messages_add(msgs, "system", system_prompt);
agent_handle agent = agent_create(goal_str, msgs);
if (!agent) {
messages_destroy(msgs);
return strdup("Error: Failed to create sub-agent");
}
tool_registry_t *specialized_tools = tool_registry_get_specialized(type);
agent_set_tool_registry(agent, specialized_tools);
agent_set_max_iterations(agent, 50); // Sub-agents have lower limit
char *result = agent_run(agent, goal_str);
agent_destroy(agent);
// specialized_tools should be destroyed?
// In tool_registry_get_specialized we create a new one.
tool_registry_destroy(specialized_tools);
if (!result) {
return strdup("Sub-agent failed to provide a result.");
}
return result;
}
static void tool_spawn_agent_print_action(const char *name, struct json_object *args) {
struct json_object *persona_obj, *goal_obj;
const char *persona = "unknown";
const char *goal = "unknown";
if (json_object_object_get_ex(args, "persona", &persona_obj)) persona = json_object_get_string(persona_obj);
if (json_object_object_get_ex(args, "goal", &goal_obj)) goal = json_object_get_string(goal_obj);
printf("\033[1;34m[Agent] Spawning %s agent for: %s\033[0m\n", persona, goal);
}
static const tool_vtable_t tool_spawn_agent_vtable = {
.get_description = tool_spawn_agent_get_description,
.execute = tool_spawn_agent_execute,
.print_action = tool_spawn_agent_print_action
};
tool_t *tool_spawn_agent_create(void) {
tool_agent_t *tool = calloc(1, sizeof(tool_agent_t));
if (!tool) return NULL;
tool->tool.vtable = &tool_spawn_agent_vtable;
tool->tool.name = "spawn_agent";
return &tool->tool;
}

153
src/util/path.c Normal file
View File

@ -0,0 +1,153 @@
// retoor <retoor@molodetz.nl>
#include "path.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
char *path_expand_home(const char *path) {
if (!path) return NULL;
if (path[0] != '~') return strdup(path);
const char *home_dir = getenv("HOME");
if (!home_dir) home_dir = getenv("USERPROFILE");
if (!home_dir) return strdup(path);
size_t home_len = strlen(home_dir);
size_t path_len = strlen(path);
char *expanded = malloc(home_len + path_len);
if (!expanded) return NULL;
strcpy(expanded, home_dir);
strcat(expanded, path + 1);
return expanded;
}
char *path_normalize(const char *path) {
if (!path) return NULL;
char *normalized = strdup(path);
if (!normalized) return NULL;
size_t write_idx = 0;
for (size_t i = 0; normalized[i]; i++) {
if (normalized[i] == '/' && write_idx > 0 && normalized[write_idx - 1] == '/') {
continue;
}
normalized[write_idx++] = normalized[i];
}
normalized[write_idx] = '\0';
return normalized;
}
char *path_join(const char *base, const char *relative) {
if (!base) return relative ? strdup(relative) : NULL;
if (!relative) return strdup(base);
size_t base_len = strlen(base);
size_t rel_len = strlen(relative);
bool needs_sep = base_len > 0 && base[base_len - 1] != '/';
size_t total_len = base_len + rel_len + (needs_sep ? 1 : 0);
char *joined = malloc(total_len + 1);
if (!joined) return NULL;
strcpy(joined, base);
if (needs_sep) strcat(joined, "/");
strcat(joined, relative);
return joined;
}
char *path_dirname(const char *path) {
if (!path || !*path) return strdup(".");
char *copy = strdup(path);
if (!copy) return NULL;
char *last_sep = strrchr(copy, '/');
if (last_sep) {
if (last_sep == copy) {
last_sep[1] = '\0';
} else {
*last_sep = '\0';
}
} else {
strcpy(copy, ".");
}
return copy;
}
char *path_basename(const char *path) {
if (!path || !*path) return strdup(".");
const char *last_sep = strrchr(path, '/');
if (last_sep) {
return strdup(last_sep + 1);
}
return strdup(path);
}
bool path_exists(const char *path) {
if (!path) return false;
struct stat st;
return stat(path, &st) == 0;
}
bool path_is_file(const char *path) {
if (!path) return false;
struct stat st;
return stat(path, &st) == 0 && S_ISREG(st.st_mode);
}
bool path_is_directory(const char *path) {
if (!path) return false;
struct stat st;
return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
}
bool path_ensure_parent_exists(const char *path) {
if (!path) return false;
char *parent = path_dirname(path);
if (!parent) return false;
bool exists = path_is_directory(parent);
if (exists) {
free(parent);
return true;
}
char temp[4096];
size_t len = strlen(parent);
if (len >= sizeof(temp)) {
free(parent);
return false;
}
memcpy(temp, parent, len + 1);
if (temp[len - 1] == '/') temp[len - 1] = '\0';
for (char *p = temp + 1; *p; p++) {
if (*p == '/') {
*p = '\0';
if (mkdir(temp, 0755) != 0 && errno != EEXIST) {
free(parent);
return false;
}
*p = '/';
}
}
if (mkdir(temp, 0755) != 0 && errno != EEXIST) {
free(parent);
return false;
}
free(parent);
return true;
}

19
src/util/path.h Normal file
View File

@ -0,0 +1,19 @@
// retoor <retoor@molodetz.nl>
#ifndef R_UTIL_PATH_H
#define R_UTIL_PATH_H
#include <stdbool.h>
#include <stddef.h>
char *path_expand_home(const char *path);
char *path_normalize(const char *path);
char *path_join(const char *base, const char *relative);
char *path_dirname(const char *path);
char *path_basename(const char *path);
bool path_exists(const char *path);
bool path_is_file(const char *path);
bool path_is_directory(const char *path);
bool path_ensure_parent_exists(const char *path);
#endif

50
src/util/time.c Normal file
View File

@ -0,0 +1,50 @@
// retoor <retoor@molodetz.nl>
#define _XOPEN_SOURCE
#include "time.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
char *time_format_iso8601(time_t t) {
struct tm *tm_info = localtime(&t);
if (!tm_info) return NULL;
char *buf = malloc(32);
if (!buf) return NULL;
strftime(buf, 32, "%Y-%m-%dT%H:%M:%S%z", tm_info);
return buf;
}
char *time_format_local(time_t t, const char *format) {
struct tm *tm_info = localtime(&t);
if (!tm_info) return NULL;
const char *fmt = format ? format : "%Y-%m-%d %H:%M:%S";
char *buf = malloc(64);
if (!buf) return NULL;
strftime(buf, 64, fmt, tm_info);
return buf;
}
time_t time_parse_iso8601(const char *str) {
if (!str) return 0;
struct tm tm = {0};
char *result = strptime(str, "%Y-%m-%dT%H:%M:%S", &tm);
if (!result) {
result = strptime(str, "%Y-%m-%d %H:%M:%S", &tm);
}
if (!result) return 0;
return mktime(&tm);
}
double time_elapsed_seconds(time_t start) {
time_t now = time(NULL);
return difftime(now, start);
}

14
src/util/time.h Normal file
View File

@ -0,0 +1,14 @@
// retoor <retoor@molodetz.nl>
#ifndef R_UTIL_TIME_H
#define R_UTIL_TIME_H
#include <stddef.h>
#include <time.h>
char *time_format_iso8601(time_t t);
char *time_format_local(time_t t, const char *format);
time_t time_parse_iso8601(const char *str);
double time_elapsed_seconds(time_t start);
#endif

1720
tools.h

File diff suppressed because it is too large Load Diff

102
url.h
View File

@ -1,102 +0,0 @@
// Written by retoor@molodetz.nl
// This code defines a URL parser in C that extracts components such as the
// scheme, hostname, port, path, and query from a given URL.
// Includes:
// - <stdio.h> for standard I/O operations
// - <string.h> for string manipulation functions
// - <stdlib.h> for memory allocation and management
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or
// substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS",
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
// THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef R_URL_H
#define R_URL_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char scheme[16];
char hostname[256];
char port[8];
char path[512];
char query[512];
} url_t;
int parse_url(const char *url, url_t *parsed_url) {
memset(parsed_url, 0, sizeof(url_t));
const char *scheme_end = strstr(url, "://");
if (!scheme_end) {
return -1;
}
strncpy(parsed_url->scheme, url, scheme_end - url);
parsed_url->scheme[scheme_end - url] = '\0';
const char *hostname_start = scheme_end + 3;
const char *port_start = strchr(hostname_start, ':');
const char *path_start = strchr(hostname_start, '/');
const char *query_start = strchr(hostname_start, '?');
if (port_start && (!path_start || port_start < path_start)) {
size_t hostname_length = port_start - hostname_start;
if (hostname_length >= sizeof(parsed_url->hostname)) {
fprintf(stderr, "Hostname is too long\n");
return -1;
}
strncpy(parsed_url->hostname, hostname_start, hostname_length);
parsed_url->hostname[hostname_length] = '\0';
size_t port_length = query_start ? (size_t)(query_start - port_start - 1)
: strlen(port_start + 1);
if (port_length >= sizeof(parsed_url->port)) {
fprintf(stderr, "Port value is too long\n");
return -1;
}
strncpy(parsed_url->port, port_start + 1, port_length);
parsed_url->port[port_length] = '\0';
} else {
size_t hostname_length =
path_start ? (size_t)(path_start - hostname_start)
: (query_start ? (size_t)(query_start - hostname_start)
: strlen(hostname_start));
if (hostname_length >= sizeof(parsed_url->hostname)) {
fprintf(stderr, "Hostname is too long\n");
return -1;
}
strncpy(parsed_url->hostname, hostname_start, hostname_length);
parsed_url->hostname[hostname_length] = '\0';
}
if (path_start) {
if (query_start) {
strncpy(parsed_url->path, path_start, query_start - path_start);
parsed_url->path[query_start - path_start] = '\0';
} else {
strcpy(parsed_url->path, path_start);
}
}
if (query_start) {
strcpy(parsed_url->query, query_start + 1);
}
return 0;
}
#endif

146
utils.h
View File

@ -1,146 +0,0 @@
// Written by retoor@molodetz.nl
// This header file contains utility functions for manipulating file system
// paths, focusing primarily on expanding paths starting with '~' to the home
// directory.
// This code uses standard libraries: stdio.h, stdlib.h, and string.h, and
// conditionally includes the posix libraries pwd.h and unistd.h when expanding
// the home directory manually.
// MIT License
//
// Permission is granted to use, copy, modify, merge, distribute, sublicense,
// and/or sell copies of the Software. The license includes conditions about
// providing a copy of the license and the limitation of liability and warranty.
#ifndef UTILS_H
#define UTILS_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <limits.h>
#include <pwd.h>
#include <unistd.h>
#endif
void get_current_directory() {
char buffer[PATH_MAX];
#ifdef _WIN32
DWORD length = GetCurrentDirectory(PATH_MAX, buffer);
if (length > 0 && length < PATH_MAX) {
printf("Current Directory: %s\n", buffer);
} else {
printf("Error getting current directory.\n");
}
#else
if (getcwd(buffer, sizeof(buffer)) != NULL) {
printf("Current Directory: %s\n", buffer);
} else {
perror("Error getting current directory");
}
#endif
}
char *expand_home_directory(const char *path) {
if (path[0] != '~') {
return strdup(path); // Return the original path if it doesn't start with ~
}
char *home_dir;
#ifdef _WIN32
home_dir = getenv("USERPROFILE"); // Get home directory on Windows
#else
struct passwd *pw = getpwuid(getuid());
home_dir = pw->pw_dir; // Get home directory on Linux
#endif
if (home_dir == NULL) {
return NULL; // Error getting home directory
}
size_t home_len = strlen(home_dir);
size_t path_len = strlen(path + 1);
size_t expanded_size = home_len + path_len + 1;
char *expanded_path = (char *)malloc(expanded_size);
if (expanded_path == NULL) {
return NULL;
}
memcpy(expanded_path, home_dir, home_len);
memcpy(expanded_path + home_len, path + 1, path_len);
expanded_path[home_len + path_len] = '\0';
return expanded_path;
}
unsigned long hash(const char *str) {
unsigned long hash = 5381; // Starting value
int c;
while ((c = *str++)) {
hash = ((hash << 5) + hash) + c; // hash * 33 + c
}
return hash;
}
char *joinpath(const char *base_url, const char *path) {
static char result[4096];
size_t base_len = strlen(base_url);
size_t pos = 0;
if (base_len >= sizeof(result) - 2) {
base_len = sizeof(result) - 2;
}
memcpy(result, base_url, base_len);
pos = base_len;
if (pos > 0 && result[pos - 1] != '/') {
result[pos++] = '/';
}
if (path[0] == '/') {
path++;
}
size_t path_len = strlen(path);
if (pos + path_len >= sizeof(result)) {
path_len = sizeof(result) - pos - 1;
}
memcpy(result + pos, path, path_len);
result[pos + path_len] = '\0';
return result;
}
char *read_file(const char *path) {
char *expanded_path = expand_home_directory(path);
if (expanded_path == NULL) {
return NULL;
}
FILE *file = fopen(expanded_path, "r");
free(expanded_path);
if (file == NULL) {
return NULL;
}
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, 0, SEEK_SET);
char *buffer = (char *)malloc(size + 1);
size_t read = fread(buffer, 1, size, file);
if (read == 0) {
free(buffer);
return NULL;
}
fclose(file);
buffer[read] = '\0';
return buffer;
}
#endif