From 5cf55f423c1b9254b56af8bd63bcabc260ee424b Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 26 Dec 2025 07:24:13 +0100 Subject: [PATCH] Update session. --- browse.h | 198 +++------------------------------------------------- http_curl.h | 101 +++++++++++++++++++++------ main.c | 73 +++++++++++++++++-- messages.h | 136 +++++++++++++++++++++++++++++++++++- r.h | 5 ++ tools.h | 4 +- 6 files changed, 296 insertions(+), 221 deletions(-) diff --git a/browse.h b/browse.h index 7cfd14f..c10011e 100755 --- a/browse.h +++ b/browse.h @@ -1,201 +1,23 @@ #include "http_curl.h" #include -#include -#include #include #include #include char *url_encode(char *s) { return curl_easy_escape(NULL, s, 0); } -char *web_search_news(char *q) { - const int MAX_RETRIES = 3; - for (int retry = 0; retry < MAX_RETRIES; retry++) { - char *news = malloc(4096); - if (!news) - return NULL; - news[0] = 0; - char *q_encoded = url_encode(q); - snprintf( - news, 4096, - "https://search.molodetz.nl/" - "search?q=%s&format=json&categories=news&preferences=eJx1WMuy4zYO_" - "Zp4o4ormaRqahZepZL5gexVFAlLiEhCzYdt3a8PoIdFWt2Ldl8fkCCIxwForRL0FBDirQc" - "PQdmLVb7PqoebyokulrSycPP2Il81uclCgtvlrh6oybcBItkHhNsFHW9qp0Cv-" - "faXshEuDtJA5vb_P_--RHWHCCro4fbLJQ3g4BZRdF1YQbYptqzLw7NNqtt2G8JDOyn-" - "eqXQX9ZtbUyz3UzU4BOEVlnsveO_t_3KPJTXYNrt3BX9liHMLfo2YWIFK4j-" - "jh4Ta9WBrN3QdZ8YplcvzazKgk63v0OGy0BphDneDNwV3-CSg23vFJxKCX1_" - "mwKkNF8MRtVZtgJ8j57d_L9e9W0bSaOyjQOD6qf__" - "MG366CJ7OCxbRdHxhptHmiA2nb571OmskGWuRxRi8hOfFJj0edXMyk9ijpWm0TmvYqN3As" - "f0LZ3tOtJ09g4DIFCibGfG_" - "6UU0K52CuOvFGbSo8vis0TR9yADi3Kv8LaDlOX9QhpX5IM9v2hcvHDVWt9NVBs0xN78Q4B" - "OI7bTg5QjAyyCzUKztgMU7M6rXCegJxQDMknAwbgi9Pk7SYDMamEvIA4IQMjEOiJ5vhu2G" - "T519NZfSFbzS3MLmS1BQf-BCWHHGet3nZq4uX8KdY4-" - "gcnccyx6tdXYcLdBBJzdydyDps0qOS4usplAThR6J6eKkBjMHAGSy6v_" - "rwH9CMqXW6Y58IJPeDXwPVQIFw5qtvjQQY6CP32lWXAgSS3fycyAZQptxP1fNXJqlmyKx4" - "XKCWOHgilU7muIEjNCO2UkRhUF5R8bEcOnPEQVsevALzeycNK4lWKZfmOznSHYej6HArF6" - "FVxPnr-EynH72N7VnHEAlPJXOj5B-NAxzFcHEGFuRG-" - "jVhc31IXE1zDbjfTp9KT8nudTbtT_" - "azUsY3MHKGsGprAb4ccpwrICoFqKMBEhQkrW2Dc65TLb2XBcwEcoirHp9xxDT-2_" - "QGMwXSiu0ApMVETx5SgSA0mS51jvE4zN4793loZM0tKubw5drEMfx7IwglfbZKsbOTjjTr" - "lE-omat6lQskeicaZEnGMRvH17ook9-TlKr_vlqp0EcaLE-" - "glTQ91JhvwxyouYoDx7L4Nr3y35PyEltKxP-Ru7sHtOTEBhJS7MtyLU3n1yJufT-" - "gK0az0fD46ZMcdqVhmuRdEDn2JPemFI3mOZRNnT34WTtmN4nwbQ237Cp3OWuETQ8ZHL5lX" - "LHwwlS0da7vonJ2zc1U1R2VEykHDkN9pOvbXnva8u4CvWy4PMC_" - "lTRAm3aYcYVzJ3bVJFs1LBU6TsgeHFz7K-Ko4llo6VqyVmw4GEGUbbRfLeCT4_" - "H721oJWbl2Qk_" - "u6qSuVSSHzkBRL1tcD3Efi8eXtQW0pmzsnPygsd3NHfRrJnRLk7lmt4eEG4QORtjyXGPsf" - "uczYhWWjNH3DI5LMV7gQd7GehKmbIe_" - "NRBSymdvgVawUnxqVKpce3bREefaCubIU0uyICdEXCXy3qMeS63k5ljTeCz3tbYKb2mHj2" - "qTKA7a2dYrmhlfx3LBTRDf8O_Q0bOcth49stoplzwDn5mYnwZWKaq5dV-" - "QI4Ucy7gDpRzLRzK44i79-freYty1bY6M7b_Nc4LH0hkghBeWj5SZc-o9D0z-" - "q0DrFI6ch_wOz3-JBxYF5-jsrzN49HL6WxD9C6-hrqArAPZkP6-" - "N5JIJ9npDqUjw1Sf5FSBXZ7zLuepxRasvwWh4TD2CJZ7p9sJuM8PexaOKXhHS7TYrymoFY" - "1tAkhFJky_L9ulzouNeEMt10qojIRMxNkUvCyAi0J_-7c5d3Ptr5npuFbLldY7hfo1_z_" - "HhCHEbmzomVxcXmaafVb08mhlLnAtS1sUKnMlrhU8UExZNX0zF5xXKqD9gPqTGqIoX1Zm6" - "OWGde5G6rdl8czYkyc3qdNPxU0CMxyd4tPfdWEcfcZZ_" - "y3pUyz345vhMnChNO8igujySLhufvsFbV96aNY3H2kYfhOJTPGjVQdbcFqB05U_6YEd7I-" - "_Gj0DItSsIWyx7oqkfmE7v5k4Y7ojF-" - "gtXxAnzLVLt6mZaWtn1C9xmqgsWyaoCVpdLkllGv1vCg-cPLggp1cTleTwlVCk9pVQo_" - "3tWlaInbEb4n2XtQjl_eQ92jUKcv8pVtjkcpxw-w5k2GphT_-ttv_30dmiN8eeWq_" - "cvkWCJePSTpypyoG_OTgvE4FgMcn62reuRul-QBKYo-yPSprB34CP_" - "hQ8c9vTolpXDFYvbldvCorr4A54Cs8HnKWeDP9lnpS7_vPyIcP7RMNvPYF2-" - "k2vUnpGfgsl86aUzsy7XeKLQ8IunxPSzu28QzPJW08nNO4EA91uFJWZ05VhSWsuQ3Dbc4F" - "qVtHrb3Fv2d5BSp11XZhZ8WzP-3fwFOiVFV", - q_encoded); - free(q_encoded); - - char *ret = curl_get(news); - free(news); - if (!ret) - continue; - - json_object *json_ret = json_tokener_parse(ret); - if (!json_ret) { - free(ret); - continue; - } - - json_object *json_results = json_object_object_get(json_ret, "results"); - json_object *json_result = json_object_array_get_idx(json_results, 0); - if (json_result) { - json_object_put(json_ret); - return ret; - } - json_object_put(json_ret); - free(ret); - } - return NULL; -} - char *web_search(char *q) { - const int MAX_RETRIES = 3; - for (int retry = 0; retry < MAX_RETRIES; retry++) { - char *news = malloc(4096); - if (!news) - return NULL; - news[0] = 0; - char *q_encoded = url_encode(q); - snprintf( - news, 4096, - "https://search.molodetz.nl/" - "search?q=%s&format=json&preferences=eJx1WMuy4zYO_" - "Zp4o4ormaRqahZepZL5gexVFAlLiEhCzYdt3a8PoIdFWt2Ldl8fkCCIxwForRL0FBDirQc" - "PQdmLVb7PqoebyokulrSycPP2Il81uclCgtvlrh6oybcBItkHhNsFHW9qp0Cv-" - "faXshEuDtJA5vb_P_--RHWHCCro4fbLJQ3g4BZRdF1YQbYptqzLw7NNqtt2G8JDOyn-" - "eqXQX9ZtbUyz3UzU4BOEVlnsveO_t_3KPJTXYNrt3BX9liHMLfo2YWIFK4j-" - "jh4Ta9WBrN3QdZ8YplcvzazKgk63v0OGy0BphDneDNwV3-CSg23vFJxKCX1_" - "mwKkNF8MRtVZtgJ8j57d_L9e9W0bSaOyjQOD6qf__" - "MG366CJ7OCxbRdHxhptHmiA2nb571OmskGWuRxRi8hOfFJj0edXMyk9ijpWm0TmvYqN3As" - "f0LZ3tOtJ09g4DIFCibGfG_" - "6UU0K52CuOvFGbSo8vis0TR9yADi3Kv8LaDlOX9QhpX5IM9v2hcvHDVWt9NVBs0xN78Q4B" - "OI7bTg5QjAyyCzUKztgMU7M6rXCegJxQDMknAwbgi9Pk7SYDMamEvIA4IQMjEOiJ5vhu2G" - "T519NZfSFbzS3MLmS1BQf-BCWHHGet3nZq4uX8KdY4-" - "gcnccyx6tdXYcLdBBJzdydyDps0qOS4usplAThR6J6eKkBjMHAGSy6v_" - "rwH9CMqXW6Y58IJPeDXwPVQIFw5qtvjQQY6CP32lWXAgSS3fycyAZQptxP1fNXJqlmyKx4" - "XKCWOHgilU7muIEjNCO2UkRhUF5R8bEcOnPEQVsevALzeycNK4lWKZfmOznSHYej6HArF6" - "FVxPnr-EynH72N7VnHEAlPJXOj5B-NAxzFcHEGFuRG-" - "jVhc31IXE1zDbjfTp9KT8nudTbtT_" - "azUsY3MHKGsGprAb4ccpwrICoFqKMBEhQkrW2Dc65TLb2XBcwEcoirHp9xxDT-2_" - "QGMwXSiu0ApMVETx5SgSA0mS51jvE4zN4793loZM0tKubw5drEMfx7IwglfbZKsbOTjjTr" - "lE-omat6lQskeicaZEnGMRvH17ook9-TlKr_vlqp0EcaLE-" - "glTQ91JhvwxyouYoDx7L4Nr3y35PyEltKxP-Ru7sHtOTEBhJS7MtyLU3n1yJufT-" - "gK0az0fD46ZMcdqVhmuRdEDn2JPemFI3mOZRNnT34WTtmN4nwbQ237Cp3OWuETQ8ZHL5lX" - "LHwwlS0da7vonJ2zc1U1R2VEykHDkN9pOvbXnva8u4CvWy4PMC_" - "lTRAm3aYcYVzJ3bVJFs1LBU6TsgeHFz7K-Ko4llo6VqyVmw4GEGUbbRfLeCT4_" - "H721oJWbl2Qk_" - "u6qSuVSSHzkBRL1tcD3Efi8eXtQW0pmzsnPygsd3NHfRrJnRLk7lmt4eEG4QORtjyXGPsf" - "uczYhWWjNH3DI5LMV7gQd7GehKmbIe_" - "NRBSymdvgVawUnxqVKpce3bREefaCubIU0uyICdEXCXy3qMeS63k5ljTeCz3tbYKb2mHj2" - "qTKA7a2dYrmhlfx3LBTRDf8O_Q0bOcth49stoplzwDn5mYnwZWKaq5dV-" - "QI4Ucy7gDpRzLRzK44i79-freYty1bY6M7b_Nc4LH0hkghBeWj5SZc-o9D0z-" - "q0DrFI6ch_wOz3-JBxYF5-jsrzN49HL6WxD9C6-hrqArAPZkP6-" - "N5JIJ9npDqUjw1Sf5FSBXZ7zLuepxRasvwWh4TD2CJZ7p9sJuM8PexaOKXhHS7TYrymoFY" - "1tAkhFJky_L9ulzouNeEMt10qojIRMxNkUvCyAi0J_-7c5d3Ptr5npuFbLldY7hfo1_z_" - "HhCHEbmzomVxcXmaafVb08mhlLnAtS1sUKnMlrhU8UExZNX0zF5xXKqD9gPqTGqIoX1Zm6" - "OWGde5G6rdl8czYkyc3qdNPxU0CMxyd4tPfdWEcfcZZ_" - "y3pUyz345vhMnChNO8igujySLhufvsFbV96aNY3H2kYfhOJTPGjVQdbcFqB05U_6YEd7I-" - "_Gj0DItSsIWyx7oqkfmE7v5k4Y7ojF-" - "gtXxAnzLVLt6mZaWtn1C9xmqgsWyaoCVpdLkllGv1vCg-cPLggp1cTleTwlVCk9pVQo_" - "3tWlaInbEb4n2XtQjl_eQ92jUKcv8pVtjkcpxw-w5k2GphT_-ttv_30dmiN8eeWq_" - "cvkWCJePSTpypyoG_OTgvE4FgMcn62reuRul-QBKYo-yPSprB34CP_" - "hQ8c9vTolpXDFYvbldvCorr4A54Cs8HnKWeDP9lnpS7_vPyIcP7RMNvPYF2-" - "k2vUnpGfgsl86aUzsy7XeKLQ8IunxPSzu28QzPJW08nNO4EA91uFJWZ05VhSWsuQ3Dbc4F" - "qVtHrb3Fv2d5BSp11XZhZ8WzP-3fwFOiVFV", - q_encoded); - free(q_encoded); - - char *ret = curl_get(news); - free(news); - if (!ret) - continue; - - json_object *json_ret = json_tokener_parse(ret); - if (!json_ret) { - free(ret); - continue; - } - - json_object *json_results = json_object_object_get(json_ret, "results"); - json_object *json_result = json_object_array_get_idx(json_results, 0); - if (json_result) { - json_object_put(json_ret); - return ret; - } - json_object_put(json_ret); - free(ret); + char url[4096]; + char *q_encoded = url_encode(q); + if (!q_encoded) { + return NULL; } - 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_engine(char *q) { - const int MAX_RETRIES = 3; - for (int retry = 0; retry < MAX_RETRIES; retry++) { - char *searx = malloc(4096); - if (!searx) - return NULL; - searx[0] = 0; - snprintf(searx, 4096, "https://searx.molodetz.nl/search?q=%s&format=json", - q); - char *ret = curl_get(searx); - free(searx); - if (!ret) - continue; - - json_object *json_ret = json_tokener_parse(ret); - if (!json_ret) { - free(ret); - continue; - } - - json_object *json_results = json_object_object_get(json_ret, "results"); - json_object *json_result = json_object_array_get_idx(json_results, 0); - if (json_result) { - json_object_put(json_ret); - return ret; - } - json_object_put(json_ret); - free(ret); - } - return NULL; +char *web_search_news(char *q) { + return web_search(q); } diff --git a/http_curl.h b/http_curl.h index f84cdaa..be2e4ee 100644 --- a/http_curl.h +++ b/http_curl.h @@ -33,6 +33,7 @@ #include #include #include +#include struct ResponseBuffer { char *data; @@ -61,18 +62,36 @@ static size_t WriteCallback(void *contents, size_t size, size_t nmemb, return total_size; } +#define HTTP_MAX_RETRIES 3 +#define HTTP_RETRY_DELAY_MS 2000 + char *curl_post(const char *url, const char *data) { CURL *curl; CURLcode res; - struct ResponseBuffer response = {malloc(1), 0}; + struct ResponseBuffer response = {NULL, 0}; + int retry_count = 0; - if (!response.data) - return NULL; + 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; + } - curl = curl_easy_init(); - if (curl) { 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", @@ -82,16 +101,26 @@ char *curl_post(const char *url, const char *data) { 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); - if (res != CURLE_OK) { - fprintf(stderr, "Url: %s\n", data); - fprintf(stderr, "Data: %s\n", data); - fprintf(stderr, "An error occurred: %s\n", curl_easy_strerror(res)); - } curl_slist_free_all(headers); curl_easy_cleanup(curl); - return response.data; + + 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; } @@ -99,15 +128,30 @@ char *curl_post(const char *url, const char *data) { char *curl_get(const char *url) { CURL *curl; CURLcode res; - struct ResponseBuffer response = {malloc(1), 0}; + struct ResponseBuffer response = {NULL, 0}; + int retry_count = 0; - if (!response.data) - return NULL; + 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; + } - curl = curl_easy_init(); - if (curl) { 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", @@ -116,17 +160,28 @@ char *curl_get(const char *url) { 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); - if (res != CURLE_OK) { - fprintf(stderr, "An error occurred: %s\n", curl_easy_strerror(res)); - } curl_slist_free_all(headers); curl_easy_cleanup(curl); - } else { - free(response.data); - return NULL; + + 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); + } } - return response.data; + + fprintf(stderr, "Failed after %d attempts.\n", HTTP_MAX_RETRIES); + free(response.data); + return NULL; } #endif \ No newline at end of file diff --git a/main.c b/main.c index 8b44b9f..ad663ad 100644 --- a/main.c +++ b/main.c @@ -114,6 +114,11 @@ static char *get_prompt_from_args(int argc, char **argv) { } 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) ? " " : "."); @@ -184,6 +189,17 @@ static void repl(void) { printf("%s\n", message_json()); 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", @@ -239,9 +255,15 @@ static void init(void) { 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); + snprintf( payload, sizeof(payload), "# AUTONOMOUS AGENT INSTRUCTIONS\n" + "Current date/time: %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" @@ -253,16 +275,24 @@ static void init(void) { "## 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" - "- Always verify results before concluding\n\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" - "## Completion\n" - "When the task is fully complete, provide a clear final response.\n" - "Do not stop mid-task. Continue using tools until the goal is achieved.\n", - schema); + "## 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, schema); free(schema); fprintf(stderr, "Loading..."); @@ -297,15 +327,46 @@ static void handle_sigint(int sig) { } } +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; diff --git a/messages.h b/messages.h index 7037758..ac05484 100755 --- a/messages.h +++ b/messages.h @@ -28,8 +28,129 @@ #include "db_utils.h" #include "json-c/json.h" #include "tools.h" +#include "utils.h" #include +#include + 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) { @@ -42,19 +163,25 @@ bool messages_remove_last() { 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() { - while (messages_remove_last()) - continue; + 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; } @@ -77,12 +204,14 @@ struct json_object *message_add_tool_result(const char *tool_call_id, 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); @@ -110,6 +239,9 @@ struct json_object *message_add(const char *role, const char *content) { } json_object_array_add(messages, message); + if (strcmp(role, "system") != 0) { + messages_save(); + } return message; } diff --git a/r.h b/r.h index 1ffe72a..6d01732 100644 --- a/r.h +++ b/r.h @@ -58,6 +58,11 @@ char *get_env_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"); diff --git a/tools.h b/tools.h index 575e40a..614b110 100644 --- a/tools.h +++ b/tools.h @@ -232,9 +232,9 @@ char *tool_function_web_search(char *query) { return strdup("Query cannot be NULL."); } - char *result = web_search_news(query); + char *result = web_search(query); if (result == NULL) { - return strdup("Failed to fetch news."); + return strdup("Failed to search."); } return result;