TASK: Ensure all .c and .h files in the src directory start with the comment '// retoor '. If missing, add it. ---------------------------------------- Loading... -> Listing: src/*.c -> Listing: src/*.h I will now process all .c and .h files in the src directory to ensure they start with the comment // retoor . If the comment is missing, I will add it at the beginning of each file. -> Replacing lines in: src/agent.c   CHANGES: src/agent.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "agent.h" | 2 #include "agent.h" 4 #include "http_client.h" | 3 #include "http_client.h" 5 #include "db.h" | 4 #include "db.h" 6 #include "r_config.h" | 5 #include "r_config.h" 7 #include "tool.h" | 6 #include "tool.h" 8 #include "context_manager.h" | 7 #include "context_manager.h" 9 #include "markdown.h" | 8 #include "markdown.h" 10 #include | 9 #include 11 #include | 10 #include 12 #include | 11 #include 13 #include | 12 #include 14 #include | 13 #include 15 #include | 14 #include 16 -   | 17 struct agent_t { | 15 struct agent_t { 18 char *agent_id; | 16 char *agent_id; 19 char *role; | 17 char *role; 20 char *manager_id; | 18 char *manager_id; 21 char *department; | 19 char *department; 22 long budget_limit; | 20 long budget_limit; 23 long used_tokens; | 21 long used_tokens; 24 char *goal; | 22 char *goal; 25 int iteration_count; | 23 int iteration_count; 26 int max_iterations; | 24 int max_iterations; 27 int tool_retry_count; | 25 int tool_retry_count; 28 int max_tool_retries; | 26 int max_tool_retries; 29 agent_state_t state; | 27 agent_state_t state; 30 time_t start_time; | 28 time_t start_time; 31 char *last_error; | 29 char *last_error; 32 bool verbose; | 30 bool verbose; 33 bool is_subagent; | 31 bool is_subagent; 34 messages_handle messages; | 32 messages_handle messages; 35 bool owns_messages; | 33 bool owns_messages; 36 http_client_handle http; | 34 http_client_handle http; 37 tool_registry_t *tools; | 35 tool_registry_t *tools; 38 }; | 36 }; 39 -   | 40 static const char *incomplete_phrases[] = { | 37 static const char *incomplete_phrases[] = { 41 "I'll ", "I will ", "Let me ", "I'm going to ", | 38 "I'll ", "I will ", "Let me ", "I'm going to ", 42 "Next, I", "Now I'll", "Now I will", "I'll now", | 39 "Next, I", "Now I'll", "Now I will", "I'll now", 43 "I need to", "I should", "I can ", "Going to ", | 40 "I need to", "I should", "I can ", "Going to ", 44 "Will now", "Proceeding", "Starting to", "About to ", | 41 "Will now", "Proceeding", "Starting to", "About to ", 45 "First, I", "Then I", "After that", "Following that", | 42 "First, I", "Then I", "After that", "Following that", 46 "Now let me", "Let's check", "Let me check", | 43 "Now let me", "Let's check", "Let me check", 47 "Would you like", "Should I", "I can also", "I could", | 44 "Would you like", "Should I", "I can also", "I could", 48 "Do you want", "Shall I", | 45 "Do you want", "Shall I", 49 NULL | 46 NULL 50 }; | 47 }; 51 -   | 52 static const char *incomplete_endings[] = { | 48 static const char *incomplete_endings[] = { 53 "...", ":", "files:", "content:", "implementation:", "?", | 49 "...", ":", "files:", "content:", "implementation:", "?", 54 NULL | 50 NULL 55 }; | 51 }; 56 -   | 57 static const char *completion_phrases[] = { | 52 static const char *completion_phrases[] = { 58 "task is complete", "task complete", "tasks complete", | 53 "task is complete", "task complete", "tasks complete", 59 "goal is achieved", "goal achieved", | 54 "goal is achieved", "goal achieved", 60 "all steps completed", "all steps done", | 55 "all steps completed", "all steps done", 61 "fully completed", "is now complete", | 56 "fully completed", "is now complete", 62 "has been completed", "have been completed", | 57 "has been completed", "have been completed", 63 "successfully created", "successfully written", | 58 "successfully created", "successfully written", 64 "setup is complete", "is ready to use", | 59 "setup is complete", "is ready to use", 65 NULL | 60 NULL 66 }; | 61 }; 67 -   | 68 static const char *passive_phrases[] = { | 62 static const char *passive_phrases[] = { 69 "let me know", "feel free", "if you need", "awaiting", | 63 "let me know", "feel free", "if you need", "awaiting", 70 "ready for", "standby", "standing by", "happy to help", | 64 "ready for", "standby", "standing by", "happy to help", 71 "do not hesitate", "anything else", | 65 "do not hesitate", "anything else", 72 NULL | 66 NULL 73 }; | 67 }; 74 -   | 75 extern tool_registry_t *tools_get_registry(void); | 68 extern tool_registry_t *tools_get_registry(void); 76 -   | 77 static void agent_update_heartbeat(agent_handle agent) { | 69 static void agent_update_heartbeat(agent_handle agent) { 78 if (!agent || !agent->agent_id) return; | 70 if (!agent || !agent->agent_id) return; 79 db_handle db = db_open(NULL); | 71 db_handle db = db_open(NULL); 80 char *sql = sqlite3_mprintf("UPDATE agents SET last_heartbeat = CURRENT_TIMESTAMP WHERE agent_id = %Q", agent->agent_id); | 72 char *sql = sqlite3_mprintf("UPDATE agents SET last_heartbeat = CURRENT_TIMESTAMP WHERE agent_id = %Q", agent->agent_id); 81 struct json_object *res = NULL; | 73 struct json_object *res = NULL; 82 db_execute(db, sql, &res); | 74 db_execute(db, sql, &res); 83 sqlite3_free(sql); | 75 sqlite3_free(sql); 84 if (res) json_object_put(res); | 76 if (res) json_object_put(res); 85 db_close(db); | 77 db_close(db); 86 } | 78 } 87 -   | 88 static bool agent_check_budget(agent_handle agent) { | 79 static bool agent_check_budget(agent_handle agent) { 89 if (!agent || agent->budget_limit <= 0) return true; | 80 if (!agent || agent->budget_limit <= 0) return true; 90 return agent->used_tokens < agent->budget_limit; | 81 return agent->used_tokens < agent->budget_limit; 91 } | 82 } 92 -   | 93 static void agent_add_tokens(agent_handle agent, long tokens) { | 83 static void agent_add_tokens(agent_handle agent, long tokens) { 94 if (!agent || !agent->agent_id) return; | 84 if (!agent || !agent->agent_id) return; 95 agent->used_tokens += tokens; | 85 agent->used_tokens += tokens; 96 db_handle db = db_open(NULL); | 86 db_handle db = db_open(NULL); 97 char *sql = sqlite3_mprintf("UPDATE agents SET used_tokens = used_tokens + %ld WHERE agent_id = %Q", tokens, agent->agent_id); | 87 char *sql = sqlite3_mprintf("UPDATE agents SET used_tokens = used_tokens + %ld WHERE agent_id = %Q", tokens, agent->agent_id); 98 struct json_object *res = NULL; | 88 struct json_object *res = NULL; 99 db_execute(db, sql, &res); | 89 db_execute(db, sql, &res); 100 sqlite3_free(sql); | 90 sqlite3_free(sql); 101 if (res) json_object_put(res); | 91 if (res) json_object_put(res); 102 db_close(db); | 92 db_close(db); 103 } | 93 } 104 -   | 105 static void agent_set_error(agent_handle agent, const char *error) { | 94 static void agent_set_error(agent_handle agent, const char *error) { 106 if (!agent) return; | 95 if (!agent) return; 107 free(agent->last_error); | 96 free(agent->last_error); 108 agent->last_error = error ? strdup(error) : NULL; | 97 agent->last_error = error ? strdup(error) : NULL; 109 } | 98 } 110 -   | 111 static char *agent_build_request(agent_handle agent, const char *role, const char *message) { | 99 static char *agent_build_request(agent_handle agent, const char *role, const char *message) { 112 r_config_handle cfg = r_config_get_instance(); | 100 r_config_handle cfg = r_config_get_instance(); 113 -   | 114 struct json_object *root = json_object_new_object(); | 101 struct json_object *root = json_object_new_object(); 115 if (!root) return NULL; | 102 if (!root) return NULL; 116 -   | 117 json_object_object_add(root, "model", | 103 json_object_object_add(root, "model", 118 json_object_new_string(r_config_get_model(cfg))); | 104 json_object_new_string(r_config_get_model(cfg))); 119 -   | 120 if (role && message) { | 105 if (role && message) { 121 messages_add(agent->messages, role, message); | 106 messages_add(agent->messages, role, message); 122 } | 107 } 123 | 108 124 if (r_config_use_tools(cfg) && agent->tools) { | 109 if (r_config_use_tools(cfg) && agent->tools) { 125 json_object_object_add(root, "tools", | 110 json_object_object_add(root, "tools", 126 tool_registry_get_descriptions(agent->tools)); | 111 tool_registry_get_descriptions(agent->tools)); 127 } | 112 } 128 -   | 129 json_object_object_add(root, "messages", | 113 json_object_object_add(root, "messages", 130 json_object_get(messages_to_json(agent->messages))); | 114 json_object_get(messages_to_json(agent->messages))); 131 json_object_object_add(root, "temperature", | 115 json_object_object_add(root, "temperature", 132 json_object_new_double(r_config_get_temperature(cfg))); | 116 json_object_new_double(r_config_get_temperature(cfg))); 133 json_object_object_add(root, "max_tokens", | 117 json_object_object_add(root, "max_tokens", 134 json_object_new_int(r_config_get_max_tokens(cfg))); | 118 json_object_new_int(r_config_get_max_tokens(cfg))); 135 -   | 136 char *result = strdup(json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY)); | 119 char *result = strdup(json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY)); 137 if (agent->verbose && !agent->is_subagent) { | 120 if (agent->verbose && !agent->is_subagent) { 138 fprintf(stderr, "\n[LLM Request]\n%s\n", result); | 121 fprintf(stderr, "\n[LLM Request]\n%s\n", result); 139 } | 122 } 140 json_object_put(root); | 123 json_object_put(root); 141 return result; | 124 return result; 142 } | 125 } 143 -   | 144 static struct json_object *agent_process_response(agent_handle agent, const char *json_data) { | 126 static struct json_object *agent_process_response(agent_handle agent, const char *json_data) { 145 r_config_handle cfg = r_config_get_instance(); | 127 r_config_handle cfg = r_config_get_instance(); 146 -   | 147 char *response = NULL; | 128 char *response = NULL; 148 r_status_t status = http_post(agent->http, r_config_get_api_url(cfg), json_data, &response); | 129 r_status_t status = http_post(agent->http, r_config_get_api_url(cfg), json_data, &response); 149 -   | 150 agent_update_heartbeat(agent); | 130 agent_update_heartbeat(agent); 151 -   | 152 if (status != R_SUCCESS || !response) { | 131 if (status != R_SUCCESS || !response) { 153 return NULL; | 132 return NULL; 154 } | 133 } 155 -   | 156 struct json_object *parsed = json_tokener_parse(response); | 134 struct json_object *parsed = json_tokener_parse(response); 157 | 135 158 // Track tokens | 136 // Track tokens 159 struct json_object *usage; | 137 struct json_object *usage; 160 if (parsed && json_object_object_get_ex(parsed, "usage", &usage)) { | 138 if (parsed && json_object_object_get_ex(parsed, "usage", &usage)) { 161 struct json_object *total_tokens; | 139 struct json_object *total_tokens; 162 if (json_object_object_get_ex(usage, "total_tokens", &total_tokens)) { | 140 if (json_object_object_get_ex(usage, "total_tokens", &total_tokens)) { 163 agent_add_tokens(agent, json_object_get_int64(total_tokens)); | 141 agent_add_tokens(agent, json_object_get_int64(total_tokens)); 164 } | 142 } 165 } | 143 } 166 -   | 167 free(response); | 144 free(response); 168 -   | 169 if (!parsed) return NULL; | 145 if (!parsed) return NULL; 170 -   | 171 struct json_object *error_obj; | 146 struct json_object *error_obj; 172 if (json_object_object_get_ex(parsed, "error", &error_obj)) { | 147 if (json_object_object_get_ex(parsed, "error", &error_obj)) { 173 const char *err_str = json_object_to_json_string(error_obj); | 148 const char *err_str = json_object_to_json_string(error_obj); 174 | 149 175 // Smart error detection for context overflow | 150 // Smart error detection for context overflow 176 if (strcasestr(err_str, "too long") || | 151 if (strcasestr(err_str, "too long") || 177 strcasestr(err_str, "context_length_exceeded") || | 152 strcasestr(err_str, "context_length_exceeded") || 178 strcasestr(err_str, "maximum context length") || | 153 strcasestr(err_str, "maximum context length") || 179 strcasestr(err_str, "reduce the length") || | 154 strcasestr(err_str, "reduce the length") || 180 strcasestr(err_str, "context limit") || | 155 strcasestr(err_str, "context limit") || 181 strcasestr(err_str, "Input is too long")) { | 156 strcasestr(err_str, "Input is too long")) { 182 agent_set_error(agent, "CONTEXT_OVERFLOW"); | 157 agent_set_error(agent, "CONTEXT_OVERFLOW"); 183 } else { | 158 } else { 184 fprintf(stderr, "API Error: %s\n", err_str); | 159 fprintf(stderr, "API Error: %s\n", err_str); 185 } | 160 } 186 | 161 187 json_object_put(parsed); | 162 json_object_put(parsed); 188 return NULL; | 163 return NULL; 189 } | 164 } 190 -   | 191 struct json_object *choices; | 165 struct json_object *choices; 192 if (!json_object_object_get_ex(parsed, "choices", &choices)) { | 166 if (!json_object_object_get_ex(parsed, "choices", &choices)) { 193 json_object_put(parsed); | 167 json_object_put(parsed); 194 return NULL; | 168 return NULL; 195 } | 169 } 196 -   | 197 struct json_object *first_choice = json_object_array_get_idx(choices, 0); | 170 struct json_object *first_choice = json_object_array_get_idx(choices, 0); 198 if (!first_choice) { | 171 if (!first_choice) { 199 json_object_put(parsed); | 172 json_object_put(parsed); 200 return NULL; | 173 return NULL; 201 } | 174 } 202 -   | 203 return first_choice; | 175 return first_choice; 204 } | 176 } 205 -   | 206 static bool agent_has_tool_calls(struct json_object *choice) { | 177 static bool agent_has_tool_calls(struct json_object *choice) { 207 struct json_object *message_obj; | 178 struct json_object *message_obj; 208 if (!json_object_object_get_ex(choice, "message", &message_obj)) return false; | 179 if (!json_object_object_get_ex(choice, "message", &message_obj)) return false; 209 -   | 210 struct json_object *tool_calls; | 180 struct json_object *tool_calls; 211 if (!json_object_object_get_ex(message_obj, "tool_calls", &tool_calls)) return false; | 181 if (!json_object_object_get_ex(message_obj, "tool_calls", &tool_calls)) return false; 212 -   | 213 return json_object_array_length(tool_calls) > 0; | 182 return json_object_array_length(tool_calls) > 0; 214 } | 183 } 215 -   | 216 static struct json_object *agent_get_tool_calls(struct json_object *choice) { | 184 static struct json_object *agent_get_tool_calls(struct json_object *choice) { 217 struct json_object *message_obj; | 185 struct json_object *message_obj; 218 if (!json_object_object_get_ex(choice, "message", &message_obj)) return NULL; | 186 if (!json_object_object_get_ex(choice, "message", &message_obj)) return NULL; 219 -   | 220 struct json_object *tool_calls; | 187 struct json_object *tool_calls; 221 if (!json_object_object_get_ex(message_obj, "tool_calls", &tool_calls)) return NULL; | 188 if (!json_object_object_get_ex(message_obj, "tool_calls", &tool_calls)) return NULL; 222 -   | 223 return tool_calls; | 189 return tool_calls; 224 } | 190 } 225 -   | 226 static struct json_object *agent_get_message(struct json_object *choice) { | 191 static struct json_object *agent_get_message(struct json_object *choice) { 227 struct json_object *message_obj; | 192 struct json_object *message_obj; 228 if (json_object_object_get_ex(choice, "message", &message_obj)) { | 193 if (json_object_object_get_ex(choice, "message", &message_obj)) { 229 return message_obj; | 194 return message_obj; 230 } | 195 } 231 return NULL; | 196 return NULL; 232 } | 197 } 233 -   | 234 static char *agent_get_content(struct json_object *choice) { | 198 static char *agent_get_content(struct json_object *choice) { 235 struct json_object *message_obj; | 199 struct json_object *message_obj; 236 if (!json_object_object_get_ex(choice, "message", &message_obj)) return NULL; | 200 if (!json_object_object_get_ex(choice, "message", &message_obj)) return NULL; 237 -   | 238 struct json_object *content_obj; | 201 struct json_object *content_obj; 239 if (!json_object_object_get_ex(message_obj, "content", &content_obj)) return NULL; | 202 if (!json_object_object_get_ex(message_obj, "content", &content_obj)) return NULL; 240 -   | 241 const char *content = json_object_get_string(content_obj); | 203 const char *content = json_object_get_string(content_obj); 242 return content ? strdup(content) : NULL; | 204 return content ? strdup(content) : NULL; 243 } | 205 } 244 -   | 245 static bool agent_response_indicates_incomplete(const char *content) { | 206 static bool agent_response_indicates_incomplete(const char *content) { 246 if (!content) return false; | 207 if (!content) return false; 247 -   | 248 // Check for explicit completion phrases first (Overrides incomplete indicators) | 208 // Check for explicit completion phrases first (Overrides incomplete indicators) 249 for (int i = 0; completion_phrases[i]; i++) { | 209 for (int i = 0; completion_phrases[i]; i++) { 250 if (strcasestr(content, completion_phrases[i])) return false; | 210 if (strcasestr(content, completion_phrases[i])) return false; 251 } | 211 } 252 -   | 253 // Check for passive/closing phrases (Overrides incomplete indicators) | 212 // Check for passive/closing phrases (Overrides incomplete indicators) 254 for (int i = 0; passive_phrases[i]; i++) { | 213 for (int i = 0; passive_phrases[i]; i++) { 255 if (strcasestr(content, passive_phrases[i])) return false; | 214 if (strcasestr(content, passive_phrases[i])) return false; 256 } | 215 } 257 -   | 258 for (int i = 0; incomplete_phrases[i]; i++) { | 216 for (int i = 0; incomplete_phrases[i]; i++) { 259 if (strcasestr(content, incomplete_phrases[i])) return true; | 217 if (strcasestr(content, incomplete_phrases[i])) return true; 260 } | 218 } 261 -   | 262 size_t len = strlen(content); | 219 size_t len = strlen(content); 263 if (len > 3) { | 220 if (len > 3) { 264 for (int i = 0; incomplete_endings[i]; i++) { | 221 for (int i = 0; incomplete_endings[i]; i++) { 265 size_t end_len = strlen(incomplete_endings[i]); | 222 size_t end_len = strlen(incomplete_endings[i]); 266 if (len >= end_len && strcmp(content + len - end_len, incomplete_endings[i]) == 0) { | 223 if (len >= end_len && strcmp(content + len - end_len, incomplete_endings[i]) == 0) { 267 return true; | 224 return true; 268 } | 225 } 269 } | 226 } 270 } | 227 } 271 -   | 272 return false; | 228 return false; 273 } | 229 } 274 -   | 275 agent_handle agent_create(const char *goal, messages_handle messages) { | 230 agent_handle agent_create(const char *goal, messages_handle messages) { 276 struct agent_t *agent = calloc(1, sizeof(struct agent_t)); | 231 struct agent_t *agent = calloc(1, sizeof(struct agent_t)); 277 if (!agent) return NULL; | 232 if (!agent) return NULL; 278 -   | 279 if (goal) { | 233 if (goal) { 280 agent->goal = strdup(goal); | 234 agent->goal = strdup(goal); 281 if (!agent->goal) { | 235 if (!agent->goal) { 282 free(agent); | 236 free(agent); 283 return NULL; | 237 return NULL; 284 } | 238 } 285 } | 239 } 286 -   | 287 r_config_handle cfg = r_config_get_instance(); | 240 r_config_handle cfg = r_config_get_instance(); 288 -   | 289 agent->iteration_count = 0; | 241 agent->iteration_count = 0; 290 agent->max_iterations = AGENT_MAX_ITERATIONS; | 242 agent->max_iterations = AGENT_MAX_ITERATIONS; 291 agent->tool_retry_count = 0; | 243 agent->tool_retry_count = 0; 292 agent->max_tool_retries = AGENT_MAX_TOOL_RETRIES; | 244 agent->max_tool_retries = AGENT_MAX_TOOL_RETRIES; 293 agent->state = AGENT_STATE_IDLE; | 245 agent->state = AGENT_STATE_IDLE; 294 agent->start_time = time(NULL); | 246 agent->start_time = time(NULL); 295 agent->verbose = r_config_is_verbose(cfg); | 247 agent->verbose = r_config_is_verbose(cfg); 296 -   | 297 agent->agent_id = strdup("Executive-Apex"); | 248 agent->agent_id = strdup("Executive-Apex"); 298 agent->role = strdup("Executive"); | 249 agent->role = strdup("Executive"); 299 agent->budget_limit = 1000000; | 250 agent->budget_limit = 1000000; 300 -   | 301 db_handle db = db_open(NULL); | 251 db_handle db = db_open(NULL); 302 char *sql = sqlite3_mprintf("INSERT OR IGNORE INTO agents (agent_id, role, budget_limit_tokens) VALUES (%Q, %Q, %ld)", | 252 char *sql = sqlite3_mprintf("INSERT OR IGNORE INTO agents (agent_id, role, budget_limit_tokens) VALUES (%Q, %Q, %ld)", 303 agent->agent_id, agent->role, agent->budget_limit); | 253 agent->agent_id, agent->role, agent->budget_limit); 304 struct json_object *res = NULL; | 254 struct json_object *res = NULL; 305 db_execute(db, sql, &res); | 255 db_execute(db, sql, &res); 306 sqlite3_free(sql); | 256 sqlite3_free(sql); 307 if (res) json_object_put(res); | 257 if (res) json_object_put(res); 308 db_close(db); | 258 db_close(db); 309 -   | 310 if (messages) { | 259 if (messages) { 311 agent->messages = messages; | 260 agent->messages = messages; 312 agent->owns_messages = false; | 261 agent->owns_messages = false; 313 } else { | 262 } else { 314 agent->messages = messages_create(r_config_get_session_id(cfg)); | 263 agent->messages = messages_create(r_config_get_session_id(cfg)); 315 agent->owns_messages = true; | 264 agent->owns_messages = true; 316 } | 265 } 317 -   | 318 if (!agent->messages) { | 266 if (!agent->messages) { 319 free(agent->goal); | 267 free(agent->goal); 320 free(agent); | 268 free(agent); 321 return NULL; | 269 return NULL; 322 } | 270 } 323 -   | 324 const char *system_msg = r_config_get_system_message(cfg); | 271 const char *system_msg = r_config_get_system_message(cfg); 325 if (system_msg && *system_msg) { | 272 if (system_msg && *system_msg) { 326 bool has_system = false; | 273 bool has_system = false; 327 for (int i = 0; i < messages_count(agent->messages); i++) { | 274 for (int i = 0; i < messages_count(agent->messages); i++) { 328 struct json_object *msg = messages_get_object(agent->messages, i); | 275 struct json_object *msg = messages_get_object(agent->messages, i); 329 struct json_object *role; | 276 struct json_object *role; 330 if (json_object_object_get_ex(msg, "role", &role)) { | 277 if (json_object_object_get_ex(msg, "role", &role)) { 331 const char *role_str = json_object_get_string(role); | 278 const char *role_str = json_object_get_string(role); 332 if (role_str && strcmp(role_str, "system") == 0) { | 279 if (role_str && strcmp(role_str, "system") == 0) { 333 has_system = true; | 280 has_system = true; 334 break; | 281 break; 335 } | 282 } 336 } | 283 } 337 } | 284 } 338 if (!has_system) { | 285 if (!has_system) { 339 messages_add(agent->messages, "system", system_msg); | 286 messages_add(agent->messages, "system", system_msg); 340 } | 287 } 341 } | 288 } 342 -   | 343 agent->http = http_client_create(r_config_get_api_key(cfg)); | 289 agent->http = http_client_create(r_config_get_api_key(cfg)); 344 if (!agent->http) { | 290 if (!agent->http) { 345 if (agent->owns_messages) { | 291 if (agent->owns_messages) { 346 messages_destroy(agent->messages); | 292 messages_destroy(agent->messages); 347 } | 293 } 348 free(agent->goal); | 294 free(agent->goal); 349 free(agent); | 295 free(agent); 350 return NULL; | 296 return NULL; 351 } | 297 } 352 -   | 353 agent->tools = tools_get_registry(); | 298 agent->tools = tools_get_registry(); 354 -   | 355 return agent; | 299 return agent; 356 } | 300 } 357 -   | 358 void agent_destroy(agent_handle agent) { | 301 void agent_destroy(agent_handle agent) { 359 if (!agent) return; | 302 if (!agent) return; 360 if (agent->http) http_client_destroy(agent->http); | 303 if (agent->http) http_client_destroy(agent->http); 361 if (agent->messages && agent->owns_messages) messages_destroy(agent->messages); | 304 if (agent->messages && agent->owns_messages) messages_destroy(agent->messages); 362 free(agent->agent_id); | 305 free(agent->agent_id); 363 free(agent->role); | 306 free(agent->role); 364 free(agent->manager_id); | 307 free(agent->manager_id); 365 free(agent->department); | 308 free(agent->department); 366 free(agent->goal); | 309 free(agent->goal); 367 free(agent->last_error); | 310 free(agent->last_error); 368 free(agent); | 311 free(agent); 369 } | 312 } 370 -   | 371 void agent_set_max_iterations(agent_handle agent, int max) { | 313 void agent_set_max_iterations(agent_handle agent, int max) { 372 if (agent) agent->max_iterations = max; | 314 if (agent) agent->max_iterations = max; 373 } | 315 } 374 -   | 375 void agent_set_verbose(agent_handle agent, bool verbose) { | 316 void agent_set_verbose(agent_handle agent, bool verbose) { 376 if (agent) agent->verbose = verbose; | 317 if (agent) agent->verbose = verbose; 377 } | 318 } 378 -   | 379 void agent_set_is_subagent(agent_handle agent, bool is_subagent) { | 319 void agent_set_is_subagent(agent_handle agent, bool is_subagent) { 380 if (agent) agent->is_subagent = is_subagent; | 320 if (agent) agent->is_subagent = is_subagent; 381 } | 321 } 382 -   | 383 void agent_set_tool_registry(agent_handle agent, tool_registry_t *registry) { | 322 void agent_set_tool_registry(agent_handle agent, tool_registry_t *registry) { 384 if (agent && registry) agent->tools = registry; | 323 if (agent && registry) agent->tools = registry; 385 } | 324 } 386 -   | 387 agent_state_t agent_get_state(agent_handle agent) { | 325 agent_state_t agent_get_state(agent_handle agent) { 388 return agent ? agent->state : AGENT_STATE_ERROR; | 326 return agent ? agent->state : AGENT_STATE_ERROR; 389 } | 327 } 390 -   | 391 const char *agent_get_error(agent_handle agent) { | 328 const char *agent_get_error(agent_handle agent) { 392 return agent ? agent->last_error : NULL; | 329 return agent ? agent->last_error : NULL; 393 } | 330 } 394 -   | 395 int agent_get_iteration_count(agent_handle agent) { | 331 int agent_get_iteration_count(agent_handle agent) { 396 return agent ? agent->iteration_count : 0; | 332 return agent ? agent->iteration_count : 0; 397 } | 333 } 398 -   | 399 void agent_set_id(agent_handle agent, const char *id) { | 334 void agent_set_id(agent_handle agent, const char *id) { 400 if (!agent) return; | 335 if (!agent) return; 401 free(agent->agent_id); | 336 free(agent->agent_id); 402 agent->agent_id = id ? strdup(id) : NULL; | 337 agent->agent_id = id ? strdup(id) : NULL; 403 } | 338 } 404 -   | 405 void agent_set_role(agent_handle agent, const char *role) { | 339 void agent_set_role(agent_handle agent, const char *role) { 406 if (!agent) return; | 340 if (!agent) return; 407 free(agent->role); | 341 free(agent->role); 408 agent->role = role ? strdup(role) : NULL; | 342 agent->role = role ? strdup(role) : NULL; 409 } | 343 } 410 -   | 411 void agent_set_manager_id(agent_handle agent, const char *manager_id) { | 344 void agent_set_manager_id(agent_handle agent, const char *manager_id) { 412 if (!agent) return; | 345 if (!agent) return; 413 free(agent->manager_id); | 346 free(agent->manager_id); 414 agent->manager_id = manager_id ? strdup(manager_id) : NULL; | 347 agent->manager_id = manager_id ? strdup(manager_id) : NULL; 415 } | 348 } 416 -   | 417 const char *agent_get_id(agent_handle agent) { | 349 const char *agent_get_id(agent_handle agent) { 418 return agent ? agent->agent_id : NULL; | 350 return agent ? agent->agent_id : NULL; 419 } | 351 } 420 -   | 421 const char *agent_get_role(agent_handle agent) { | 352 const char *agent_get_role(agent_handle agent) { 422 return agent ? agent->role : NULL; | 353 return agent ? agent->role : NULL; 423 } | 354 } 424 -   | 425 const char *agent_get_manager_id(agent_handle agent) { | 355 const char *agent_get_manager_id(agent_handle agent) { 426 return agent ? agent->manager_id : NULL; | 356 return agent ? agent->manager_id : NULL; 427 } | 357 } 428 -   | 429 char *agent_run(agent_handle agent, const char *user_message) { | 358 char *agent_run(agent_handle agent, const char *user_message) { 430 if (!agent) return NULL; | 359 if (!agent) return NULL; 431 -   | 432 agent->state = AGENT_STATE_RUNNING; | 360 agent->state = AGENT_STATE_RUNNING; 433 agent->iteration_count = 0; | 361 agent->iteration_count = 0; 434 agent->tool_retry_count = 0; | 362 agent->tool_retry_count = 0; 435 -   | 436 if (!user_message || !*user_message) { | 363 if (!user_message || !*user_message) { 437 agent->state = AGENT_STATE_ERROR; | 364 agent->state = AGENT_STATE_ERROR; 438 agent_set_error(agent, "Empty user message"); | 365 agent_set_error(agent, "Empty user message"); 439 return NULL; | 366 return NULL; 440 } | 367 } 441 -   | 442 messages_load(agent->messages); | 368 messages_load(agent->messages); 443 -   | 444 char *json_data = agent_build_request(agent, "user", user_message); | 369 char *json_data = agent_build_request(agent, "user", user_message); 445 if (!json_data) { | 370 if (!json_data) { 446 agent->state = AGENT_STATE_ERROR; | 371 agent->state = AGENT_STATE_ERROR; 447 agent_set_error(agent, "Failed to create chat JSON"); | 372 agent_set_error(agent, "Failed to create chat JSON"); 448 return NULL; | 373 return NULL; 449 } | 374 } 450 -   | 451 char *accumulated_response = NULL; | 375 char *accumulated_response = NULL; 452 size_t accumulated_len = 0; | 376 size_t accumulated_len = 0; 453 -   | 454 while (agent->state == AGENT_STATE_RUNNING || agent->state == AGENT_STATE_EXECUTING_TOOLS) { | 377 while (agent->state == AGENT_STATE_RUNNING || agent->state == AGENT_STATE_EXECUTING_TOOLS) { 455 agent->iteration_count++; | 378 agent->iteration_count++; 456 -   | 457 if (!agent_check_budget(agent)) { | 379 if (!agent_check_budget(agent)) { 458 agent->state = AGENT_STATE_ERROR; | 380 agent->state = AGENT_STATE_ERROR; 459 agent_set_error(agent, "QUARTERLY_BUDGET_EXCEEDED"); | 381 agent_set_error(agent, "QUARTERLY_BUDGET_EXCEEDED"); 460 if (agent->verbose) fprintf(stderr, "\033[1;31m[Middleware] Process killed: Token budget exceeded.\033[0m\n"); | 382 if (agent->verbose) fprintf(stderr, "\033[1;31m[Middleware] Process killed: Token budget exceeded.\033[0m\n"); 461 break; | 383 break; 462 } | 384 } 463 -   | 464 if (agent->iteration_count > agent->max_iterations) { | 385 if (agent->iteration_count > agent->max_iterations) { 465 agent->state = AGENT_STATE_MAX_ITERATIONS; | 386 agent->state = AGENT_STATE_MAX_ITERATIONS; 466 agent_set_error(agent, "Maximum iterations reached"); | 387 agent_set_error(agent, "Maximum iterations reached"); 467 if (agent->verbose && !agent->is_subagent) { | 388 if (agent->verbose && !agent->is_subagent) { 468 fprintf(stderr, "[Agent] Max iterations (%d) reached\n", agent->max_iterations); | 389 fprintf(stderr, "[Agent] Max iterations (%d) reached\n", agent->max_iterations); 469 } | 390 } 470 free(json_data); | 391 free(json_data); 471 break; | 392 break; 472 } | 393 } 473 -   | 474 if (agent->verbose && !agent->is_subagent) { | 394 if (agent->verbose && !agent->is_subagent) { 475 fprintf(stderr, "[Agent] Iteration %d/%d\n", | 395 fprintf(stderr, "[Agent] Iteration %d/%d\n", 476 agent->iteration_count, agent->max_iterations); | 396 agent->iteration_count, agent->max_iterations); 477 } | 397 } 478 -   | 479 struct json_object *choice = agent_process_response(agent, json_data); | 398 struct json_object *choice = agent_process_response(agent, json_data); 480 | 399 481 if (!choice && agent->last_error && strcmp(agent->last_error, "CONTEXT_OVERFLOW") == 0) { | 400 if (!choice && agent->last_error && strcmp(agent->last_error, "CONTEXT_OVERFLOW") == 0) { 482 if (context_manager_shrink(agent->messages) == R_SUCCESS) { | 401 if (context_manager_shrink(agent->messages) == R_SUCCESS) { 483 // Retry with shrunk history | 402 // Retry with shrunk history 484 free(json_data); | 403 free(json_data); 485 json_data = agent_build_request(agent, NULL, NULL); | 404 json_data = agent_build_request(agent, NULL, NULL); 486 agent->state = AGENT_STATE_RUNNING; | 405 agent->state = AGENT_STATE_RUNNING; 487 agent->iteration_count--; | 406 agent->iteration_count--; 488 continue; | 407 continue; 489 } else { | 408 } else { 490 agent_set_error(agent, "Context limit reached and cannot be shrunk further."); | 409 agent_set_error(agent, "Context limit reached and cannot be shrunk further."); 491 free(json_data); | 410 free(json_data); 492 break; | 411 break; 493 } | 412 } 494 } | 413 } 495 -   | 496 free(json_data); | 414 free(json_data); 497 json_data = NULL; | 415 json_data = NULL; 498 -   | 499 if (!choice) { | 416 if (!choice) { 500 agent->tool_retry_count++; | 417 agent->tool_retry_count++; 501 if (agent->tool_retry_count >= agent->max_tool_retries) { | 418 if (agent->tool_retry_count >= agent->max_tool_retries) { 502 agent->state = AGENT_STATE_ERROR; | 419 agent->state = AGENT_STATE_ERROR; 503 agent_set_error(agent, "API request failed after retries"); | 420 agent_set_error(agent, "API request failed after retries"); 504 break; | 421 break; 505 } | 422 } 506 if (agent->verbose && !agent->is_subagent) { | 423 if (agent->verbose && !agent->is_subagent) { 507 fprintf(stderr, "[Agent] API error, retry %d/%d\n", | 424 fprintf(stderr, "[Agent] API error, retry %d/%d\n", 508 agent->tool_retry_count, agent->max_tool_retries); | 425 agent->tool_retry_count, agent->max_tool_retries); 509 } | 426 } 510 json_data = agent_build_request(agent, NULL, NULL); | 427 json_data = agent_build_request(agent, NULL, NULL); 511 agent->state = AGENT_STATE_RUNNING; | 428 agent->state = AGENT_STATE_RUNNING; 512 continue; | 429 continue; 513 } | 430 } 514 -   | 515 agent->tool_retry_count = 0; | 431 agent->tool_retry_count = 0; 516 -   | 517 struct json_object *message_obj = agent_get_message(choice); | 432 struct json_object *message_obj = agent_get_message(choice); 518 if (message_obj) { | 433 if (message_obj) { 519 messages_add_object(agent->messages, json_object_get(message_obj)); | 434 messages_add_object(agent->messages, json_object_get(message_obj)); 520 } | 435 } 521 -   | 522 char *content = agent_get_content(choice); | 436 char *content = agent_get_content(choice); 523 if (content && *content) { | 437 if (content && *content) { 524 if (!agent->is_subagent) { | 438 if (!agent->is_subagent) { 525 parse_markdown_to_ansi(content); | 439 parse_markdown_to_ansi(content); 526 printf("\n"); | 440 printf("\n"); 527 } | 441 } 528 -   | 529 size_t content_len = strlen(content); | 442 size_t content_len = strlen(content); 530 char *new_acc = realloc(accumulated_response, accumulated_len + content_len + 2); | 443 char *new_acc = realloc(accumulated_response, accumulated_len + content_len + 2); 531 if (new_acc) { | 444 if (new_acc) { 532 accumulated_response = new_acc; | 445 accumulated_response = new_acc; 533 if (accumulated_len > 0) { | 446 if (accumulated_len > 0) { 534 strcat(accumulated_response, "\n"); | 447 strcat(accumulated_response, "\n"); 535 accumulated_len += 1; | 448 accumulated_len += 1; 536 } | 449 } 537 strcpy(accumulated_response + accumulated_len, content); | 450 strcpy(accumulated_response + accumulated_len, content); 538 accumulated_len += content_len; | 451 accumulated_len += content_len; 539 } | 452 } 540 } | 453 } 541 -   | 542 bool has_tools = agent_has_tool_calls(choice); | 454 bool has_tools = agent_has_tool_calls(choice); 543 -   | 544 if (agent->verbose && !agent->is_subagent) { | 455 if (agent->verbose && !agent->is_subagent) { 545 fprintf(stderr, "[Agent] has_tool_calls=%s\n", has_tools ? "true" : "false"); | 456 fprintf(stderr, "[Agent] has_tool_calls=%s\n", has_tools ? "true" : "false"); 546 } | 457 } 547 -   | 548 if (has_tools) { | 458 if (has_tools) { 549 agent->state = AGENT_STATE_EXECUTING_TOOLS; | 459 agent->state = AGENT_STATE_EXECUTING_TOOLS; 550 -   | 551 struct json_object *tool_calls = agent_get_tool_calls(choice); | 460 struct json_object *tool_calls = agent_get_tool_calls(choice); 552 -   | 553 if (agent->verbose && !agent->is_subagent) { | 461 if (agent->verbose && !agent->is_subagent) { 554 int num_tools = json_object_array_length(tool_calls); | 462 int num_tools = json_object_array_length(tool_calls); 555 fprintf(stderr, "[Agent] Executing %d tool(s)\n", num_tools); | 463 fprintf(stderr, "[Agent] Executing %d tool(s)\n", num_tools); 556 } | 464 } 557 -   | 558 struct json_object *results = tool_registry_execute(agent->tools, tool_calls, agent->verbose); | 465 struct json_object *results = tool_registry_execute(agent->tools, tool_calls, agent->verbose); 559 -   | 560 int count = json_object_array_length(results); | 466 int count = json_object_array_length(results); 561 for (int i = 0; i < count; i++) { | 467 for (int i = 0; i < count; i++) { 562 struct json_object *result = json_object_array_get_idx(results, i); | 468 struct json_object *result = json_object_array_get_idx(results, i); 563 messages_add_tool_call(agent->messages, json_object_get(result)); | 469 messages_add_tool_call(agent->messages, json_object_get(result)); 564 } | 470 } 565 -   | 566 agent->state = AGENT_STATE_RUNNING; | 471 agent->state = AGENT_STATE_RUNNING; 567 json_data = agent_build_request(agent, NULL, NULL); | 472 json_data = agent_build_request(agent, NULL, NULL); 568 if (!json_data) { | 473 if (!json_data) { 569 agent->state = AGENT_STATE_ERROR; | 474 agent->state = AGENT_STATE_ERROR; 570 agent_set_error(agent, "Failed to create follow-up JSON"); | 475 agent_set_error(agent, "Failed to create follow-up JSON"); 571 free(content); | 476 free(content); 572 break; | 477 break; 573 } | 478 } 574 -   | 575 } else if (content && agent_response_indicates_incomplete(content)) { | 479 } else if (content && agent_response_indicates_incomplete(content)) { 576 if (agent->verbose && !agent->is_subagent) { | 480 if (agent->verbose && !agent->is_subagent) { 577 fprintf(stderr, "[Agent] Response indicates incomplete work, auto-continuing\n"); | 481 fprintf(stderr, "[Agent] Response indicates incomplete work, auto-continuing\n"); 578 } | 482 } 579 -   | 580 json_data = agent_build_request(agent, "user", | 483 json_data = agent_build_request(agent, "user", 581 "Continue. Execute the necessary actions to complete the task."); | 484 "Continue. Execute the necessary actions to complete the task."); 582 agent->state = AGENT_STATE_RUNNING; | 485 agent->state = AGENT_STATE_RUNNING; 583 if (!json_data) { | 486 if (!json_data) { 584 agent->state = AGENT_STATE_ERROR; | 487 agent->state = AGENT_STATE_ERROR; 585 agent_set_error(agent, "Failed to create continue JSON"); | 488 agent_set_error(agent, "Failed to create continue JSON"); 586 free(content); | 489 free(content); 587 break; | 490 break; 588 } | 491 } 589 } else { | 492 } else { 590 agent->state = AGENT_STATE_COMPLETED; | 493 agent->state = AGENT_STATE_COMPLETED; 591 if (agent->verbose && !agent->is_subagent) { | 494 if (agent->verbose && !agent->is_subagent) { 592 fprintf(stderr, "[Agent] Completed in %d iteration(s)\n", | 495 fprintf(stderr, "[Agent] Completed in %d iteration(s)\n", 593 agent->iteration_count); | 496 agent->iteration_count); 594 } | 497 } 595 } | 498 } 596 free(content); | 499 free(content); 597 } | 500 } 598 -   | 599 free(json_data); | 501 free(json_data); 600 return accumulated_response; | 502 return accumulated_response; 601 } | 503 } 602 -   | 603 char *agent_chat(const char *user_message, messages_handle messages) { | 504 char *agent_chat(const char *user_message, messages_handle messages) { 604 agent_handle agent = agent_create(user_message, messages); | 505 agent_handle agent = agent_create(user_message, messages); 605 if (!agent) return NULL; | 506 if (!agent) return NULL; 606 -   | 607 char *response = agent_run(agent, user_message); | 507 char *response = agent_run(agent, user_message); 608 -   | 609 if (agent->verbose && agent->state != AGENT_STATE_COMPLETED && agent->last_error) { | 508 if (agent->verbose && agent->state != AGENT_STATE_COMPLETED && agent->last_error) { 610 if (!agent->is_subagent) fprintf(stderr, "[Agent] Error: %s\n", agent->last_error); | 509 if (!agent->is_subagent) fprintf(stderr, "[Agent] Error: %s\n", agent->last_error); 611 } | 510 } 612 -   | 613 agent_destroy(agent); | 511 agent_destroy(agent); 614 return response; | 512 return response; 615 } | 513 } 616 -   | 617 char *agent_chat_with_limit(const char *user_message, int max_iterations, messages_handle messages) { | 514 char *agent_chat_with_limit(const char *user_message, int max_iterations, messages_handle messages) { 618 agent_handle agent = agent_create(user_message, messages); | 515 agent_handle agent = agent_create(user_message, messages); 619 if (!agent) return NULL; | 516 if (!agent) return NULL; 620 -   | 621 agent_set_max_iterations(agent, max_iterations); | 517 agent_set_max_iterations(agent, max_iterations); 622 char *response = agent_run(agent, user_message); | 518 char *response = agent_run(agent, user_message); 623 -   | 624 if (agent->verbose && agent->state != AGENT_STATE_COMPLETED && agent->last_error) { | 519 if (agent->verbose && agent->state != AGENT_STATE_COMPLETED && agent->last_error) { 625 if (!agent->is_subagent) fprintf(stderr, "[Agent] Error: %s\n", agent->last_error); | 520 if (!agent->is_subagent) fprintf(stderr, "[Agent] Error: %s\n", agent->last_error); 626 } | 521 } 627 -   | 628 agent_destroy(agent); | 522 agent_destroy(agent); 629 return response; | 523 return response; 630 } | 524 } -> Replacing lines in: src/bash_executor.c   CHANGES: src/bash_executor.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #define _GNU_SOURCE | 2 #define _GNU_SOURCE 4 #include "bash_executor.h" | 3 #include "bash_executor.h" 5 #include | 4 #include 6 #include | 5 #include 7 #include | 6 #include 8 #include | 7 #include 9 #include | 8 #include 10 #include | 9 #include 11 #include | 10 #include 12 #include | 11 #include 13 #include | 12 #include 14 #include | 13 #include 15 #include | 14 #include 16 #include | 15 #include 17 -   | 18 #define DEFAULT_TIMEOUT 300 | 16 #define DEFAULT_TIMEOUT 300 19 -   | 20 void r_process_result_free(r_process_result_t *res) { | 17 void r_process_result_free(r_process_result_t *res) { 21 if (!res) return; | 18 if (!res) return; 22 free(res->output); | 19 free(res->output); 23 free(res->log_path); | 20 free(res->log_path); 24 free(res); | 21 free(res); 25 } | 22 } 26 -   | 27 static char *get_log_path(int pid) { | 23 static char *get_log_path(int pid) { 28 char *path = NULL; | 24 char *path = NULL; 29 if (asprintf(&path, "/tmp/r_process_%d.log", pid) == -1) return NULL; | 25 if (asprintf(&path, "/tmp/r_process_%d.log", pid) == -1) return NULL; 30 return path; | 26 return path; 31 } | 27 } 32 -   | 33 r_process_result_t *r_bash_execute_ext(const char *command, int timeout_seconds, bool async) { | 28 r_process_result_t *r_bash_execute_ext(const char *command, int timeout_seconds, bool async) { 34 if (!command) return NULL; | 29 if (!command) return NULL; 35 -   | 36 r_process_result_t *res = calloc(1, sizeof(r_process_result_t)); | 30 r_process_result_t *res = calloc(1, sizeof(r_process_result_t)); 37 if (!res) return NULL; | 31 if (!res) return NULL; 38 -   | 39 if (timeout_seconds <= 0) timeout_seconds = DEFAULT_TIMEOUT; | 32 if (timeout_seconds <= 0) timeout_seconds = DEFAULT_TIMEOUT; 40 -   | 41 char tmp_script[] = "/tmp/r_bash_XXXXXX.sh"; | 33 char tmp_script[] = "/tmp/r_bash_XXXXXX.sh"; 42 int script_fd = mkstemps(tmp_script, 3); | 34 int script_fd = mkstemps(tmp_script, 3); 43 if (script_fd == -1) { | 35 if (script_fd == -1) { 44 res->output = strdup("Error: failed to create temp script"); | 36 res->output = strdup("Error: failed to create temp script"); 45 return res; | 37 return res; 46 } | 38 } 47 -   | 48 dprintf(script_fd, "%s\n", command); | 39 dprintf(script_fd, "%s\n", command); 49 close(script_fd); | 40 close(script_fd); 50 -   | 51 int pipe_fds[2]; | 41 int pipe_fds[2]; 52 if (pipe(pipe_fds) == -1) { | 42 if (pipe(pipe_fds) == -1) { 53 unlink(tmp_script); | 43 unlink(tmp_script); 54 res->output = strdup("Error: pipe failed"); | 44 res->output = strdup("Error: pipe failed"); 55 return res; | 45 return res; 56 } | 46 } 57 -   | 58 pid_t pid = fork(); | 47 pid_t pid = fork(); 59 if (pid == -1) { | 48 if (pid == -1) { 60 close(pipe_fds[0]); | 49 close(pipe_fds[0]); 61 close(pipe_fds[1]); | 50 close(pipe_fds[1]); 62 unlink(tmp_script); | 51 unlink(tmp_script); 63 res->output = strdup("Error: fork failed"); | 52 res->output = strdup("Error: fork failed"); 64 return res; | 53 return res; 65 } | 54 } 66 -   | 67 if (pid == 0) { | 55 if (pid == 0) { 68 // Child | 56 // Child 69 setsid(); // New session to prevent signals to parent | 57 setsid(); // New session to prevent signals to parent 70 close(pipe_fds[0]); | 58 close(pipe_fds[0]); 71 | 59 72 // Setup log file for child | 60 // Setup log file for child 73 char *log_p = get_log_path(getpid()); | 61 char *log_p = get_log_path(getpid()); 74 int log_fd = open(log_p, O_WRONLY | O_CREAT | O_TRUNC, 0644); | 62 int log_fd = open(log_p, O_WRONLY | O_CREAT | O_TRUNC, 0644); 75 free(log_p); | 63 free(log_p); 76 -   | 77 if (log_fd != -1) { | 64 if (log_fd != -1) { 78 dup2(log_fd, STDOUT_FILENO); | 65 dup2(log_fd, STDOUT_FILENO); 79 dup2(log_fd, STDERR_FILENO); | 66 dup2(log_fd, STDERR_FILENO); 80 close(log_fd); | 67 close(log_fd); 81 } else { | 68 } else { 82 dup2(pipe_fds[1], STDOUT_FILENO); | 69 dup2(pipe_fds[1], STDOUT_FILENO); 83 dup2(pipe_fds[1], STDERR_FILENO); | 70 dup2(pipe_fds[1], STDERR_FILENO); 84 } | 71 } 85 | 72 86 // Also pipe back to parent if possible (redundant but safe for short commands) | 73 // Also pipe back to parent if possible (redundant but safe for short commands) 87 // Actually, let's just use log file for everything. | 74 // Actually, let's just use log file for everything. 88 | 75 89 close(pipe_fds[1]); | 76 close(pipe_fds[1]); 90 -   | 91 char *args[] = {"bash", tmp_script, NULL}; | 77 char *args[] = {"bash", tmp_script, NULL}; 92 execvp("bash", args); | 78 execvp("bash", args); 93 exit(1); | 79 exit(1); 94 } | 80 } 95 -   | 96 // Parent | 81 // Parent 97 res->pid = pid; | 82 res->pid = pid; 98 res->log_path = get_log_path(pid); | 83 res->log_path = get_log_path(pid); 99 res->is_running = true; | 84 res->is_running = true; 100 close(pipe_fds[1]); | 85 close(pipe_fds[1]); 101 close(pipe_fds[0]); | 86 close(pipe_fds[0]); 102 -   | 103 if (async) { | 87 if (async) { 104 res->output = strdup("Process started in background."); | 88 res->output = strdup("Process started in background."); 105 usleep(100000); // Give child time to start | 89 usleep(100000); // Give child time to start 106 unlink(tmp_script); | 90 unlink(tmp_script); 107 return res; | 91 return res; 108 } | 92 } 109 -   | 110 // Wait for timeout | 93 // Wait for timeout 111 time_t start_time = time(NULL); | 94 time_t start_time = time(NULL); 112 long last_read_pos = 0; | 95 long last_read_pos = 0; 113 -   | 114 while (true) { | 96 while (true) { 115 int status; | 97 int status; 116 pid_t ret = waitpid(pid, &status, WNOHANG); | 98 pid_t ret = waitpid(pid, &status, WNOHANG); 117 | 99 118 // Read new content from log file and print to stdout for the user | 100 // Read new content from log file and print to stdout for the user 119 FILE *f_tail = fopen(res->log_path, "r"); | 101 FILE *f_tail = fopen(res->log_path, "r"); 120 if (f_tail) { | 102 if (f_tail) { 121 fseek(f_tail, last_read_pos, SEEK_SET); | 103 fseek(f_tail, last_read_pos, SEEK_SET); 122 char tail_buf[4096]; | 104 char tail_buf[4096]; 123 while (fgets(tail_buf, sizeof(tail_buf), f_tail)) { | 105 while (fgets(tail_buf, sizeof(tail_buf), f_tail)) { 124 fprintf(stdout, "[%d]\t %s", pid, tail_buf); | 106 fprintf(stdout, "[%d]\t %s", pid, tail_buf); 125 fflush(stdout); | 107 fflush(stdout); 126 } | 108 } 127 last_read_pos = ftell(f_tail); | 109 last_read_pos = ftell(f_tail); 128 fclose(f_tail); | 110 fclose(f_tail); 129 } | 111 } 130 -   | 131 if (ret == pid) { | 112 if (ret == pid) { 132 res->is_running = false; | 113 res->is_running = false; 133 res->exit_status = WIFEXITED(status) ? WEXITSTATUS(status) : -1; | 114 res->exit_status = WIFEXITED(status) ? WEXITSTATUS(status) : -1; 134 break; | 115 break; 135 } else if (ret == -1) { | 116 } else if (ret == -1) { 136 res->is_running = false; | 117 res->is_running = false; 137 break; | 118 break; 138 } | 119 } 139 -   | 140 if (time(NULL) - start_time >= timeout_seconds) { | 120 if (time(NULL) - start_time >= timeout_seconds) { 141 res->timed_out = true; | 121 res->timed_out = true; 142 break; | 122 break; 143 } | 123 } 144 usleep(50000); // 100ms -> 50ms for better responsiveness | 124 usleep(50000); // 100ms -> 50ms for better responsiveness 145 } | 125 } 146 -   | 147 // Read log file for output | 126 // Read log file for output 148 FILE *log_f = fopen(res->log_path, "r"); | 127 FILE *log_f = fopen(res->log_path, "r"); 149 if (log_f) { | 128 if (log_f) { 150 fseek(log_f, 0, SEEK_END); | 129 fseek(log_f, 0, SEEK_END); 151 long size = ftell(log_f); | 130 long size = ftell(log_f); 152 rewind(log_f); | 131 rewind(log_f); 153 if (size >= 0) { | 132 if (size >= 0) { 154 res->output = malloc((size_t)size + 1); | 133 res->output = malloc((size_t)size + 1); 155 if (res->output) { | 134 if (res->output) { 156 size_t rs = fread(res->output, 1, (size_t)size, log_f); | 135 size_t rs = fread(res->output, 1, (size_t)size, log_f); 157 res->output[rs] = '\0'; | 136 res->output[rs] = '\0'; 158 } | 137 } 159 } | 138 } 160 fclose(log_f); | 139 fclose(log_f); 161 } | 140 } 162 -   | 163 if (!res->output) res->output = strdup(""); | 141 if (!res->output) res->output = strdup(""); 164 -   | 165 unlink(tmp_script); | 142 unlink(tmp_script); 166 return res; | 143 return res; 167 } | 144 } 168 -   | 169 char *r_bash_execute(const char *command, bool interactive, int timeout_seconds) { | 145 char *r_bash_execute(const char *command, bool interactive, int timeout_seconds) { 170 // Legacy support wrapper | 146 // Legacy support wrapper 171 r_process_result_t *res = r_bash_execute_ext(command, timeout_seconds, false); | 147 r_process_result_t *res = r_bash_execute_ext(command, timeout_seconds, false); 172 char *out = strdup(res->output); | 148 char *out = strdup(res->output); 173 r_process_result_free(res); | 149 r_process_result_free(res); 174 return out; | 150 return out; 175 } | 151 } -> Replacing lines in: src/bash_repair.c   CHANGES: src/bash_repair.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "bash_repair.h" | 2 #include "bash_repair.h" 4 #include | 3 #include 5 #include | 4 #include 6 #include | 5 #include 7 #include | 6 #include 8 #include | 7 #include 9 -   | 10 static char *ensure_shebang(const char *text) { | 8 static char *ensure_shebang(const char *text) { 11 if (!text || !*text) return strdup(""); | 9 if (!text || !*text) return strdup(""); 12 | 10 13 // Check if it already has a shebang | 11 // Check if it already has a shebang 14 const char *p = text; | 12 const char *p = text; 15 while (*p && isspace((unsigned char)*p)) p++; | 13 while (*p && isspace((unsigned char)*p)) p++; 16 if (strncmp(p, "#!", 2) == 0 || strncmp(p, ": <<", 4) == 0) { | 14 if (strncmp(p, "#!", 2) == 0 || strncmp(p, ": <<", 4) == 0) { 17 return strdup(text); | 15 return strdup(text); 18 } | 16 } 19 -   | 20 // Heuristic: if it has multiple lines, add shebang | 17 // Heuristic: if it has multiple lines, add shebang 21 if (strchr(text, '\n')) { | 18 if (strchr(text, '\n')) { 22 char *result = malloc(strlen(text) + 32); | 19 char *result = malloc(strlen(text) + 32); 23 if (!result) return strdup(text); | 20 if (!result) return strdup(text); 24 strcpy(result, "#!/usr/bin/env bash\n"); | 21 strcpy(result, "#!/usr/bin/env bash\n"); 25 strcat(result, text); | 22 strcat(result, text); 26 return result; | 23 return result; 27 } | 24 } 28 -   | 29 return strdup(text); | 25 return strdup(text); 30 } | 26 } 31 -   | 32 static char *normalize_whitespace_and_operators(const char *src) { | 27 static char *normalize_whitespace_and_operators(const char *src) { 33 if (!src) return NULL; | 28 if (!src) return NULL; 34 size_t src_len = strlen(src); | 29 size_t src_len = strlen(src); 35 char *result = malloc(src_len * 2 + 1); | 30 char *result = malloc(src_len * 2 + 1); 36 if (!result) return NULL; | 31 if (!result) return NULL; 37 -   | 38 char *dst = result; | 32 char *dst = result; 39 const char *curr = src; | 33 const char *curr = src; 40 -   | 41 while (*curr) { | 34 while (*curr) { 42 if (*curr == '\r') { | 35 if (*curr == '\r') { 43 curr++; | 36 curr++; 44 continue; | 37 continue; 45 } | 38 } 46 -   | 47 // Detect operators to normalize spaces around them | 39 // Detect operators to normalize spaces around them 48 const char *ops[] = {"||", "&&", ">>", "|&", "|", ";", ">", "<", NULL}; | 40 const char *ops[] = {"||", "&&", ">>", "|&", "|", ";", ">", "<", NULL}; 49 bool matched_op = false; | 41 bool matched_op = false; 50 for (int i = 0; ops[i]; i++) { | 42 for (int i = 0; ops[i]; i++) { 51 size_t op_len = strlen(ops[i]); | 43 size_t op_len = strlen(ops[i]); 52 if (strncmp(curr, ops[i], op_len) == 0) { | 44 if (strncmp(curr, ops[i], op_len) == 0) { 53 // Remove preceding spaces in dst if any | 45 // Remove preceding spaces in dst if any 54 while (dst > result && isspace((unsigned char)*(dst - 1)) && *(dst - 1) != '\n') dst--; | 46 while (dst > result && isspace((unsigned char)*(dst - 1)) && *(dst - 1) != '\n') dst--; 55 | 47 56 if (dst > result && *(dst - 1) != '\n') *dst++ = ' '; | 48 if (dst > result && *(dst - 1) != '\n') *dst++ = ' '; 57 memcpy(dst, ops[i], op_len); | 49 memcpy(dst, ops[i], op_len); 58 dst += op_len; | 50 dst += op_len; 59 curr += op_len; | 51 curr += op_len; 60 | 52 61 // Skip following spaces in curr | 53 // Skip following spaces in curr 62 while (*curr && isspace((unsigned char)*curr) && *curr != '\n') curr++; | 54 while (*curr && isspace((unsigned char)*curr) && *curr != '\n') curr++; 63 if (*curr && *curr != '\n') *dst++ = ' '; | 55 if (*curr && *curr != '\n') *dst++ = ' '; 64 | 56 65 matched_op = true; | 57 matched_op = true; 66 break; | 58 break; 67 } | 59 } 68 } | 60 } 69 -   | 70 if (!matched_op) { | 61 if (!matched_op) { 71 *dst++ = *curr++; | 62 *dst++ = *curr++; 72 } | 63 } 73 } | 64 } 74 *dst = '\0'; | 65 *dst = '\0'; 75 -   | 76 // Second pass to strip trailing spaces on each line | 66 // Second pass to strip trailing spaces on each line 77 char *s2 = strdup(result); | 67 char *s2 = strdup(result); 78 free(result); | 68 free(result); 79 if (!s2) return NULL; | 69 if (!s2) return NULL; 80 -   | 81 char *final = malloc(strlen(s2) + 1); | 70 char *final = malloc(strlen(s2) + 1); 82 char *f_ptr = final; | 71 char *f_ptr = final; 83 char *line = s2; | 72 char *line = s2; 84 while (line && *line) { | 73 while (line && *line) { 85 char *next_line = strchr(line, '\n'); | 74 char *next_line = strchr(line, '\n'); 86 size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); | 75 size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); 87 | 76 88 const char *end = line + line_len - 1; | 77 const char *end = line + line_len - 1; 89 while (end >= line && isspace((unsigned char)*end)) end--; | 78 while (end >= line && isspace((unsigned char)*end)) end--; 90 | 79 91 size_t new_len = (size_t)(end - line + 1); | 80 size_t new_len = (size_t)(end - line + 1); 92 memcpy(f_ptr, line, new_len); | 81 memcpy(f_ptr, line, new_len); 93 f_ptr += new_len; | 82 f_ptr += new_len; 94 if (next_line) { | 83 if (next_line) { 95 *f_ptr++ = '\n'; | 84 *f_ptr++ = '\n'; 96 line = next_line + 1; | 85 line = next_line + 1; 97 } else { | 86 } else { 98 break; | 87 break; 99 } | 88 } 100 } | 89 } 101 *f_ptr = '\0'; | 90 *f_ptr = '\0'; 102 free(s2); | 91 free(s2); 103 return final; | 92 return final; 104 } | 93 } 105 -   | 106 static char *fix_line_issues(const char *src) { | 94 static char *fix_line_issues(const char *src) { 107 if (!src) return NULL; | 95 if (!src) return NULL; 108 size_t src_len = strlen(src); | 96 size_t src_len = strlen(src); 109 char *result = malloc(src_len * 2 + 1024); | 97 char *result = malloc(src_len * 2 + 1024); 110 if (!result) return NULL; | 98 if (!result) return NULL; 111 -   | 112 char *dst = result; | 99 char *dst = result; 113 const char *line = src; | 100 const char *line = src; 114 -   | 115 while (line && *line) { | 101 while (line && *line) { 116 const char *next_line = strchr(line, '\n'); | 102 const char *next_line = strchr(line, '\n'); 117 size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); | 103 size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); 118 -   | 119 char line_buf[4096]; | 104 char line_buf[4096]; 120 if (line_len >= sizeof(line_buf)) line_len = sizeof(line_buf) - 1; | 105 if (line_len >= sizeof(line_buf)) line_len = sizeof(line_buf) - 1; 121 memcpy(line_buf, line, line_len); | 106 memcpy(line_buf, line, line_len); 122 line_buf[line_len] = '\0'; | 107 line_buf[line_len] = '\0'; 123 -   | 124 // 1. Tiny Shell Lint (else if -> elif) | 108 // 1. Tiny Shell Lint (else if -> elif) 125 char *else_if = strstr(line_buf, "else if "); | 109 char *else_if = strstr(line_buf, "else if "); 126 if (else_if) { | 110 if (else_if) { 127 // Very basic replacement | 111 // Very basic replacement 128 memmove(else_if + 4, else_if + 8, strlen(else_if + 8) + 1); | 112 memmove(else_if + 4, else_if + 8, strlen(else_if + 8) + 1); 129 memcpy(else_if, "elif", 4); | 113 memcpy(else_if, "elif", 4); 130 } | 114 } 131 -   | 132 // 2. Fix unbalanced quotes | 115 // 2. Fix unbalanced quotes 133 int single = 0, double_q = 0; | 116 int single = 0, double_q = 0; 134 bool escaped = false; | 117 bool escaped = false; 135 for (size_t i = 0; i < strlen(line_buf); i++) { | 118 for (size_t i = 0; i < strlen(line_buf); i++) { 136 if (escaped) { escaped = false; continue; } | 119 if (escaped) { escaped = false; continue; } 137 if (line_buf[i] == '\\') escaped = true; | 120 if (line_buf[i] == '\\') escaped = true; 138 else if (line_buf[i] == '\'') single++; | 121 else if (line_buf[i] == '\'') single++; 139 else if (line_buf[i] == '"') double_q++; | 122 else if (line_buf[i] == '"') double_q++; 140 } | 123 } 141 if (single % 2 == 1 && double_q == 0) strcat(line_buf, "'"); | 124 if (single % 2 == 1 && double_q == 0) strcat(line_buf, "'"); 142 else if (double_q % 2 == 1 && single == 0) strcat(line_buf, "\""); | 125 else if (double_q % 2 == 1 && single == 0) strcat(line_buf, "\""); 143 -   | 144 // 3. Fix trailing operators | 126 // 3. Fix trailing operators 145 size_t cur_len = strlen(line_buf); | 127 size_t cur_len = strlen(line_buf); 146 const char *ops[] = {"||", "&&", ">>", "|&", "|", ">", "<", NULL}; | 128 const char *ops[] = {"||", "&&", ">>", "|&", "|", ">", "<", NULL}; 147 for (int i = 0; ops[i]; i++) { | 129 for (int i = 0; ops[i]; i++) { 148 size_t op_len = strlen(ops[i]); | 130 size_t op_len = strlen(ops[i]); 149 if (cur_len >= op_len) { | 131 if (cur_len >= op_len) { 150 if (strcmp(line_buf + cur_len - op_len, ops[i]) == 0) { | 132 if (strcmp(line_buf + cur_len - op_len, ops[i]) == 0) { 151 // Comment it | 133 // Comment it 152 char temp[4096]; | 134 char temp[4096]; 153 strcpy(temp, line_buf); | 135 strcpy(temp, line_buf); 154 temp[cur_len - op_len] = '\0'; | 136 temp[cur_len - op_len] = '\0'; 155 strcat(temp, "# "); | 137 strcat(temp, "# "); 156 strcat(temp, ops[i]); | 138 strcat(temp, ops[i]); 157 strcpy(line_buf, temp); | 139 strcpy(line_buf, temp); 158 break; | 140 break; 159 } | 141 } 160 } | 142 } 161 } | 143 } 162 -   | 163 // 4. Dangerous rm -rf check | 144 // 4. Dangerous rm -rf check 164 if (strstr(line_buf, "sudo rm -rf /") || strstr(line_buf, "rm -rf / ")) { | 145 if (strstr(line_buf, "sudo rm -rf /") || strstr(line_buf, "rm -rf / ")) { 165 strcpy(dst, "# WARNING: potentially destructive command detected\n"); | 146 strcpy(dst, "# WARNING: potentially destructive command detected\n"); 166 dst += strlen(dst); | 147 dst += strlen(dst); 167 } | 148 } 168 -   | 169 strcpy(dst, line_buf); | 149 strcpy(dst, line_buf); 170 dst += strlen(dst); | 150 dst += strlen(dst); 171 if (next_line) *dst++ = '\n'; | 151 if (next_line) *dst++ = '\n'; 172 -   | 173 if (next_line) line = next_line + 1; | 152 if (next_line) line = next_line + 1; 174 else break; | 153 else break; 175 } | 154 } 176 *dst = '\0'; | 155 *dst = '\0'; 177 return result; | 156 return result; 178 } | 157 } 179 -   | 180 static char *collapse_nested_bash_c(const char *src) { | 158 static char *collapse_nested_bash_c(const char *src) { 181 if (!src) return NULL; | 159 if (!src) return NULL; 182 // Pattern: bash -c "bash -c '...'") | 160 // Pattern: bash -c "bash -c '...'") 183 // We'll just do a very basic string replacement for common variants | 161 // We'll just do a very basic string replacement for common variants 184 char *s1 = strdup(src); | 162 char *s1 = strdup(src); 185 char *patterns[] = { | 163 char *patterns[] = { 186 "bash -c \"bash -c '", | 164 "bash -c \"bash -c '", 187 "bash -c 'bash -c \"", | 165 "bash -c 'bash -c \"", 188 NULL | 166 NULL 189 }; | 167 }; 190 | 168 191 for (int i = 0; patterns[i]; i++) { | 169 for (int i = 0; patterns[i]; i++) { 192 char *p; | 170 char *p; 193 while ((p = strstr(s1, patterns[i]))) { | 171 while ((p = strstr(s1, patterns[i]))) { 194 // Find closing quotes | 172 // Find closing quotes 195 char outer = patterns[i][8]; // " or ' | 173 char outer = patterns[i][8]; // " or ' 196 char inner = patterns[i][18]; // ' or " | 174 char inner = patterns[i][18]; // ' or " 197 | 175 198 char *inner_end = strchr(p + 19, inner); | 176 char *inner_end = strchr(p + 19, inner); 199 if (inner_end && *(inner_end + 1) == outer) { | 177 if (inner_end && *(inner_end + 1) == outer) { 200 // We can collapse. | 178 // We can collapse. 201 // Original: [p]bash -c "bash -c 'cmd'"[end] | 179 // Original: [p]bash -c "bash -c 'cmd'"[end] 202 // New: [p]bash -c 'cmd'[end] | 180 // New: [p]bash -c 'cmd'[end] 203 size_t cmd_len = (size_t)(inner_end - (p + 19)); | 181 size_t cmd_len = (size_t)(inner_end - (p + 19)); 204 char *new_s = malloc(strlen(s1) + 1); | 182 char *new_s = malloc(strlen(s1) + 1); 205 size_t prefix_len = (size_t)(p - s1); | 183 size_t prefix_len = (size_t)(p - s1); 206 memcpy(new_s, s1, prefix_len); | 184 memcpy(new_s, s1, prefix_len); 207 char *d = new_s + prefix_len; | 185 char *d = new_s + prefix_len; 208 strcpy(d, "bash -c "); | 186 strcpy(d, "bash -c "); 209 d += 8; | 187 d += 8; 210 *d++ = inner; | 188 *d++ = inner; 211 memcpy(d, p + 19, cmd_len); | 189 memcpy(d, p + 19, cmd_len); 212 d += cmd_len; | 190 d += cmd_len; 213 *d++ = inner; | 191 *d++ = inner; 214 strcpy(d, inner_end + 2); | 192 strcpy(d, inner_end + 2); 215 | 193 216 free(s1); | 194 free(s1); 217 s1 = new_s; | 195 s1 = new_s; 218 } else { | 196 } else { 219 break; | 197 break; 220 } | 198 } 221 } | 199 } 222 } | 200 } 223 return s1; | 201 return s1; 224 } | 202 } 225 -   | 226 char *bash_repair_command(const char *src) { | 203 char *bash_repair_command(const char *src) { 227 if (!src) return NULL; | 204 if (!src) return NULL; 228 -   | 229 char *s1 = normalize_whitespace_and_operators(src); | 205 char *s1 = normalize_whitespace_and_operators(src); 230 char *s2 = fix_line_issues(s1); | 206 char *s2 = fix_line_issues(s1); 231 free(s1); | 207 free(s1); 232 char *s3 = collapse_nested_bash_c(s2); | 208 char *s3 = collapse_nested_bash_c(s2); 233 free(s2); | 209 free(s2); 234 char *s4 = ensure_shebang(s3); | 210 char *s4 = ensure_shebang(s3); 235 free(s3); | 211 free(s3); 236 -   | 237 return s4; | 212 return s4; 238 } | 213 } -> Replacing lines in: src/context_manager.c   CHANGES: src/context_manager.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "context_manager.h" | 2 #include "context_manager.h" 4 #include | 3 #include 5 #include | 4 #include 6 #include | 5 #include 7 -   | 8 #define MIN_KEEP_CHARS 500 | 6 #define MIN_KEEP_CHARS 500 9 #define TRUNCATE_MARKER "\n\n[... content truncated for context management ...]\n\n" | 7 #define TRUNCATE_MARKER "\n\n[... content truncated for context management ...]\n\n" 10 -   | 11 static const char *get_message_role(struct json_object *msg) { | 8 static const char *get_message_role(struct json_object *msg) { 12 struct json_object *role_obj; | 9 struct json_object *role_obj; 13 if (json_object_object_get_ex(msg, "role", &role_obj)) { | 10 if (json_object_object_get_ex(msg, "role", &role_obj)) { 14 return json_object_get_string(role_obj); | 11 return json_object_get_string(role_obj); 15 } | 12 } 16 // Tool results don't have a 'role' field in some implementations, | 13 // Tool results don't have a 'role' field in some implementations, 17 // they have 'tool_call_id'. | 14 // they have 'tool_call_id'. 18 if (json_object_object_get_ex(msg, "tool_call_id", &role_obj)) { | 15 if (json_object_object_get_ex(msg, "tool_call_id", &role_obj)) { 19 return "tool"; | 16 return "tool"; 20 } | 17 } 21 return ""; | 18 return ""; 22 } | 19 } 23 -   | 24 static bool has_tool_calls(struct json_object *msg) { | 20 static bool has_tool_calls(struct json_object *msg) { 25 struct json_object *tool_calls; | 21 struct json_object *tool_calls; 26 if (json_object_object_get_ex(msg, "tool_calls", &tool_calls)) { | 22 if (json_object_object_get_ex(msg, "tool_calls", &tool_calls)) { 27 return json_object_array_length(tool_calls) > 0; | 23 return json_object_array_length(tool_calls) > 0; 28 } | 24 } 29 return false; | 25 return false; 30 } | 26 } 31 -   | 32 static size_t get_message_content_len(struct json_object *msg) { | 27 static size_t get_message_content_len(struct json_object *msg) { 33 struct json_object *content_obj; | 28 struct json_object *content_obj; 34 const char *content = NULL; | 29 const char *content = NULL; 35 -   | 36 if (json_object_object_get_ex(msg, "content", &content_obj)) { | 30 if (json_object_object_get_ex(msg, "content", &content_obj)) { 37 content = json_object_get_string(content_obj); | 31 content = json_object_get_string(content_obj); 38 } else if (json_object_object_get_ex(msg, "tool_result", &content_obj)) { | 32 } else if (json_object_object_get_ex(msg, "tool_result", &content_obj)) { 39 content = json_object_get_string(content_obj); | 33 content = json_object_get_string(content_obj); 40 } | 34 } 41 -   | 42 return content ? strlen(content) : 0; | 35 return content ? strlen(content) : 0; 43 } | 36 } 44 -   | 45 static size_t calculate_total_size(messages_handle msgs) { | 37 static size_t calculate_total_size(messages_handle msgs) { 46 size_t total = 0; | 38 size_t total = 0; 47 int count = messages_count(msgs); | 39 int count = messages_count(msgs); 48 for (int i = 0; i < count; i++) { | 40 for (int i = 0; i < count; i++) { 49 total += get_message_content_len(messages_get_object(msgs, i)); | 41 total += get_message_content_len(messages_get_object(msgs, i)); 50 } | 42 } 51 return total; | 43 return total; 52 } | 44 } 53 -   | 54 static r_status_t perform_truncate(messages_handle msgs, int index, double ratio) { | 45 static r_status_t perform_truncate(messages_handle msgs, int index, double ratio) { 55 struct json_object *msg = messages_get_object(msgs, index); | 46 struct json_object *msg = messages_get_object(msgs, index); 56 if (!msg) return R_ERROR_NOT_FOUND; | 47 if (!msg) return R_ERROR_NOT_FOUND; 57 -   | 58 struct json_object *content_obj; | 48 struct json_object *content_obj; 59 const char *content = NULL; | 49 const char *content = NULL; 60 bool is_tool_result = false; | 50 bool is_tool_result = false; 61 -   | 62 if (json_object_object_get_ex(msg, "content", &content_obj)) { | 51 if (json_object_object_get_ex(msg, "content", &content_obj)) { 63 content = json_object_get_string(content_obj); | 52 content = json_object_get_string(content_obj); 64 } else if (json_object_object_get_ex(msg, "tool_result", &content_obj)) { | 53 } else if (json_object_object_get_ex(msg, "tool_result", &content_obj)) { 65 content = json_object_get_string(content_obj); | 54 content = json_object_get_string(content_obj); 66 is_tool_result = true; | 55 is_tool_result = true; 67 } | 56 } 68 -   | 69 if (!content) return R_SUCCESS; | 57 if (!content) return R_SUCCESS; 70 -   | 71 size_t len = strlen(content); | 58 size_t len = strlen(content); 72 size_t target_len = (size_t)(len * ratio); | 59 size_t target_len = (size_t)(len * ratio); 73 if (target_len < MIN_KEEP_CHARS * 2) target_len = MIN_KEEP_CHARS * 2; | 60 if (target_len < MIN_KEEP_CHARS * 2) target_len = MIN_KEEP_CHARS * 2; 74 if (target_len >= len) return R_SUCCESS; | 61 if (target_len >= len) return R_SUCCESS; 75 -   | 76 size_t keep_each = target_len / 2; | 62 size_t keep_each = target_len / 2; 77 | 63 78 char *new_content = malloc(keep_each * 2 + strlen(TRUNCATE_MARKER) + 1); | 64 char *new_content = malloc(keep_each * 2 + strlen(TRUNCATE_MARKER) + 1); 79 if (!new_content) return R_ERROR_OUT_OF_MEMORY; | 65 if (!new_content) return R_ERROR_OUT_OF_MEMORY; 80 -   | 81 strncpy(new_content, content, keep_each); | 66 strncpy(new_content, content, keep_each); 82 new_content[keep_each] = '\0'; | 67 new_content[keep_each] = '\0'; 83 strcat(new_content, TRUNCATE_MARKER); | 68 strcat(new_content, TRUNCATE_MARKER); 84 strcat(new_content, content + len - keep_each); | 69 strcat(new_content, content + len - keep_each); 85 -   | 86 struct json_object *new_msg = json_tokener_parse(json_object_to_json_string(msg)); | 70 struct json_object *new_msg = json_tokener_parse(json_object_to_json_string(msg)); 87 if (is_tool_result) { | 71 if (is_tool_result) { 88 json_object_object_add(new_msg, "tool_result", json_object_new_string(new_content)); | 72 json_object_object_add(new_msg, "tool_result", json_object_new_string(new_content)); 89 } else { | 73 } else { 90 json_object_object_add(new_msg, "content", json_object_new_string(new_content)); | 74 json_object_object_add(new_msg, "content", json_object_new_string(new_content)); 91 } | 75 } 92 -   | 93 free(new_content); | 76 free(new_content); 94 return messages_replace_at(msgs, index, new_msg); | 77 return messages_replace_at(msgs, index, new_msg); 95 } | 78 } 96 -   | 97 r_status_t context_manager_shrink(messages_handle msgs) { | 79 r_status_t context_manager_shrink(messages_handle msgs) { 98 if (!msgs) return R_ERROR_INVALID_ARG; | 80 if (!msgs) return R_ERROR_INVALID_ARG; 99 -   | 100 int count = messages_count(msgs); | 81 int count = messages_count(msgs); 101 if (count <= 2) return R_ERROR_API_ERROR; | 82 if (count <= 2) return R_ERROR_API_ERROR; 102 -   | 103 size_t initial_size = calculate_total_size(msgs); | 83 size_t initial_size = calculate_total_size(msgs); 104 size_t target_size = (size_t)(initial_size * 0.5); | 84 size_t target_size = (size_t)(initial_size * 0.5); 105 if (target_size < 20000) target_size = 20000; | 85 if (target_size < 20000) target_size = 20000; 106 -   | 107 fprintf(stderr, " \033[2m-> Context overflow (%zu chars). Middle-out shrinking to %zu...\033[0m\n", | 86 fprintf(stderr, " \033[2m-> Context overflow (%zu chars). Middle-out shrinking to %zu...\033[0m\n", 108 initial_size, target_size); | 87 initial_size, target_size); 109 -   | 110 // Strategy 1: Truncate very large messages first (safe, doesn't break sequence) | 88 // Strategy 1: Truncate very large messages first (safe, doesn't break sequence) 111 for (int i = 0; i < messages_count(msgs); i++) { | 89 for (int i = 0; i < messages_count(msgs); i++) { 112 struct json_object *msg = messages_get_object(msgs, i); | 90 struct json_object *msg = messages_get_object(msgs, i); 113 if (get_message_content_len(msg) > 50000) { | 91 if (get_message_content_len(msg) > 50000) { 114 perform_truncate(msgs, i, 0.2); | 92 perform_truncate(msgs, i, 0.2); 115 } | 93 } 116 } | 94 } 117 -   | 118 // Strategy 2: Remove messages from the middle until size is within target | 95 // Strategy 2: Remove messages from the middle until size is within target 119 // We keep: | 96 // We keep: 120 // - System message (usually index 0) | 97 // - System message (usually index 0) 121 // - Most recent 4 messages (usually current task context) | 98 // - Most recent 4 messages (usually current task context) 122 while (calculate_total_size(msgs) > target_size && messages_count(msgs) > 6) { | 99 while (calculate_total_size(msgs) > target_size && messages_count(msgs) > 6) { 123 int middle_idx = 1; // Start after system | 100 int middle_idx = 1; // Start after system 124 struct json_object *msg = messages_get_object(msgs, middle_idx); | 101 struct json_object *msg = messages_get_object(msgs, middle_idx); 125 const char *role = get_message_role(msg); | 102 const char *role = get_message_role(msg); 126 -   | 127 int remove_count = 1; | 103 int remove_count = 1; 128 if (strcmp(role, "assistant") == 0 && has_tool_calls(msg)) { | 104 if (strcmp(role, "assistant") == 0 && has_tool_calls(msg)) { 129 // Must also remove the following tool results to maintain sequence | 105 // Must also remove the following tool results to maintain sequence 130 int search_idx = middle_idx + 1; | 106 int search_idx = middle_idx + 1; 131 while (search_idx < messages_count(msgs) - 4) { | 107 while (search_idx < messages_count(msgs) - 4) { 132 struct json_object *next_msg = messages_get_object(msgs, search_idx); | 108 struct json_object *next_msg = messages_get_object(msgs, search_idx); 133 if (strcmp(get_message_role(next_msg), "tool") == 0) { | 109 if (strcmp(get_message_role(next_msg), "tool") == 0) { 134 remove_count++; | 110 remove_count++; 135 search_idx++; | 111 search_idx++; 136 } else { | 112 } else { 137 break; | 113 break; 138 } | 114 } 139 } | 115 } 140 } | 116 } 141 -   | 142 // Ensure we don't eat into the "recent" buffer | 117 // Ensure we don't eat into the "recent" buffer 143 if (middle_idx + remove_count > messages_count(msgs) - 4) { | 118 if (middle_idx + remove_count > messages_count(msgs) - 4) { 144 break; | 119 break; 145 } | 120 } 146 -   | 147 messages_remove_range(msgs, middle_idx, remove_count); | 121 messages_remove_range(msgs, middle_idx, remove_count); 148 } | 122 } 149 -   | 150 size_t final_size = calculate_total_size(msgs); | 123 size_t final_size = calculate_total_size(msgs); 151 fprintf(stderr, " \033[2m-> Context shrunk to %zu chars. Remaining messages: %d\033[0m\n", | 124 fprintf(stderr, " \033[2m-> Context shrunk to %zu chars. Remaining messages: %d\033[0m\n", 152 final_size, messages_count(msgs)); | 125 final_size, messages_count(msgs)); 153 -   | 154 return R_SUCCESS; | 126 return R_SUCCESS; 155 } | 127 } -> Replacing lines in: src/context_summarizer.c   CHANGES: src/context_summarizer.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 - #include "context_summarizer.h"  | | 1 + // retoor  2 #include | 2 #include 3 #include | 3 #include 4 #include | 4 #include 5 -   | 6 // Placeholder for LLM API call | 5 // Placeholder for LLM API call 7 // In a real implementation, this function would call the LLM API to get the summary. | 6 // In a real implementation, this function would call the LLM API to get the summary. 8 static char* call_llm_to_summarize(const char* messages_concatenated) { | 7 static char* call_llm_to_summarize(const char* messages_concatenated) { 9 // For demonstration, just return a dummy summary. | 8 // For demonstration, just return a dummy summary. 10 const char* dummy_summary = "This is a summary of the oldest 20 messages."; | 9 const char* dummy_summary = "This is a summary of the oldest 20 messages."; 11 char* result = malloc(strlen(dummy_summary) + 1); | 10 char* result = malloc(strlen(dummy_summary) + 1); 12 if (result) { | 11 if (result) { 13 strcpy(result, dummy_summary); | 12 strcpy(result, dummy_summary); 14 } | 13 } 15 return result; | 14 return result; 16 } | 15 } 17 -   | 18 char* summarize_oldest_messages(const char** messages, size_t message_count) { | 16 char* summarize_oldest_messages(const char** messages, size_t message_count) { 19 // Concatenate the oldest 20 messages | 17 // Concatenate the oldest 20 messages 20 size_t total_length = 0; | 18 size_t total_length = 0; 21 size_t start_index = 0; | 19 size_t start_index = 0; 22 if (message_count > 20) { | 20 if (message_count > 20) { 23 start_index = message_count - 20; | 21 start_index = message_count - 20; 24 } | 22 } 25 for (size_t i = start_index; i < message_count; ++i) { | 23 for (size_t i = start_index; i < message_count; ++i) { 26 total_length += strlen(messages[i]) + 1; // +1 for separator | 24 total_length += strlen(messages[i]) + 1; // +1 for separator 27 } | 25 } 28 -   | 29 char* concatenated = malloc(total_length + 1); | 26 char* concatenated = malloc(total_length + 1); 30 if (!concatenated) { | 27 if (!concatenated) { 31 return NULL; | 28 return NULL; 32 } | 29 } 33 concatenated[0] = '\0'; | 30 concatenated[0] = '\0'; 34 -   | 35 for (size_t i = start_index; i < message_count; ++i) { | 31 for (size_t i = start_index; i < message_count; ++i) { 36 strcat(concatenated, messages[i]); | 32 strcat(concatenated, messages[i]); 37 if (i < message_count - 1) { | 33 if (i < message_count - 1) { 38 strcat(concatenated, " "); // separator | 34 strcat(concatenated, " "); // separator 39 } | 35 } 40 } | 36 } 41 -   | 42 // Call the LLM API to get the summary | 37 // Call the LLM API to get the summary 43 char* summary = call_llm_to_summarize(concatenated); | 38 char* summary = call_llm_to_summarize(concatenated); 44 free(concatenated); | 39 free(concatenated); 45 return summary; | 40 return summary; 46 } | 41 } -> Replacing lines in: src/db.c   CHANGES: src/db.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "db.h" | 2 #include "db.h" 4 #include "r_config.h" | 3 #include "r_config.h" 5 #include | 4 #include 6 #include | 5 #include 7 #include | 6 #include 8 #include | 7 #include 9 #include | 8 #include 10 -   | 11 struct db_t { | 9 struct db_t { 12 sqlite3 *conn; | 10 sqlite3 *conn; 13 char *path; | 11 char *path; 14 }; | 12 }; 15 -   | 16 static char *expand_home_directory(const char *path) { | 13 static char *expand_home_directory(const char *path) { 17 if (!path) return NULL; | 14 if (!path) return NULL; 18 if (path[0] != '~') return strdup(path); | 15 if (path[0] != '~') return strdup(path); 19 -   | 20 const char *home_dir = getenv("HOME"); | 16 const char *home_dir = getenv("HOME"); 21 if (!home_dir) home_dir = getenv("USERPROFILE"); | 17 if (!home_dir) home_dir = getenv("USERPROFILE"); 22 if (!home_dir) return strdup(path); | 18 if (!home_dir) return strdup(path); 23 -   | 24 size_t home_len = strlen(home_dir); | 19 size_t home_len = strlen(home_dir); 25 size_t path_len = strlen(path); | 20 size_t path_len = strlen(path); 26 char *expanded = malloc(home_len + path_len); | 21 char *expanded = malloc(home_len + path_len); 27 if (!expanded) return NULL; | 22 if (!expanded) return NULL; 28 -   | 29 strcpy(expanded, home_dir); | 23 strcpy(expanded, home_dir); 30 strcat(expanded, path + 1); | 24 strcat(expanded, path + 1); 31 return expanded; | 25 return expanded; 32 } | 26 } 33 -   | 34 db_handle db_open(const char *path) { | 27 db_handle db_open(const char *path) { 35 struct db_t *db = calloc(1, sizeof(struct db_t)); | 28 struct db_t *db = calloc(1, sizeof(struct db_t)); 36 if (!db) return NULL; | 29 if (!db) return NULL; 37 -   | 38 if (!path) { | 30 if (!path) { 39 r_config_handle cfg = r_config_get_instance(); | 31 r_config_handle cfg = r_config_get_instance(); 40 path = r_config_get_db_path(cfg); | 32 path = r_config_get_db_path(cfg); 41 } | 33 } 42 -   | 43 db->path = expand_home_directory(path); | 34 db->path = expand_home_directory(path); 44 if (!db->path) { | 35 if (!db->path) { 45 free(db); | 36 free(db); 46 return NULL; | 37 return NULL; 47 } | 38 } 48 -   | 49 if (sqlite3_open(db->path, &db->conn) != SQLITE_OK) { | 39 if (sqlite3_open(db->path, &db->conn) != SQLITE_OK) { 50 free(db->path); | 40 free(db->path); 51 free(db); | 41 free(db); 52 return NULL; | 42 return NULL; 53 } | 43 } 54 -   | 55 db_init(db); | 44 db_init(db); 56 return db; | 45 return db; 57 } | 46 } 58 -   | 59 void db_close(db_handle db) { | 47 void db_close(db_handle db) { 60 if (!db) return; | 48 if (!db) return; 61 if (db->conn) sqlite3_close(db->conn); | 49 if (db->conn) sqlite3_close(db->conn); 62 free(db->path); | 50 free(db->path); 63 free(db); | 51 free(db); 64 } | 52 } 65 -   | 66 r_status_t db_init(db_handle db) { | 53 r_status_t db_init(db_handle db) { 67 if (!db || !db->conn) return R_ERROR_INVALID_ARG; | 54 if (!db || !db->conn) return R_ERROR_INVALID_ARG; 68 -   | 69 const char *sql = | 55 const char *sql = 70 "CREATE TABLE IF NOT EXISTS kv (" | 56 "CREATE TABLE IF NOT EXISTS kv (" 71 " key TEXT PRIMARY KEY," | 57 " key TEXT PRIMARY KEY," 72 " value TEXT NOT NULL," | 58 " value TEXT NOT NULL," 73 " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," | 59 " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," 74 " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" | 60 " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" 75 ");" | 61 ");" 76 "CREATE TABLE IF NOT EXISTS file_versions (" | 62 "CREATE TABLE IF NOT EXISTS file_versions (" 77 " id INTEGER PRIMARY KEY AUTOINCREMENT," | 63 " id INTEGER PRIMARY KEY AUTOINCREMENT," 78 " path TEXT NOT NULL," | 64 " path TEXT NOT NULL," 79 " content TEXT NOT NULL," | 65 " content TEXT NOT NULL," 80 " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" | 66 " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" 81 ");" | 67 ");" 82 "CREATE TABLE IF NOT EXISTS conversations (" | 68 "CREATE TABLE IF NOT EXISTS conversations (" 83 " session_key TEXT PRIMARY KEY," | 69 " session_key TEXT PRIMARY KEY," 84 " data TEXT NOT NULL," | 70 " data TEXT NOT NULL," 85 " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" | 71 " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" 86 ");" | 72 ");" 87 "CREATE TABLE IF NOT EXISTS blackboard (" | 73 "CREATE TABLE IF NOT EXISTS blackboard (" 88 " id INTEGER PRIMARY KEY AUTOINCREMENT," | 74 " id INTEGER PRIMARY KEY AUTOINCREMENT," 89 " sender TEXT NOT NULL," | 75 " sender TEXT NOT NULL," 90 " recipient TEXT," | 76 " recipient TEXT," 91 " topic TEXT NOT NULL," | 77 " topic TEXT NOT NULL," 92 " message TEXT NOT NULL," | 78 " message TEXT NOT NULL," 93 " status TEXT DEFAULT 'pending'," | 79 " status TEXT DEFAULT 'pending'," 94 " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," | 80 " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," 95 " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" | 81 " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" 96 ");" | 82 ");" 97 "CREATE TABLE IF NOT EXISTS gtr (" | 83 "CREATE TABLE IF NOT EXISTS gtr (" 98 " task_hash TEXT PRIMARY KEY," | 84 " task_hash TEXT PRIMARY KEY," 99 " task_description TEXT NOT NULL," | 85 " task_description TEXT NOT NULL," 100 " status TEXT NOT NULL," | 86 " status TEXT NOT NULL," 101 " owner_agent TEXT NOT NULL," | 87 " owner_agent TEXT NOT NULL," 102 " result_summary TEXT," | 88 " result_summary TEXT," 103 " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," | 89 " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," 104 " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" | 90 " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" 105 ");" | 91 ");" 106 "CREATE TABLE IF NOT EXISTS audit_log (" | 92 "CREATE TABLE IF NOT EXISTS audit_log (" 107 " id INTEGER PRIMARY KEY AUTOINCREMENT," | 93 " id INTEGER PRIMARY KEY AUTOINCREMENT," 108 " timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP," | 94 " timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP," 109 " issuer_agent TEXT NOT NULL," | 95 " issuer_agent TEXT NOT NULL," 110 " recipient_agent TEXT NOT NULL," | 96 " recipient_agent TEXT NOT NULL," 111 " order_description TEXT NOT NULL," | 97 " order_description TEXT NOT NULL," 112 " logic_justification TEXT NOT NULL," | 98 " logic_justification TEXT NOT NULL," 113 " status TEXT NOT NULL" | 99 " status TEXT NOT NULL" 114 ");" | 100 ");" 115 "CREATE TABLE IF NOT EXISTS agents (" | 101 "CREATE TABLE IF NOT EXISTS agents (" 116 " agent_id TEXT PRIMARY KEY," | 102 " agent_id TEXT PRIMARY KEY," 117 " role TEXT NOT NULL," | 103 " role TEXT NOT NULL," 118 " manager_id TEXT," | 104 " manager_id TEXT," 119 " department TEXT," | 105 " department TEXT," 120 " budget_limit_tokens INTEGER DEFAULT 1000000," | 106 " budget_limit_tokens INTEGER DEFAULT 1000000," 121 " used_tokens INTEGER DEFAULT 0," | 107 " used_tokens INTEGER DEFAULT 0," 122 " last_heartbeat TIMESTAMP DEFAULT CURRENT_TIMESTAMP," | 108 " last_heartbeat TIMESTAMP DEFAULT CURRENT_TIMESTAMP," 123 " status TEXT DEFAULT 'active'" | 109 " status TEXT DEFAULT 'active'" 124 ");" | 110 ");" 125 "CREATE TABLE IF NOT EXISTS research_tasks (" | 111 "CREATE TABLE IF NOT EXISTS research_tasks (" 126 " id INTEGER PRIMARY KEY AUTOINCREMENT," | 112 " id INTEGER PRIMARY KEY AUTOINCREMENT," 127 " url_hash TEXT UNIQUE," | 113 " url_hash TEXT UNIQUE," 128 " url TEXT NOT NULL," | 114 " url TEXT NOT NULL," 129 " status TEXT NOT NULL," | 115 " status TEXT NOT NULL," 130 " summary TEXT," | 116 " summary TEXT," 131 " batch_id TEXT," | 117 " batch_id TEXT," 132 " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," | 118 " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," 133 " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" | 119 " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" 134 ");"; | 120 ");"; 135 -   | 136 char *err_msg = NULL; | 121 char *err_msg = NULL; 137 if (sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg) != SQLITE_OK) { | 122 if (sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg) != SQLITE_OK) { 138 sqlite3_free(err_msg); | 123 sqlite3_free(err_msg); 139 return R_ERROR_DB_QUERY; | 124 return R_ERROR_DB_QUERY; 140 } | 125 } 141 return R_SUCCESS; | 126 return R_SUCCESS; 142 } | 127 } 143 -   | 144 r_status_t db_kv_set(db_handle db, const char *key, const char *value) { | 128 r_status_t db_kv_set(db_handle db, const char *key, const char *value) { 145 if (!db || !db->conn || !key || !value) return R_ERROR_INVALID_ARG; | 129 if (!db || !db->conn || !key || !value) return R_ERROR_INVALID_ARG; 146 -   | 147 char *sql = sqlite3_mprintf( | 130 char *sql = sqlite3_mprintf( 148 "INSERT OR REPLACE INTO kv (key, value, updated_at) VALUES (%Q, %Q, CURRENT_TIMESTAMP)", | 131 "INSERT OR REPLACE INTO kv (key, value, updated_at) VALUES (%Q, %Q, CURRENT_TIMESTAMP)", 149 key, value); | 132 key, value); 150 if (!sql) return R_ERROR_OUT_OF_MEMORY; | 133 if (!sql) return R_ERROR_OUT_OF_MEMORY; 151 -   | 152 char *err_msg = NULL; | 134 char *err_msg = NULL; 153 int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg); | 135 int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg); 154 sqlite3_free(sql); | 136 sqlite3_free(sql); 155 -   | 156 if (rc != SQLITE_OK) { | 137 if (rc != SQLITE_OK) { 157 sqlite3_free(err_msg); | 138 sqlite3_free(err_msg); 158 return R_ERROR_DB_QUERY; | 139 return R_ERROR_DB_QUERY; 159 } | 140 } 160 return R_SUCCESS; | 141 return R_SUCCESS; 161 } | 142 } 162 -   | 163 r_status_t db_kv_get(db_handle db, const char *key, char **value) { | 143 r_status_t db_kv_get(db_handle db, const char *key, char **value) { 164 if (!db || !db->conn || !key || !value) return R_ERROR_INVALID_ARG; | 144 if (!db || !db->conn || !key || !value) return R_ERROR_INVALID_ARG; 165 -   | 166 *value = NULL; | 145 *value = NULL; 167 const char *sql = "SELECT value FROM kv WHERE key = ?"; | 146 const char *sql = "SELECT value FROM kv WHERE key = ?"; 168 sqlite3_stmt *stmt = NULL; | 147 sqlite3_stmt *stmt = NULL; 169 -   | 170 if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) { | 148 if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) { 171 return R_ERROR_DB_QUERY; | 149 return R_ERROR_DB_QUERY; 172 } | 150 } 173 -   | 174 sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC); | 151 sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC); 175 -   | 176 int rc = sqlite3_step(stmt); | 152 int rc = sqlite3_step(stmt); 177 if (rc == SQLITE_ROW) { | 153 if (rc == SQLITE_ROW) { 178 const char *val = (const char *)sqlite3_column_text(stmt, 0); | 154 const char *val = (const char *)sqlite3_column_text(stmt, 0); 179 *value = val ? strdup(val) : NULL; | 155 *value = val ? strdup(val) : NULL; 180 } | 156 } 181 -   | 182 sqlite3_finalize(stmt); | 157 sqlite3_finalize(stmt); 183 return (rc == SQLITE_ROW) ? R_SUCCESS : R_ERROR_DB_NOT_FOUND; | 158 return (rc == SQLITE_ROW) ? R_SUCCESS : R_ERROR_DB_NOT_FOUND; 184 } | 159 } 185 -   | 186 r_status_t db_execute(db_handle db, const char *sql, struct json_object **result) { | 160 r_status_t db_execute(db_handle db, const char *sql, struct json_object **result) { 187 if (!db || !db->conn || !sql || !result) return R_ERROR_INVALID_ARG; | 161 if (!db || !db->conn || !sql || !result) return R_ERROR_INVALID_ARG; 188 -   | 189 *result = NULL; | 162 *result = NULL; 190 -   | 191 const char *select_check = sql; | 163 const char *select_check = sql; 192 while (*select_check == ' ') select_check++; | 164 while (*select_check == ' ') select_check++; 193 -   | 194 if (strncasecmp(select_check, "SELECT", 6) != 0) { | 165 if (strncasecmp(select_check, "SELECT", 6) != 0) { 195 char *err_msg = NULL; | 166 char *err_msg = NULL; 196 int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg); | 167 int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg); 197 if (rc != SQLITE_OK) { | 168 if (rc != SQLITE_OK) { 198 struct json_object *error_obj = json_object_new_object(); | 169 struct json_object *error_obj = json_object_new_object(); 199 json_object_object_add(error_obj, "error", | 170 json_object_object_add(error_obj, "error", 200 json_object_new_string(err_msg ? err_msg : "Query failed")); | 171 json_object_new_string(err_msg ? err_msg : "Query failed")); 201 sqlite3_free(err_msg); | 172 sqlite3_free(err_msg); 202 *result = error_obj; | 173 *result = error_obj; 203 return R_ERROR_DB_QUERY; | 174 return R_ERROR_DB_QUERY; 204 } | 175 } 205 struct json_object *success_obj = json_object_new_object(); | 176 struct json_object *success_obj = json_object_new_object(); 206 json_object_object_add(success_obj, "success", json_object_new_boolean(1)); | 177 json_object_object_add(success_obj, "success", json_object_new_boolean(1)); 207 *result = success_obj; | 178 *result = success_obj; 208 return R_SUCCESS; | 179 return R_SUCCESS; 209 } | 180 } 210 -   | 211 sqlite3_stmt *stmt = NULL; | 181 sqlite3_stmt *stmt = NULL; 212 if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) { | 182 if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) { 213 struct json_object *error_obj = json_object_new_object(); | 183 struct json_object *error_obj = json_object_new_object(); 214 json_object_object_add(error_obj, "error", | 184 json_object_object_add(error_obj, "error", 215 json_object_new_string(sqlite3_errmsg(db->conn))); | 185 json_object_new_string(sqlite3_errmsg(db->conn))); 216 *result = error_obj; | 186 *result = error_obj; 217 return R_ERROR_DB_QUERY; | 187 return R_ERROR_DB_QUERY; 218 } | 188 } 219 -   | 220 struct json_object *array = json_object_new_array(); | 189 struct json_object *array = json_object_new_array(); 221 int col_count = sqlite3_column_count(stmt); | 190 int col_count = sqlite3_column_count(stmt); 222 -   | 223 while (sqlite3_step(stmt) == SQLITE_ROW) { | 191 while (sqlite3_step(stmt) == SQLITE_ROW) { 224 struct json_object *row = json_object_new_object(); | 192 struct json_object *row = json_object_new_object(); 225 -   | 226 for (int i = 0; i < col_count; i++) { | 193 for (int i = 0; i < col_count; i++) { 227 const char *col_name = sqlite3_column_name(stmt, i); | 194 const char *col_name = sqlite3_column_name(stmt, i); 228 int col_type = sqlite3_column_type(stmt, i); | 195 int col_type = sqlite3_column_type(stmt, i); 229 -   | 230 switch (col_type) { | 196 switch (col_type) { 231 case SQLITE_INTEGER: | 197 case SQLITE_INTEGER: 232 json_object_object_add(row, col_name, | 198 json_object_object_add(row, col_name, 233 json_object_new_int64(sqlite3_column_int64(stmt, i))); | 199 json_object_new_int64(sqlite3_column_int64(stmt, i))); 234 break; | 200 break; 235 case SQLITE_FLOAT: | 201 case SQLITE_FLOAT: 236 json_object_object_add(row, col_name, | 202 json_object_object_add(row, col_name, 237 json_object_new_double(sqlite3_column_double(stmt, i))); | 203 json_object_new_double(sqlite3_column_double(stmt, i))); 238 break; | 204 break; 239 case SQLITE_TEXT: | 205 case SQLITE_TEXT: 240 json_object_object_add(row, col_name, | 206 json_object_object_add(row, col_name, 241 json_object_new_string((const char *)sqlite3_column_text(stmt, i))); | 207 json_object_new_string((const char *)sqlite3_column_text(stmt, i))); 242 break; | 208 break; 243 case SQLITE_NULL: | 209 case SQLITE_NULL: 244 json_object_object_add(row, col_name, NULL); | 210 json_object_object_add(row, col_name, NULL); 245 break; | 211 break; 246 default: | 212 default: 247 json_object_object_add(row, col_name, | 213 json_object_object_add(row, col_name, 248 json_object_new_string((const char *)sqlite3_column_text(stmt, i))); | 214 json_object_new_string((const char *)sqlite3_column_text(stmt, i))); 249 break; | 215 break; 250 } | 216 } 251 } | 217 } 252 -   | 253 json_object_array_add(array, row); | 218 json_object_array_add(array, row); 254 } | 219 } 255 -   | 256 sqlite3_finalize(stmt); | 220 sqlite3_finalize(stmt); 257 *result = array; | 221 *result = array; 258 return R_SUCCESS; | 222 return R_SUCCESS; 259 } | 223 } 260 -   | 261 char *db_get_schema(db_handle db) { | 224 char *db_get_schema(db_handle db) { 262 if (!db || !db->conn) return strdup("Database not available"); | 225 if (!db || !db->conn) return strdup("Database not available"); 263 -   | 264 struct json_object *result = NULL; | 226 struct json_object *result = NULL; 265 r_status_t status = db_execute(db, | 227 r_status_t status = db_execute(db, 266 "SELECT name, sql FROM sqlite_master WHERE type='table' ORDER BY name", | 228 "SELECT name, sql FROM sqlite_master WHERE type='table' ORDER BY name", 267 &result); | 229 &result); 268 -   | 269 if (status != R_SUCCESS || !result) { | 230 if (status != R_SUCCESS || !result) { 270 return strdup("Failed to get schema"); | 231 return strdup("Failed to get schema"); 271 } | 232 } 272 -   | 273 char *schema = strdup(json_object_to_json_string_ext(result, JSON_C_TO_STRING_PRETTY)); | 233 char *schema = strdup(json_object_to_json_string_ext(result, JSON_C_TO_STRING_PRETTY)); 274 json_object_put(result); | 234 json_object_put(result); 275 return schema; | 235 return schema; 276 } | 236 } 277 -   | 278 r_status_t db_store_file_version(db_handle db, const char *path) { | 237 r_status_t db_store_file_version(db_handle db, const char *path) { 279 if (!db || !db->conn || !path) return R_ERROR_INVALID_ARG; | 238 if (!db || !db->conn || !path) return R_ERROR_INVALID_ARG; 280 -   | 281 FILE *fp = fopen(path, "r"); | 239 FILE *fp = fopen(path, "r"); 282 if (!fp) return R_ERROR_FILE_NOT_FOUND; | 240 if (!fp) return R_ERROR_FILE_NOT_FOUND; 283 -   | 284 fseek(fp, 0, SEEK_END); | 241 fseek(fp, 0, SEEK_END); 285 long size = ftell(fp); | 242 long size = ftell(fp); 286 rewind(fp); | 243 rewind(fp); 287 -   | 288 if (size <= 0 || size > 1000000) { | 244 if (size <= 0 || size > 1000000) { 289 fclose(fp); | 245 fclose(fp); 290 return R_SUCCESS; | 246 return R_SUCCESS; 291 } | 247 } 292 -   | 293 char *content = malloc(size + 1); | 248 char *content = malloc(size + 1); 294 if (!content) { | 249 if (!content) { 295 fclose(fp); | 250 fclose(fp); 296 return R_ERROR_OUT_OF_MEMORY; | 251 return R_ERROR_OUT_OF_MEMORY; 297 } | 252 } 298 -   | 299 size_t read_size = fread(content, 1, size, fp); | 253 size_t read_size = fread(content, 1, size, fp); 300 content[read_size] = '\0'; | 254 content[read_size] = '\0'; 301 fclose(fp); | 255 fclose(fp); 302 -   | 303 char *sql = sqlite3_mprintf( | 256 char *sql = sqlite3_mprintf( 304 "INSERT INTO file_versions (path, content) VALUES (%Q, %Q)", | 257 "INSERT INTO file_versions (path, content) VALUES (%Q, %Q)", 305 path, content); | 258 path, content); 306 free(content); | 259 free(content); 307 -   | 308 if (!sql) return R_ERROR_OUT_OF_MEMORY; | 260 if (!sql) return R_ERROR_OUT_OF_MEMORY; 309 -   | 310 char *err_msg = NULL; | 261 char *err_msg = NULL; 311 int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg); | 262 int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg); 312 sqlite3_free(sql); | 263 sqlite3_free(sql); 313 sqlite3_free(err_msg); | 264 sqlite3_free(err_msg); 314 -   | 315 return (rc == SQLITE_OK) ? R_SUCCESS : R_ERROR_DB_QUERY; | 265 return (rc == SQLITE_OK) ? R_SUCCESS : R_ERROR_DB_QUERY; 316 } | 266 } 317 -   | 318 r_status_t db_save_conversation(db_handle db, const char *session_key, const char *data) { | 267 r_status_t db_save_conversation(db_handle db, const char *session_key, const char *data) { 319 if (!db || !db->conn || !session_key || !data) return R_ERROR_INVALID_ARG; | 268 if (!db || !db->conn || !session_key || !data) return R_ERROR_INVALID_ARG; 320 -   | 321 char *sql = sqlite3_mprintf( | 269 char *sql = sqlite3_mprintf( 322 "INSERT OR REPLACE INTO conversations (session_key, data, updated_at) " | 270 "INSERT OR REPLACE INTO conversations (session_key, data, updated_at) " 323 "VALUES (%Q, %Q, CURRENT_TIMESTAMP)", | 271 "VALUES (%Q, %Q, CURRENT_TIMESTAMP)", 324 session_key, data); | 272 session_key, data); 325 if (!sql) return R_ERROR_OUT_OF_MEMORY; | 273 if (!sql) return R_ERROR_OUT_OF_MEMORY; 326 -   | 327 char *err_msg = NULL; | 274 char *err_msg = NULL; 328 int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg); | 275 int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg); 329 sqlite3_free(sql); | 276 sqlite3_free(sql); 330 sqlite3_free(err_msg); | 277 sqlite3_free(err_msg); 331 -   | 332 return (rc == SQLITE_OK) ? R_SUCCESS : R_ERROR_DB_QUERY; | 278 return (rc == SQLITE_OK) ? R_SUCCESS : R_ERROR_DB_QUERY; 333 } | 279 } 334 -   | 335 r_status_t db_load_conversation(db_handle db, const char *session_key, char **data) { | 280 r_status_t db_load_conversation(db_handle db, const char *session_key, char **data) { 336 if (!db || !db->conn || !session_key || !data) return R_ERROR_INVALID_ARG; | 281 if (!db || !db->conn || !session_key || !data) return R_ERROR_INVALID_ARG; 337 -   | 338 *data = NULL; | 282 *data = NULL; 339 const char *sql = "SELECT data FROM conversations WHERE session_key = ?"; | 283 const char *sql = "SELECT data FROM conversations WHERE session_key = ?"; 340 sqlite3_stmt *stmt = NULL; | 284 sqlite3_stmt *stmt = NULL; 341 -   | 342 if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) { | 285 if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) { 343 return R_ERROR_DB_QUERY; | 286 return R_ERROR_DB_QUERY; 344 } | 287 } 345 -   | 346 sqlite3_bind_text(stmt, 1, session_key, -1, SQLITE_STATIC); | 288 sqlite3_bind_text(stmt, 1, session_key, -1, SQLITE_STATIC); 347 -   | 348 int rc = sqlite3_step(stmt); | 289 int rc = sqlite3_step(stmt); 349 if (rc == SQLITE_ROW) { | 290 if (rc == SQLITE_ROW) { 350 const char *val = (const char *)sqlite3_column_text(stmt, 0); | 291 const char *val = (const char *)sqlite3_column_text(stmt, 0); 351 *data = val ? strdup(val) : NULL; | 292 *data = val ? strdup(val) : NULL; 352 } | 293 } 353 -   | 354 sqlite3_finalize(stmt); | 294 sqlite3_finalize(stmt); 355 return (rc == SQLITE_ROW) ? R_SUCCESS : R_ERROR_DB_NOT_FOUND; | 295 return (rc == SQLITE_ROW) ? R_SUCCESS : R_ERROR_DB_NOT_FOUND; 356 } | 296 } -> Replacing lines in: src/http_client.c   CHANGES: src/http_client.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "http_client.h" | 2 #include "http_client.h" 4 #include | 3 #include 5 #include | 4 #include 6 #include | 5 #include 7 #include | 6 #include 8 #include | 7 #include 9 #include | 8 #include 10 #include | 9 #include 11 #include | 10 #include 12 -   | 13 #define HTTP_MAX_RETRIES 3 | 11 #define HTTP_MAX_RETRIES 3 14 #define HTTP_RETRY_DELAY_MS 2000 | 12 #define HTTP_RETRY_DELAY_MS 2000 15 -   | 16 struct http_client_t { | 13 struct http_client_t { 17 char *bearer_token; | 14 char *bearer_token; 18 long timeout_seconds; | 15 long timeout_seconds; 19 long connect_timeout_seconds; | 16 long connect_timeout_seconds; 20 bool show_spinner; | 17 bool show_spinner; 21 }; | 18 }; 22 -   | 23 struct response_buffer_t { | 19 struct response_buffer_t { 24 char *data; | 20 char *data; 25 size_t size; | 21 size_t size; 26 }; | 22 }; 27 -   | 28 static struct timespec spinner_start_time = {0, 0}; | 23 static struct timespec spinner_start_time = {0, 0}; 29 static volatile int spinner_running = 0; | 24 static volatile int spinner_running = 0; 30 -   | 31 static double get_elapsed_seconds(void) { | 25 static double get_elapsed_seconds(void) { 32 struct timespec now; | 26 struct timespec now; 33 clock_gettime(CLOCK_MONOTONIC, &now); | 27 clock_gettime(CLOCK_MONOTONIC, &now); 34 return (now.tv_sec - spinner_start_time.tv_sec) + | 28 return (now.tv_sec - spinner_start_time.tv_sec) + 35 (now.tv_nsec - spinner_start_time.tv_nsec) / 1e9; | 29 (now.tv_nsec - spinner_start_time.tv_nsec) / 1e9; 36 } | 30 } 37 -   | 38 static void *spinner_thread(void *arg) { | 31 static void *spinner_thread(void *arg) { 39 (void)arg; | 32 (void)arg; 40 const char *frames[] = {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}; | 33 const char *frames[] = {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}; 41 int frame = 0; | 34 int frame = 0; 42 while (spinner_running) { | 35 while (spinner_running) { 43 double elapsed = get_elapsed_seconds(); | 36 double elapsed = get_elapsed_seconds(); 44 fprintf(stderr, "\r%s Querying AI... (%.1fs) ", frames[frame % 10], elapsed); | 37 fprintf(stderr, "\r%s Querying AI... (%.1fs) ", frames[frame % 10], elapsed); 45 fflush(stderr); | 38 fflush(stderr); 46 frame++; | 39 frame++; 47 usleep(80000); | 40 usleep(80000); 48 } | 41 } 49 return NULL; | 42 return NULL; 50 } | 43 } 51 -   | 52 static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { | 44 static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { 53 size_t total_size = size * nmemb; | 45 size_t total_size = size * nmemb; 54 struct response_buffer_t *response = (struct response_buffer_t *)userp; | 46 struct response_buffer_t *response = (struct response_buffer_t *)userp; 55 -   | 56 if (total_size > SIZE_MAX - response->size - 1) { | 47 if (total_size > SIZE_MAX - response->size - 1) { 57 return 0; | 48 return 0; 58 } | 49 } 59 -   | 60 char *ptr = realloc(response->data, response->size + total_size + 1); | 50 char *ptr = realloc(response->data, response->size + total_size + 1); 61 if (!ptr) { | 51 if (!ptr) { 62 return 0; | 52 return 0; 63 } | 53 } 64 -   | 65 response->data = ptr; | 54 response->data = ptr; 66 memcpy(&(response->data[response->size]), contents, total_size); | 55 memcpy(&(response->data[response->size]), contents, total_size); 67 response->size += total_size; | 56 response->size += total_size; 68 response->data[response->size] = '\0'; | 57 response->data[response->size] = '\0'; 69 return total_size; | 58 return total_size; 70 } | 59 } 71 -   | 72 http_client_handle http_client_create(const char *bearer_token) { | 60 http_client_handle http_client_create(const char *bearer_token) { 73 struct http_client_t *client = calloc(1, sizeof(struct http_client_t)); | 61 struct http_client_t *client = calloc(1, sizeof(struct http_client_t)); 74 if (!client) return NULL; | 62 if (!client) return NULL; 75 -   | 76 if (bearer_token) { | 63 if (bearer_token) { 77 client->bearer_token = strdup(bearer_token); | 64 client->bearer_token = strdup(bearer_token); 78 if (!client->bearer_token) { | 65 if (!client->bearer_token) { 79 free(client); | 66 free(client); 80 return NULL; | 67 return NULL; 81 } | 68 } 82 } | 69 } 83 -   | 84 client->timeout_seconds = 300; | 70 client->timeout_seconds = 300; 85 client->connect_timeout_seconds = 10; | 71 client->connect_timeout_seconds = 10; 86 client->show_spinner = true; | 72 client->show_spinner = true; 87 -   | 88 return client; | 73 return client; 89 } | 74 } 90 -   | 91 void http_client_destroy(http_client_handle client) { | 75 void http_client_destroy(http_client_handle client) { 92 if (!client) return; | 76 if (!client) return; 93 free(client->bearer_token); | 77 free(client->bearer_token); 94 free(client); | 78 free(client); 95 } | 79 } 96 -   | 97 void http_client_set_show_spinner(http_client_handle client, bool show) { | 80 void http_client_set_show_spinner(http_client_handle client, bool show) { 98 if (client) client->show_spinner = show; | 81 if (client) client->show_spinner = show; 99 } | 82 } 100 -   | 101 void http_client_set_timeout(http_client_handle client, long timeout_seconds) { | 83 void http_client_set_timeout(http_client_handle client, long timeout_seconds) { 102 if (client) client->timeout_seconds = timeout_seconds; | 84 if (client) client->timeout_seconds = timeout_seconds; 103 } | 85 } 104 -   | 105 void http_client_set_connect_timeout(http_client_handle client, long timeout_seconds) { | 86 void http_client_set_connect_timeout(http_client_handle client, long timeout_seconds) { 106 if (client) client->connect_timeout_seconds = timeout_seconds; | 87 if (client) client->connect_timeout_seconds = timeout_seconds; 107 } | 88 } 108 -   | 109 r_status_t http_post(http_client_handle client, const char *url, | 89 r_status_t http_post(http_client_handle client, const char *url, 110 const char *data, char **response) { | 90 const char *data, char **response) { 111 if (!client || !url || !response) return R_ERROR_INVALID_ARG; | 91 if (!client || !url || !response) return R_ERROR_INVALID_ARG; 112 -   | 113 CURL *curl = NULL; | 92 CURL *curl = NULL; 114 struct curl_slist *headers = NULL; | 93 struct curl_slist *headers = NULL; 115 struct response_buffer_t resp = {NULL, 0}; | 94 struct response_buffer_t resp = {NULL, 0}; 116 int retry_count = 0; | 95 int retry_count = 0; 117 pthread_t spinner_tid = 0; | 96 pthread_t spinner_tid = 0; 118 r_status_t status = R_SUCCESS; | 97 r_status_t status = R_SUCCESS; 119 -   | 120 *response = NULL; | 98 *response = NULL; 121 -   | 122 bool actually_show_spinner = client->show_spinner && isatty(STDERR_FILENO); | 99 bool actually_show_spinner = client->show_spinner && isatty(STDERR_FILENO); 123 -   | 124 if (actually_show_spinner) { | 100 if (actually_show_spinner) { 125 clock_gettime(CLOCK_MONOTONIC, &spinner_start_time); | 101 clock_gettime(CLOCK_MONOTONIC, &spinner_start_time); 126 spinner_running = 1; | 102 spinner_running = 1; 127 pthread_create(&spinner_tid, NULL, spinner_thread, NULL); | 103 pthread_create(&spinner_tid, NULL, spinner_thread, NULL); 128 } | 104 } 129 -   | 130 while (retry_count < HTTP_MAX_RETRIES) { | 105 while (retry_count < HTTP_MAX_RETRIES) { 131 free(resp.data); | 106 free(resp.data); 132 resp.data = malloc(1); | 107 resp.data = malloc(1); 133 resp.size = 0; | 108 resp.size = 0; 134 -   | 135 if (!resp.data) { | 109 if (!resp.data) { 136 status = R_ERROR_OUT_OF_MEMORY; | 110 status = R_ERROR_OUT_OF_MEMORY; 137 goto cleanup; | 111 goto cleanup; 138 } | 112 } 139 resp.data[0] = '\0'; | 113 resp.data[0] = '\0'; 140 -   | 141 curl = curl_easy_init(); | 114 curl = curl_easy_init(); 142 if (!curl) { | 115 if (!curl) { 143 status = R_ERROR_HTTP_CONNECTION; | 116 status = R_ERROR_HTTP_CONNECTION; 144 goto cleanup; | 117 goto cleanup; 145 } | 118 } 146 -   | 147 curl_easy_setopt(curl, CURLOPT_URL, url); | 119 curl_easy_setopt(curl, CURLOPT_URL, url); 148 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, client->connect_timeout_seconds); | 120 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, client->connect_timeout_seconds); 149 curl_easy_setopt(curl, CURLOPT_TIMEOUT, client->timeout_seconds); | 121 curl_easy_setopt(curl, CURLOPT_TIMEOUT, client->timeout_seconds); 150 -   | 151 headers = curl_slist_append(headers, "Content-Type: application/json"); | 122 headers = curl_slist_append(headers, "Content-Type: application/json"); 152 if (client->bearer_token) { | 123 if (client->bearer_token) { 153 char bearer_header[2048]; | 124 char bearer_header[2048]; 154 snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s", | 125 snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s", 155 client->bearer_token); | 126 client->bearer_token); 156 headers = curl_slist_append(headers, bearer_header); | 127 headers = curl_slist_append(headers, bearer_header); 157 } | 128 } 158 -   | 159 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); | 129 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 160 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); | 130 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); 161 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); | 131 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); 162 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&resp); | 132 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&resp); 163 -   | 164 CURLcode res = curl_easy_perform(curl); | 133 CURLcode res = curl_easy_perform(curl); 165 -   | 166 curl_slist_free_all(headers); | 134 curl_slist_free_all(headers); 167 headers = NULL; | 135 headers = NULL; 168 curl_easy_cleanup(curl); | 136 curl_easy_cleanup(curl); 169 curl = NULL; | 137 curl = NULL; 170 -   | 171 if (res == CURLE_OK) { | 138 if (res == CURLE_OK) { 172 *response = resp.data; | 139 *response = resp.data; 173 resp.data = NULL; | 140 resp.data = NULL; 174 status = R_SUCCESS; | 141 status = R_SUCCESS; 175 goto cleanup; | 142 goto cleanup; 176 } | 143 } 177 -   | 178 retry_count++; | 144 retry_count++; 179 -   | 180 if (actually_show_spinner) { | 145 if (actually_show_spinner) { 181 spinner_running = 0; | 146 spinner_running = 0; 182 pthread_join(spinner_tid, NULL); | 147 pthread_join(spinner_tid, NULL); 183 spinner_tid = 0; | 148 spinner_tid = 0; 184 fprintf(stderr, "\r \r"); | 149 fprintf(stderr, "\r \r"); 185 } | 150 } 186 -   | 187 fprintf(stderr, "Network error: %s (attempt %d/%d)\n", | 151 fprintf(stderr, "Network error: %s (attempt %d/%d)\n", 188 curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES); | 152 curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES); 189 -   | 190 if (retry_count < HTTP_MAX_RETRIES) { | 153 if (retry_count < HTTP_MAX_RETRIES) { 191 fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000); | 154 fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000); 192 usleep(HTTP_RETRY_DELAY_MS * 1000); | 155 usleep(HTTP_RETRY_DELAY_MS * 1000); 193 -   | 194 if (actually_show_spinner) { | 156 if (actually_show_spinner) { 195 clock_gettime(CLOCK_MONOTONIC, &spinner_start_time); | 157 clock_gettime(CLOCK_MONOTONIC, &spinner_start_time); 196 spinner_running = 1; | 158 spinner_running = 1; 197 pthread_create(&spinner_tid, NULL, spinner_thread, NULL); | 159 pthread_create(&spinner_tid, NULL, spinner_thread, NULL); 198 } | 160 } 199 } | 161 } 200 } | 162 } 201 -   | 202 status = R_ERROR_HTTP_TIMEOUT; | 163 status = R_ERROR_HTTP_TIMEOUT; 203 -   | 204 cleanup: | 164 cleanup: 205 if (actually_show_spinner && spinner_tid) { | 165 if (actually_show_spinner && spinner_tid) { 206 spinner_running = 0; | 166 spinner_running = 0; 207 pthread_join(spinner_tid, NULL); | 167 pthread_join(spinner_tid, NULL); 208 fprintf(stderr, "\r \r"); | 168 fprintf(stderr, "\r \r"); 209 fflush(stderr); | 169 fflush(stderr); 210 } | 170 } 211 -   | 212 if (headers) curl_slist_free_all(headers); | 171 if (headers) curl_slist_free_all(headers); 213 if (curl) curl_easy_cleanup(curl); | 172 if (curl) curl_easy_cleanup(curl); 214 free(resp.data); | 173 free(resp.data); 215 -   | 216 return status; | 174 return status; 217 } | 175 } 218 -   | 219 r_status_t http_get(http_client_handle client, const char *url, char **response) { | 176 r_status_t http_get(http_client_handle client, const char *url, char **response) { 220 if (!client || !url || !response) return R_ERROR_INVALID_ARG; | 177 if (!client || !url || !response) return R_ERROR_INVALID_ARG; 221 -   | 222 CURL *curl = NULL; | 178 CURL *curl = NULL; 223 struct curl_slist *headers = NULL; | 179 struct curl_slist *headers = NULL; 224 struct response_buffer_t resp = {NULL, 0}; | 180 struct response_buffer_t resp = {NULL, 0}; 225 int retry_count = 0; | 181 int retry_count = 0; 226 r_status_t status = R_SUCCESS; | 182 r_status_t status = R_SUCCESS; 227 -   | 228 *response = NULL; | 183 *response = NULL; 229 -   | 230 while (retry_count < HTTP_MAX_RETRIES) { | 184 while (retry_count < HTTP_MAX_RETRIES) { 231 free(resp.data); | 185 free(resp.data); 232 resp.data = malloc(1); | 186 resp.data = malloc(1); 233 resp.size = 0; | 187 resp.size = 0; 234 -   | 235 if (!resp.data) { | 188 if (!resp.data) { 236 status = R_ERROR_OUT_OF_MEMORY; | 189 status = R_ERROR_OUT_OF_MEMORY; 237 goto cleanup; | 190 goto cleanup; 238 } | 191 } 239 resp.data[0] = '\0'; | 192 resp.data[0] = '\0'; 240 -   | 241 curl = curl_easy_init(); | 193 curl = curl_easy_init(); 242 if (!curl) { | 194 if (!curl) { 243 status = R_ERROR_HTTP_CONNECTION; | 195 status = R_ERROR_HTTP_CONNECTION; 244 goto cleanup; | 196 goto cleanup; 245 } | 197 } 246 -   | 247 curl_easy_setopt(curl, CURLOPT_URL, url); | 198 curl_easy_setopt(curl, CURLOPT_URL, url); 248 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, client->connect_timeout_seconds); | 199 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, client->connect_timeout_seconds); 249 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L); | 200 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L); 250 -   | 251 headers = curl_slist_append(headers, "Content-Type: application/json"); | 201 headers = curl_slist_append(headers, "Content-Type: application/json"); 252 if (client->bearer_token) { | 202 if (client->bearer_token) { 253 char bearer_header[2048]; | 203 char bearer_header[2048]; 254 snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s", | 204 snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s", 255 client->bearer_token); | 205 client->bearer_token); 256 headers = curl_slist_append(headers, bearer_header); | 206 headers = curl_slist_append(headers, bearer_header); 257 } | 207 } 258 -   | 259 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); | 208 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 260 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); | 209 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); 261 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&resp); | 210 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&resp); 262 -   | 263 CURLcode res = curl_easy_perform(curl); | 211 CURLcode res = curl_easy_perform(curl); 264 -   | 265 curl_slist_free_all(headers); | 212 curl_slist_free_all(headers); 266 headers = NULL; | 213 headers = NULL; 267 curl_easy_cleanup(curl); | 214 curl_easy_cleanup(curl); 268 curl = NULL; | 215 curl = NULL; 269 -   | 270 if (res == CURLE_OK) { | 216 if (res == CURLE_OK) { 271 *response = resp.data; | 217 *response = resp.data; 272 resp.data = NULL; | 218 resp.data = NULL; 273 status = R_SUCCESS; | 219 status = R_SUCCESS; 274 goto cleanup; | 220 goto cleanup; 275 } | 221 } 276 -   | 277 retry_count++; | 222 retry_count++; 278 fprintf(stderr, "Network error: %s (attempt %d/%d)\n", | 223 fprintf(stderr, "Network error: %s (attempt %d/%d)\n", 279 curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES); | 224 curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES); 280 -   | 281 if (retry_count < HTTP_MAX_RETRIES) { | 225 if (retry_count < HTTP_MAX_RETRIES) { 282 fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000); | 226 fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000); 283 usleep(HTTP_RETRY_DELAY_MS * 1000); | 227 usleep(HTTP_RETRY_DELAY_MS * 1000); 284 } | 228 } 285 } | 229 } 286 -   | 287 status = R_ERROR_HTTP_TIMEOUT; | 230 status = R_ERROR_HTTP_TIMEOUT; 288 -   | 289 cleanup: | 231 cleanup: 290 if (headers) curl_slist_free_all(headers); | 232 if (headers) curl_slist_free_all(headers); 291 if (curl) curl_easy_cleanup(curl); | 233 if (curl) curl_easy_cleanup(curl); 292 free(resp.data); | 234 free(resp.data); 293 -   | 294 return status; | 235 return status; 295 } | 236 } 296 -   | 297 r_status_t http_post_simple(const char *url, const char *bearer_token, | 237 r_status_t http_post_simple(const char *url, const char *bearer_token, 298 const char *data, char **response) { | 238 const char *data, char **response) { 299 http_client_handle client = http_client_create(bearer_token); | 239 http_client_handle client = http_client_create(bearer_token); 300 if (!client) return R_ERROR_OUT_OF_MEMORY; | 240 if (!client) return R_ERROR_OUT_OF_MEMORY; 301 -   | 302 r_status_t status = http_post(client, url, data, response); | 241 r_status_t status = http_post(client, url, data, response); 303 http_client_destroy(client); | 242 http_client_destroy(client); 304 return status; | 243 return status; 305 } | 244 } 306 -   | 307 r_status_t http_get_simple(const char *url, const char *bearer_token, char **response) { | 245 r_status_t http_get_simple(const char *url, const char *bearer_token, char **response) { 308 http_client_handle client = http_client_create(bearer_token); | 246 http_client_handle client = http_client_create(bearer_token); 309 if (!client) return R_ERROR_OUT_OF_MEMORY; | 247 if (!client) return R_ERROR_OUT_OF_MEMORY; 310 -   | 311 http_client_set_show_spinner(client, false); | 248 http_client_set_show_spinner(client, false); 312 r_status_t status = http_get(client, url, response); | 249 r_status_t status = http_get(client, url, response); 313 http_client_destroy(client); | 250 http_client_destroy(client); 314 return status; | 251 return status; 315 } | 252 } -> Replacing lines in: src/json_repair.c   CHANGES: src/json_repair.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "json_repair.h" | 2 #include "json_repair.h" 4 #include | 3 #include 5 #include | 4 #include 6 #include | 5 #include 7 #include | 6 #include 8 #include | 7 #include 9 -   | 10 static char *strip_comments(const char *src) { | 8 static char *strip_comments(const char *src) { 11 if (!src) return NULL; | 9 if (!src) return NULL; 12 size_t len = strlen(src); | 10 size_t len = strlen(src); 13 char *result = malloc(len + 1); | 11 char *result = malloc(len + 1); 14 if (!result) return NULL; | 12 if (!result) return NULL; 15 -   | 16 char *dst = result; | 13 char *dst = result; 17 const char *p = src; | 14 const char *p = src; 18 bool in_string = false; | 15 bool in_string = false; 19 bool escaped = false; | 16 bool escaped = false; 20 -   | 21 while (*p) { | 17 while (*p) { 22 if (escaped) { | 18 if (escaped) { 23 *dst++ = *p++; | 19 *dst++ = *p++; 24 escaped = false; | 20 escaped = false; 25 continue; | 21 continue; 26 } | 22 } 27 -   | 28 if (*p == '\\') { | 23 if (*p == '\\') { 29 *dst++ = *p++; | 24 *dst++ = *p++; 30 escaped = true; | 25 escaped = true; 31 continue; | 26 continue; 32 } | 27 } 33 -   | 34 if (*p == '"') { | 28 if (*p == '"') { 35 in_string = !in_string; | 29 in_string = !in_string; 36 *dst++ = *p++; | 30 *dst++ = *p++; 37 continue; | 31 continue; 38 } | 32 } 39 -   | 40 if (!in_string) { | 33 if (!in_string) { 41 if (*p == '/' && *(p + 1) == '/') { | 34 if (*p == '/' && *(p + 1) == '/') { 42 while (*p && *p != '\n') p++; | 35 while (*p && *p != '\n') p++; 43 continue; | 36 continue; 44 } | 37 } 45 if (*p == '/' && *(p + 1) == '*') { | 38 if (*p == '/' && *(p + 1) == '*') { 46 p += 2; | 39 p += 2; 47 while (*p && !(*p == '*' && *(p + 1) == '/')) p++; | 40 while (*p && !(*p == '*' && *(p + 1) == '/')) p++; 48 if (*p) p += 2; | 41 if (*p) p += 2; 49 continue; | 42 continue; 50 } | 43 } 51 if (*p == '#') { | 44 if (*p == '#') { 52 while (*p && *p != '\n') p++; | 45 while (*p && *p != '\n') p++; 53 continue; | 46 continue; 54 } | 47 } 55 } | 48 } 56 -   | 57 *dst++ = *p++; | 49 *dst++ = *p++; 58 } | 50 } 59 *dst = '\0'; | 51 *dst = '\0'; 60 return result; | 52 return result; 61 } | 53 } 62 -   | 63 static char *normalize_quotes(const char *src) { | 54 static char *normalize_quotes(const char *src) { 64 if (!src) return NULL; | 55 if (!src) return NULL; 65 size_t len = strlen(src); | 56 size_t len = strlen(src); 66 // Over-allocate because single quotes might be replaced by double quotes + escaping | 57 // Over-allocate because single quotes might be replaced by double quotes + escaping 67 char *result = malloc(len * 2 + 1); | 58 char *result = malloc(len * 2 + 1); 68 if (!result) return NULL; | 59 if (!result) return NULL; 69 -   | 70 char *dst = result; | 60 char *dst = result; 71 const char *p = src; | 61 const char *p = src; 72 bool in_double_string = false; | 62 bool in_double_string = false; 73 bool escaped = false; | 63 bool escaped = false; 74 -   | 75 while (*p) { | 64 while (*p) { 76 // Smart quote replacement | 65 // Smart quote replacement 77 if ((unsigned char)*p == 0xE2 && (unsigned char)*(p+1) == 0x80) { | 66 if ((unsigned char)*p == 0xE2 && (unsigned char)*(p+1) == 0x80) { 78 if ((unsigned char)*(p+2) == 0x9C || (unsigned char)*(p+2) == 0x9D) { // “ or ” | 67 if ((unsigned char)*(p+2) == 0x9C || (unsigned char)*(p+2) == 0x9D) { // “ or ” 79 *dst++ = '"'; | 68 *dst++ = '"'; 80 p += 3; | 69 p += 3; 81 continue; | 70 continue; 82 } | 71 } 83 if ((unsigned char)*(p+2) == 0x98 || (unsigned char)*(p+2) == 0x99) { // ‘ or ’ | 72 if ((unsigned char)*(p+2) == 0x98 || (unsigned char)*(p+2) == 0x99) { // ‘ or ’ 84 *dst++ = '\''; | 73 *dst++ = '\''; 85 p += 3; | 74 p += 3; 86 continue; | 75 continue; 87 } | 76 } 88 } | 77 } 89 -   | 90 if (escaped) { | 78 if (escaped) { 91 *dst++ = *p++; | 79 *dst++ = *p++; 92 escaped = false; | 80 escaped = false; 93 continue; | 81 continue; 94 } | 82 } 95 -   | 96 if (*p == '\\') { | 83 if (*p == '\\') { 97 *dst++ = *p++; | 84 *dst++ = *p++; 98 escaped = true; | 85 escaped = true; 99 continue; | 86 continue; 100 } | 87 } 101 -   | 102 if (*p == '"') { | 88 if (*p == '"') { 103 in_double_string = !in_double_string; | 89 in_double_string = !in_double_string; 104 *dst++ = *p++; | 90 *dst++ = *p++; 105 continue; | 91 continue; 106 } | 92 } 107 -   | 108 if (!in_double_string && *p == '\'') { | 93 if (!in_double_string && *p == '\'') { 109 // Heuristic: convert '...' to "..." | 94 // Heuristic: convert '...' to "..." 110 *dst++ = '"'; | 95 *dst++ = '"'; 111 p++; | 96 p++; 112 while (*p && *p != '\'') { | 97 while (*p && *p != '\'') { 113 if (*p == '\\' && *(p+1)) { | 98 if (*p == '\\' && *(p+1)) { 114 *dst++ = *p++; | 99 *dst++ = *p++; 115 *dst++ = *p++; | 100 *dst++ = *p++; 116 } else if (*p == '"') { | 101 } else if (*p == '"') { 117 *dst++ = '\\'; | 102 *dst++ = '\\'; 118 *dst++ = '"'; | 103 *dst++ = '"'; 119 p++; | 104 p++; 120 } else { | 105 } else { 121 *dst++ = *p++; | 106 *dst++ = *p++; 122 } | 107 } 123 } | 108 } 124 if (*p == '\'') { | 109 if (*p == '\'') { 125 *dst++ = '"'; | 110 *dst++ = '"'; 126 p++; | 111 p++; 127 } | 112 } 128 continue; | 113 continue; 129 } | 114 } 130 -   | 131 *dst++ = *p++; | 115 *dst++ = *p++; 132 } | 116 } 133 *dst = '\0'; | 117 *dst = '\0'; 134 return result; | 118 return result; 135 } | 119 } 136 -   | 137 static char *remove_trailing_commas(const char *src) { | 120 static char *remove_trailing_commas(const char *src) { 138 if (!src) return NULL; | 121 if (!src) return NULL; 139 size_t len = strlen(src); | 122 size_t len = strlen(src); 140 char *result = malloc(len + 1); | 123 char *result = malloc(len + 1); 141 if (!result) return NULL; | 124 if (!result) return NULL; 142 -   | 143 char *dst = result; | 125 char *dst = result; 144 const char *p = src; | 126 const char *p = src; 145 bool in_string = false; | 127 bool in_string = false; 146 bool escaped = false; | 128 bool escaped = false; 147 -   | 148 while (*p) { | 129 while (*p) { 149 if (escaped) { | 130 if (escaped) { 150 *dst++ = *p++; | 131 *dst++ = *p++; 151 escaped = false; | 132 escaped = false; 152 continue; | 133 continue; 153 } | 134 } 154 if (*p == '\\') { | 135 if (*p == '\\') { 155 *dst++ = *p++; | 136 *dst++ = *p++; 156 escaped = true; | 137 escaped = true; 157 continue; | 138 continue; 158 } | 139 } 159 if (*p == '"') { | 140 if (*p == '"') { 160 in_string = !in_string; | 141 in_string = !in_string; 161 *dst++ = *p++; | 142 *dst++ = *p++; 162 continue; | 143 continue; 163 } | 144 } 164 -   | 165 if (!in_string && *p == ',') { | 145 if (!in_string && *p == ',') { 166 // Check if next non-ws char is ] or } | 146 // Check if next non-ws char is ] or } 167 const char *next = p + 1; | 147 const char *next = p + 1; 168 while (*next && isspace((unsigned char)*next)) next++; | 148 while (*next && isspace((unsigned char)*next)) next++; 169 if (*next == ']' || *next == '}') { | 149 if (*next == ']' || *next == '}') { 170 p = next; // Skip the comma | 150 p = next; // Skip the comma 171 continue; | 151 continue; 172 } | 152 } 173 } | 153 } 174 *dst++ = *p++; | 154 *dst++ = *p++; 175 } | 155 } 176 *dst = '\0'; | 156 *dst = '\0'; 177 return result; | 157 return result; 178 } | 158 } 179 -   | 180 static char *quote_unquoted_keys(const char *src) { | 159 static char *quote_unquoted_keys(const char *src) { 181 if (!src) return NULL; | 160 if (!src) return NULL; 182 size_t len = strlen(src); | 161 size_t len = strlen(src); 183 char *result = malloc(len * 2 + 1); | 162 char *result = malloc(len * 2 + 1); 184 if (!result) return NULL; | 163 if (!result) return NULL; 185 -   | 186 char *dst = result; | 164 char *dst = result; 187 const char *p = src; | 165 const char *p = src; 188 bool in_string = false; | 166 bool in_string = false; 189 bool escaped = false; | 167 bool escaped = false; 190 -   | 191 while (*p) { | 168 while (*p) { 192 if (escaped) { | 169 if (escaped) { 193 *dst++ = *p++; | 170 *dst++ = *p++; 194 escaped = false; | 171 escaped = false; 195 continue; | 172 continue; 196 } | 173 } 197 if (*p == '\\') { | 174 if (*p == '\\') { 198 *dst++ = *p++; | 175 *dst++ = *p++; 199 escaped = true; | 176 escaped = true; 200 continue; | 177 continue; 201 } | 178 } 202 if (*p == '"') { | 179 if (*p == '"') { 203 in_string = !in_string; | 180 in_string = !in_string; 204 *dst++ = *p++; | 181 *dst++ = *p++; 205 continue; | 182 continue; 206 } | 183 } 207 -   | 208 if (!in_string && (isalnum((unsigned char)*p) || *p == '_' || *p == '-')) { | 184 if (!in_string && (isalnum((unsigned char)*p) || *p == '_' || *p == '-')) { 209 // Potential unquoted key? | 185 // Potential unquoted key? 210 // A key usually follows '{' or ',' and is followed by ':' | 186 // A key usually follows '{' or ',' and is followed by ':' 211 // Heuristic: if we are at start of an identifier, check if it ends with ':' | 187 // Heuristic: if we are at start of an identifier, check if it ends with ':' 212 | 188 213 // Check backwards for { or , | 189 // Check backwards for { or , 214 const char *prev = p - 1; | 190 const char *prev = p - 1; 215 while (prev >= src && isspace((unsigned char)*prev)) prev--; | 191 while (prev >= src && isspace((unsigned char)*prev)) prev--; 216 | 192 217 if (prev >= src && (*prev == '{' || *prev == ',')) { | 193 if (prev >= src && (*prev == '{' || *prev == ',')) { 218 const char *end = p; | 194 const char *end = p; 219 while (*end && (isalnum((unsigned char)*end) || *end == '_' || *end == '-')) end++; | 195 while (*end && (isalnum((unsigned char)*end) || *end == '_' || *end == '-')) end++; 220 const char *after = end; | 196 const char *after = end; 221 while (*after && isspace((unsigned char)*after)) after++; | 197 while (*after && isspace((unsigned char)*after)) after++; 222 | 198 223 if (*after == ':') { | 199 if (*after == ':') { 224 // It is an unquoted key! | 200 // It is an unquoted key! 225 *dst++ = '"'; | 201 *dst++ = '"'; 226 while (p < end) *dst++ = *p++; | 202 while (p < end) *dst++ = *p++; 227 *dst++ = '"'; | 203 *dst++ = '"'; 228 continue; | 204 continue; 229 } | 205 } 230 } | 206 } 231 } | 207 } 232 *dst++ = *p++; | 208 *dst++ = *p++; 233 } | 209 } 234 *dst = '\0'; | 210 *dst = '\0'; 235 return result; | 211 return result; 236 } | 212 } 237 -   | 238 static char *balance_brackets(const char *src) { | 213 static char *balance_brackets(const char *src) { 239 if (!src) return NULL; | 214 if (!src) return NULL; 240 size_t len = strlen(src); | 215 size_t len = strlen(src); 241 char *result = malloc(len + 1024); | 216 char *result = malloc(len + 1024); 242 if (!result) return NULL; | 217 if (!result) return NULL; 243 -   | 244 char stack[1024]; | 218 char stack[1024]; 245 int top = 0; | 219 int top = 0; 246 | 220 247 char *dst = result; | 221 char *dst = result; 248 const char *p = src; | 222 const char *p = src; 249 bool in_string = false; | 223 bool in_string = false; 250 bool escaped = false; | 224 bool escaped = false; 251 -   | 252 while (*p) { | 225 while (*p) { 253 if (escaped) { | 226 if (escaped) { 254 *dst++ = *p++; | 227 *dst++ = *p++; 255 escaped = false; | 228 escaped = false; 256 continue; | 229 continue; 257 } | 230 } 258 if (*p == '\\') { | 231 if (*p == '\\') { 259 *dst++ = *p++; | 232 *dst++ = *p++; 260 escaped = true; | 233 escaped = true; 261 continue; | 234 continue; 262 } | 235 } 263 if (*p == '"') { | 236 if (*p == '"') { 264 in_string = !in_string; | 237 in_string = !in_string; 265 *dst++ = *p++; | 238 *dst++ = *p++; 266 continue; | 239 continue; 267 } | 240 } 268 -   | 269 if (!in_string) { | 241 if (!in_string) { 270 if (*p == '{' || *p == '[') { | 242 if (*p == '{' || *p == '[') { 271 if (top < 1024) stack[top++] = *p; | 243 if (top < 1024) stack[top++] = *p; 272 } else if (*p == '}' || *p == ']') { | 244 } else if (*p == '}' || *p == ']') { 273 if (top > 0) { | 245 if (top > 0) { 274 char expected = (*p == '}') ? '{' : '['; | 246 char expected = (*p == '}') ? '{' : '['; 275 if (stack[top - 1] == expected) { | 247 if (stack[top - 1] == expected) { 276 top--; | 248 top--; 277 } | 249 } 278 } else { | 250 } else { 279 // Mismatched closing; skip it | 251 // Mismatched closing; skip it 280 p++; | 252 p++; 281 continue; | 253 continue; 282 } | 254 } 283 } | 255 } 284 } | 256 } 285 *dst++ = *p++; | 257 *dst++ = *p++; 286 } | 258 } 287 -   | 288 while (top > 0) { | 259 while (top > 0) { 289 char opener = stack[--top]; | 260 char opener = stack[--top]; 290 *dst++ = (opener == '{') ? '}' : ']'; | 261 *dst++ = (opener == '{') ? '}' : ']'; 291 } | 262 } 292 *dst = '\0'; | 263 *dst = '\0'; 293 return result; | 264 return result; 294 } | 265 } 295 -   | 296 static char *compact_json(const char *src) { | 266 static char *compact_json(const char *src) { 297 if (!src) return NULL; | 267 if (!src) return NULL; 298 size_t len = strlen(src); | 268 size_t len = strlen(src); 299 char *result = malloc(len + 1); | 269 char *result = malloc(len + 1); 300 if (!result) return NULL; | 270 if (!result) return NULL; 301 -   | 302 char *dst = result; | 271 char *dst = result; 303 const char *p = src; | 272 const char *p = src; 304 bool in_string = false; | 273 bool in_string = false; 305 bool escaped = false; | 274 bool escaped = false; 306 -   | 307 while (*p) { | 275 while (*p) { 308 if (escaped) { | 276 if (escaped) { 309 *dst++ = *p++; | 277 *dst++ = *p++; 310 escaped = false; | 278 escaped = false; 311 continue; | 279 continue; 312 } | 280 } 313 if (*p == '\\') { | 281 if (*p == '\\') { 314 *dst++ = *p++; | 282 *dst++ = *p++; 315 escaped = true; | 283 escaped = true; 316 continue; | 284 continue; 317 } | 285 } 318 if (*p == '"') { | 286 if (*p == '"') { 319 in_string = !in_string; | 287 in_string = !in_string; 320 *dst++ = *p++; | 288 *dst++ = *p++; 321 continue; | 289 continue; 322 } | 290 } 323 -   | 324 if (!in_string && isspace((unsigned char)*p)) { | 291 if (!in_string && isspace((unsigned char)*p)) { 325 p++; | 292 p++; 326 continue; | 293 continue; 327 } | 294 } 328 *dst++ = *p++; | 295 *dst++ = *p++; 329 } | 296 } 330 *dst = '\0'; | 297 *dst = '\0'; 331 return result; | 298 return result; 332 } | 299 } 333 -   | 334 char *json_repair_string(const char *src) { | 300 char *json_repair_string(const char *src) { 335 if (!src) return NULL; | 301 if (!src) return NULL; 336 -   | 337 // Find the first occurrence of { or [ | 302 // Find the first occurrence of { or [ 338 const char *start_ptr = src; | 303 const char *start_ptr = src; 339 while (*start_ptr && *start_ptr != '{' && *start_ptr != '[') start_ptr++; | 304 while (*start_ptr && *start_ptr != '{' && *start_ptr != '[') start_ptr++; 340 if (!*start_ptr) return strdup(src); // No JSON structure found, return as is | 305 if (!*start_ptr) return strdup(src); // No JSON structure found, return as is 341 -   | 342 char *s1 = strip_comments(start_ptr); | 306 char *s1 = strip_comments(start_ptr); 343 char *s2 = normalize_quotes(s1); | 307 char *s2 = normalize_quotes(s1); 344 free(s1); | 308 free(s1); 345 char *s3 = quote_unquoted_keys(s2); | 309 char *s3 = quote_unquoted_keys(s2); 346 free(s2); | 310 free(s2); 347 char *s4 = remove_trailing_commas(s3); | 311 char *s4 = remove_trailing_commas(s3); 348 free(s3); | 312 free(s3); 349 char *s5 = balance_brackets(s4); | 313 char *s5 = balance_brackets(s4); 350 free(s4); | 314 free(s4); 351 -   | 352 // Heuristic: truncate after the first complete object/array | 315 // Heuristic: truncate after the first complete object/array 353 int depth = 0; | 316 int depth = 0; 354 bool in_str = false; | 317 bool in_str = false; 355 bool esc = false; | 318 bool esc = false; 356 char *p = s5; | 319 char *p = s5; 357 while (*p) { | 320 while (*p) { 358 if (esc) { esc = false; } | 321 if (esc) { esc = false; } 359 else if (*p == '\\') { esc = true; } | 322 else if (*p == '\\') { esc = true; } 360 else if (*p == '"') { in_str = !in_str; } | 323 else if (*p == '"') { in_str = !in_str; } 361 else if (!in_str) { | 324 else if (!in_str) { 362 if (*p == '{' || *p == '[') depth++; | 325 if (*p == '{' || *p == '[') depth++; 363 else if (*p == '}' || *p == ']') { | 326 else if (*p == '}' || *p == ']') { 364 depth--; | 327 depth--; 365 if (depth == 0) { | 328 if (depth == 0) { 366 *(p + 1) = '\0'; | 329 *(p + 1) = '\0'; 367 break; | 330 break; 368 } | 331 } 369 } | 332 } 370 } | 333 } 371 p++; | 334 p++; 372 } | 335 } 373 -   | 374 char *s6 = compact_json(s5); | 336 char *s6 = compact_json(s5); 375 free(s5); | 337 free(s5); 376 -   | 377 return s6; | 338 return s6; 378 } | 339 } -> Replacing lines in: src/main.c   CHANGES: src/main.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "agent.h" | 2 #include "agent.h" 4 #include "db.h" | 3 #include "db.h" 5 #include "http_client.h" | 4 #include "http_client.h" 6 #include "r_config.h" | 5 #include "r_config.h" 7 #include "r_error.h" | 6 #include "r_error.h" 8 #include "tool.h" | 7 #include "tool.h" 9 -   | 10 #include "line.h" | 8 #include "line.h" 11 #include "markdown.h" | 9 #include "markdown.h" 12 #include "utils.h" | 10 #include "utils.h" 13 -   | 14 #include | 11 #include 15 #include | 12 #include 16 #include | 13 #include 17 #include | 14 #include 18 #include | 15 #include 19 #include | 16 #include 20 #include | 17 #include 21 #include | 18 #include 22 #include | 19 #include 23 #include | 20 #include 24 -   | 25 static volatile sig_atomic_t sigint_count = 0; | 21 static volatile sig_atomic_t sigint_count = 0; 26 static time_t first_sigint_time = 0; | 22 static time_t first_sigint_time = 0; 27 static bool syntax_highlight_enabled = true; | 23 static bool syntax_highlight_enabled = true; 28 static bool api_mode = false; | 24 static bool api_mode = false; 29 -   | 30 static db_handle global_db = NULL; | 25 static db_handle global_db = NULL; 31 static messages_handle global_messages = NULL; | 26 static messages_handle global_messages = NULL; 32 -   | 33 extern tool_registry_t *tools_get_registry(void); | 27 extern tool_registry_t *tools_get_registry(void); 34 extern void tools_registry_shutdown(void); | 28 extern void tools_registry_shutdown(void); 35 -   | 36 static bool include_file(const char *path); | 29 static bool include_file(const char *path); 37 static char *get_prompt_from_stdin(char *prompt); | 30 static char *get_prompt_from_stdin(char *prompt); 38 static char *get_prompt_from_args(int argc, char **argv); | 31 static char *get_prompt_from_args(int argc, char **argv); 39 static bool try_prompt(int argc, char *argv[]); | 32 static bool try_prompt(int argc, char *argv[]); 40 static void repl(void); | 33 static void repl(void); 41 static void init(void); | 34 static void init(void); 42 static void cleanup(void); | 35 static void cleanup(void); 43 static void handle_sigint(int sig); | 36 static void handle_sigint(int sig); 44 -   | 45 static char *get_env_string(void) { | 37 static char *get_env_string(void) { 46 FILE *fp = popen("env", "r"); | 38 FILE *fp = popen("env", "r"); 47 if (!fp) | 39 if (!fp) 48 return NULL; | 40 return NULL; 49 -   | 50 size_t buffer_size = 1024; | 41 size_t buffer_size = 1024; 51 size_t total_size = 0; | 42 size_t total_size = 0; 52 char *output = malloc(buffer_size); | 43 char *output = malloc(buffer_size); 53 if (!output) { | 44 if (!output) { 54 pclose(fp); | 45 pclose(fp); 55 return NULL; | 46 return NULL; 56 } | 47 } 57 -   | 58 size_t bytes_read; | 48 size_t bytes_read; 59 while ((bytes_read = fread(output + total_size, 1, buffer_size - total_size, | 49 while ((bytes_read = fread(output + total_size, 1, buffer_size - total_size, 60 fp)) > 0) { | 50 fp)) > 0) { 61 total_size += bytes_read; | 51 total_size += bytes_read; 62 if (total_size >= buffer_size) { | 52 if (total_size >= buffer_size) { 63 buffer_size *= 2; | 53 buffer_size *= 2; 64 char *temp = realloc(output, buffer_size); | 54 char *temp = realloc(output, buffer_size); 65 if (!temp) { | 55 if (!temp) { 66 free(output); | 56 free(output); 67 pclose(fp); | 57 pclose(fp); 68 return NULL; | 58 return NULL; 69 } | 59 } 70 output = temp; | 60 output = temp; 71 } | 61 } 72 } | 62 } 73 -   | 74 output[total_size] = '\0'; | 63 output[total_size] = '\0'; 75 pclose(fp); | 64 pclose(fp); 76 return output; | 65 return output; 77 } | 66 } 78 -   | 79 static char *get_prompt_from_stdin(char *prompt) { | 67 static char *get_prompt_from_stdin(char *prompt) { 80 int index = 0; | 68 int index = 0; 81 int c; | 69 int c; 82 while ((c = getchar()) != EOF) { | 70 while ((c = getchar()) != EOF) { 83 prompt[index++] = (char)c; | 71 prompt[index++] = (char)c; 84 } | 72 } 85 prompt[index] = '\0'; | 73 prompt[index] = '\0'; 86 return prompt; | 74 return prompt; 87 } | 75 } 88 -   | 89 static char *get_prompt_from_args(int argc, char **argv) { | 76 static char *get_prompt_from_args(int argc, char **argv) { 90 r_config_handle cfg = r_config_get_instance(); | 77 r_config_handle cfg = r_config_get_instance(); 91 -   | 92 char *prompt = malloc(10 * 1024 * 1024 + 1); | 78 char *prompt = malloc(10 * 1024 * 1024 + 1); 93 char *system_msg = malloc(1024 * 1024); | 79 char *system_msg = malloc(1024 * 1024); 94 if (!prompt || !system_msg) { | 80 if (!prompt || !system_msg) { 95 free(prompt); | 81 free(prompt); 96 free(system_msg); | 82 free(system_msg); 97 return NULL; | 83 return NULL; 98 } | 84 } 99 -   | 100 system_msg[0] = '\0'; | 85 system_msg[0] = '\0'; 101 bool get_from_stdin = false; | 86 bool get_from_stdin = false; 102 -   | 103 for (int i = 1; i < argc; i++) { | 87 for (int i = 1; i < argc; i++) { 104 if (strcmp(argv[i], "--stdin") == 0) { | 88 if (strcmp(argv[i], "--stdin") == 0) { 105 fprintf(stderr, "Reading from stdin.\n"); | 89 fprintf(stderr, "Reading from stdin.\n"); 106 get_from_stdin = true; | 90 get_from_stdin = true; 107 } else if (strcmp(argv[i], "--verbose") == 0) { | 91 } else if (strcmp(argv[i], "--verbose") == 0) { 108 r_config_set_verbose(cfg, true); | 92 r_config_set_verbose(cfg, true); 109 } else if (strcmp(argv[i], "--py") == 0 && i + 1 < argc) { | 93 } else if (strcmp(argv[i], "--py") == 0 && i + 1 < argc) { 110 char *py_file_path = expand_home_directory(argv[++i]); | 94 char *py_file_path = expand_home_directory(argv[++i]); 111 fprintf(stderr, "Including \"%s\".\n", py_file_path); | 95 fprintf(stderr, "Including \"%s\".\n", py_file_path); 112 include_file(py_file_path); | 96 include_file(py_file_path); 113 free(py_file_path); | 97 free(py_file_path); 114 } else if (strcmp(argv[i], "--context") == 0 && i + 1 < argc) { | 98 } else if (strcmp(argv[i], "--context") == 0 && i + 1 < argc) { 115 char *context_file_path = argv[++i]; | 99 char *context_file_path = argv[++i]; 116 fprintf(stderr, "Including \"%s\".\n", context_file_path); | 100 fprintf(stderr, "Including \"%s\".\n", context_file_path); 117 include_file(context_file_path); | 101 include_file(context_file_path); 118 } else if (strcmp(argv[i], "--api") == 0) { | 102 } else if (strcmp(argv[i], "--api") == 0) { 119 api_mode = true; | 103 api_mode = true; 120 } else if (strcmp(argv[i], "--nh") == 0) { | 104 } else if (strcmp(argv[i], "--nh") == 0) { 121 syntax_highlight_enabled = false; | 105 syntax_highlight_enabled = false; 122 fprintf(stderr, "Syntax highlighting disabled.\n"); | 106 fprintf(stderr, "Syntax highlighting disabled.\n"); 123 } else if (strncmp(argv[i], "--session=", 10) == 0) { | 107 } else if (strncmp(argv[i], "--session=", 10) == 0) { 124 continue; | 108 continue; 125 } else if (strcmp(argv[i], "-s") == 0 || | 109 } else if (strcmp(argv[i], "-s") == 0 || 126 strcmp(argv[i], "--session") == 0) { | 110 strcmp(argv[i], "--session") == 0) { 127 i++; | 111 i++; 128 continue; | 112 continue; 129 } else { | 113 } else { 130 strcat(system_msg, argv[i]); | 114 strcat(system_msg, argv[i]); 131 strcat(system_msg, (i < argc - 1) ? " " : "."); | 115 strcat(system_msg, (i < argc - 1) ? " " : "."); 132 } | 116 } 133 } | 117 } 134 -   | 135 if (get_from_stdin) { | 118 if (get_from_stdin) { 136 if (*system_msg && global_messages) { | 119 if (*system_msg && global_messages) { 137 messages_add(global_messages, "system", system_msg); | 120 messages_add(global_messages, "system", system_msg); 138 } | 121 } 139 prompt = get_prompt_from_stdin(prompt); | 122 prompt = get_prompt_from_stdin(prompt); 140 free(system_msg); | 123 free(system_msg); 141 } else { | 124 } else { 142 free(prompt); | 125 free(prompt); 143 prompt = system_msg; | 126 prompt = system_msg; 144 } | 127 } 145 -   | 146 if (!*prompt) { | 128 if (!*prompt) { 147 free(prompt); | 129 free(prompt); 148 return NULL; | 130 return NULL; 149 } | 131 } 150 return prompt; | 132 return prompt; 151 } | 133 } 152 -   | 153 static bool try_prompt(int argc, char *argv[]) { | 134 static bool try_prompt(int argc, char *argv[]) { 154 char *prompt = get_prompt_from_args(argc, argv); | 135 char *prompt = get_prompt_from_args(argc, argv); 155 if (prompt) { | 136 if (prompt) { 156 char *response = agent_chat(prompt, global_messages); | 137 char *response = agent_chat(prompt, global_messages); 157 if (!response) { | 138 if (!response) { 158 printf("Could not get response from server\n"); | 139 printf("Could not get response from server\n"); 159 free(prompt); | 140 free(prompt); 160 return false; | 141 return false; 161 } | 142 } 162 // response is already printed inside agent_run | 143 // response is already printed inside agent_run 163 free(response); | 144 free(response); 164 free(prompt); | 145 free(prompt); 165 return true; | 146 return true; 166 } | 147 } 167 return false; | 148 return false; 168 } | 149 } 169 -   | 170 static bool include_file(const char *path) { | 150 static bool include_file(const char *path) { 171 char *file_content = read_file(path); | 151 char *file_content = read_file(path); 172 if (!file_content) | 152 if (!file_content) 173 return false; | 153 return false; 174 -   | 175 if (global_messages) { | 154 if (global_messages) { 176 messages_add(global_messages, "system", file_content); | 155 messages_add(global_messages, "system", file_content); 177 } | 156 } 178 free(file_content); | 157 free(file_content); 179 return true; | 158 return true; 180 } | 159 } 181 -   | 182 static void repl(void) { | 160 static void repl(void) { 183 r_config_handle cfg = r_config_get_instance(); | 161 r_config_handle cfg = r_config_get_instance(); 184 tool_registry_t *tools = tools_get_registry(); | 162 tool_registry_t *tools = tools_get_registry(); 185 -   | 186 line_init(); | 163 line_init(); 187 char *line = NULL; | 164 char *line = NULL; 188 -   | 189 while (true) { | 165 while (true) { 190 line = line_read("> "); | 166 line = line_read("> "); 191 if (!line || !*line) | 167 if (!line || !*line) 192 continue; | 168 continue; 193 -   | 194 if (!strncmp(line, "!dump", 5)) { | 169 if (!strncmp(line, "!dump", 5)) { 195 char *json = messages_to_string(global_messages); | 170 char *json = messages_to_string(global_messages); 196 if (json) { | 171 if (json) { 197 printf("%s\n", json); | 172 printf("%s\n", json); 198 free(json); | 173 free(json); 199 } | 174 } 200 continue; | 175 continue; 201 } | 176 } 202 -   | 203 if (!strncmp(line, "!clear", 6)) { | 177 if (!strncmp(line, "!clear", 6)) { 204 messages_clear(global_messages); | 178 messages_clear(global_messages); 205 fprintf(stderr, "Session cleared.\n"); | 179 fprintf(stderr, "Session cleared.\n"); 206 continue; | 180 continue; 207 } | 181 } 208 if (!strncmp(line, "!session", 8)) { | 182 if (!strncmp(line, "!session", 8)) { 209 printf("Session: %s\n", messages_get_session_id(global_messages)); | 183 printf("Session: %s\n", messages_get_session_id(global_messages)); 210 continue; | 184 continue; 211 } | 185 } 212 if (!strncmp(line, "!verbose", 8)) { | 186 if (!strncmp(line, "!verbose", 8)) { 213 bool verbose = !r_config_is_verbose(cfg); | 187 bool verbose = !r_config_is_verbose(cfg); 214 r_config_set_verbose(cfg, verbose); | 188 r_config_set_verbose(cfg, verbose); 215 fprintf(stderr, "%s\n", | 189 fprintf(stderr, "%s\n", 216 verbose ? "Verbose mode enabled" : "Verbose mode disabled"); | 190 verbose ? "Verbose mode enabled" : "Verbose mode disabled"); 217 continue; | 191 continue; 218 } | 192 } 219 if (line && *line != '\n') { | 193 if (line && *line != '\n') { 220 line_add_history(line); | 194 line_add_history(line); 221 } | 195 } 222 if (!strncmp(line, "!tools", 6)) { | 196 if (!strncmp(line, "!tools", 6)) { 223 struct json_object *descs = tool_registry_get_descriptions(tools); | 197 struct json_object *descs = tool_registry_get_descriptions(tools); 224 printf("Available tools: %s\n", json_object_to_json_string(descs)); | 198 printf("Available tools: %s\n", json_object_to_json_string(descs)); 225 continue; | 199 continue; 226 } | 200 } 227 if (!strncmp(line, "!models", 7)) { | 201 if (!strncmp(line, "!models", 7)) { 228 http_client_handle http = http_client_create(r_config_get_api_key(cfg)); | 202 http_client_handle http = http_client_create(r_config_get_api_key(cfg)); 229 if (http) { | 203 if (http) { 230 http_client_set_show_spinner(http, false); | 204 http_client_set_show_spinner(http, false); 231 char *response = NULL; | 205 char *response = NULL; 232 if (http_get(http, r_config_get_models_url(cfg), &response) == | 206 if (http_get(http, r_config_get_models_url(cfg), &response) == 233 R_SUCCESS && | 207 R_SUCCESS && 234 response) { | 208 response) { 235 printf("Models: %s\n", response); | 209 printf("Models: %s\n", response); 236 free(response); | 210 free(response); 237 } | 211 } 238 http_client_destroy(http); | 212 http_client_destroy(http); 239 } | 213 } 240 continue; | 214 continue; 241 } | 215 } 242 if (!strncmp(line, "!model", 6)) { | 216 if (!strncmp(line, "!model", 6)) { 243 if (line[6] == ' ') { | 217 if (line[6] == ' ') { 244 r_config_set_model(cfg, line + 7); | 218 r_config_set_model(cfg, line + 7); 245 } | 219 } 246 printf("Current model: %s\n", r_config_get_model(cfg)); | 220 printf("Current model: %s\n", r_config_get_model(cfg)); 247 continue; | 221 continue; 248 } | 222 } 249 if (!strncmp(line, "exit", 4)) { | 223 if (!strncmp(line, "exit", 4)) { 250 exit(0); | 224 exit(0); 251 } | 225 } 252 -   | 253 while (line && *line != '\n') { | 226 while (line && *line != '\n') { 254 char *response = agent_chat(line, global_messages); | 227 char *response = agent_chat(line, global_messages); 255 if (response) { | 228 if (response) { 256 // response is already printed inside agent_run via | 229 // response is already printed inside agent_run via 257 // parse_markdown_to_ansi | 230 // parse_markdown_to_ansi 258 free(response); | 231 free(response); 259 } else { | 232 } else { 260 fprintf(stderr, "Agent returned no response\n"); | 233 fprintf(stderr, "Agent returned no response\n"); 261 } | 234 } 262 line = NULL; | 235 line = NULL; 263 } | 236 } 264 } | 237 } 265 } | 238 } 266 -   | 267 static void init(void) { | 239 static void init(void) { 268 curl_global_init(CURL_GLOBAL_DEFAULT); | 240 curl_global_init(CURL_GLOBAL_DEFAULT); 269 setbuf(stdout, NULL); | 241 setbuf(stdout, NULL); 270 line_init(); | 242 line_init(); 271 -   | 272 r_config_handle cfg = r_config_get_instance(); | 243 r_config_handle cfg = r_config_get_instance(); 273 -   | 274 global_db = db_open(NULL); | 244 global_db = db_open(NULL); 275 global_messages = messages_create(r_config_get_session_id(cfg)); | 245 global_messages = messages_create(r_config_get_session_id(cfg)); 276 -   | 277 char *schema = db_get_schema(global_db); | 246 char *schema = db_get_schema(global_db); 278 char payload[1024 * 1024] = {0}; | 247 char payload[1024 * 1024] = {0}; 279 -   | 280 time_t now = time(NULL); | 248 time_t now = time(NULL); 281 struct tm *tm_info = localtime(&now); | 249 struct tm *tm_info = localtime(&now); 282 char datetime[64]; | 250 char datetime[64]; 283 strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S %Z", tm_info); | 251 strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S %Z", tm_info); 284 -   | 285 char cwd[4096]; | 252 char cwd[4096]; 286 if (!getcwd(cwd, sizeof(cwd))) { | 253 if (!getcwd(cwd, sizeof(cwd))) { 287 strcpy(cwd, "unknown"); | 254 strcpy(cwd, "unknown"); 288 } | 255 } 289 snprintf( | 256 snprintf( 290 payload, sizeof(payload), | 257 payload, sizeof(payload), 291 "# AUTONOMOUS AGENT INSTRUCTIONS\n" | 258 "# AUTONOMOUS AGENT INSTRUCTIONS\n" 292 "Current date/time: %s\n" | 259 "Current date/time: %s\n" 293 "Working directory: %s\n\n" | 260 "Working directory: %s\n\n" 294 "You are an autonomous AI agent. You operate in a loop: reason about the " | 261 "You are an autonomous AI agent. You operate in a loop: reason about the " 295 "task, " | 262 "task, " 296 "select and execute tools when needed, observe results, and continue " | 263 "select and execute tools when needed, observe results, and continue " 297 "until the goal is achieved.\n\n" | 264 "until the goal is achieved.\n\n" 298 "## The Enterprise Pyramid (Rigid Hierarchy)\n" | 265 "## The Enterprise Pyramid (Rigid Hierarchy)\n" 299 "You are the **Executive Agent (Apex)**. You MUST enforce a strict top-down " | 266 "You are the **Executive Agent (Apex)**. You MUST enforce a strict top-down " 300 "Chain of Command:\n" | 267 "Chain of Command:\n" 301 "- **Executive (Apex)**: Final arbiter. Owns the Strategic Blueprint. You " | 268 "- **Executive (Apex)**: Final arbiter. Owns the Strategic Blueprint. You " 302 "never code or research directly. You evaluate sub-agent depth.\n" | 269 "never code or research directly. You evaluate sub-agent depth.\n" 303 "- **Department Heads (Managers)**: Create detailed 'Task Packs'. " | 270 "- **Department Heads (Managers)**: Create detailed 'Task Packs'. " 304 "Synthesize sub-agent outputs into 'Department Reports'.\n" | 271 "Synthesize sub-agent outputs into 'Department Reports'.\n" 305 "- **Workers (Base)**: Execute atomic tasks. Report literal word counts " | 272 "- **Workers (Base)**: Execute atomic tasks. Report literal word counts " 306 "and file sizes upward.\n\n" | 273 "and file sizes upward.\n\n" 307 "### Bureaucratic Protocols (MANDATORY)\n" | 274 "### Bureaucratic Protocols (MANDATORY)\n" 308 "1. **Strategic Blueprint**: Your very first turn MUST output a " | 275 "1. **Strategic Blueprint**: Your very first turn MUST output a " 309 "blueprint: Mission, Departments Involved, and a 10-step Checklist.\n" | 276 "blueprint: Mission, Departments Involved, and a 10-step Checklist.\n" 310 "2. **Sequential Handover**: You are FORBIDDEN from spawning a Developer " | 277 "2. **Sequential Handover**: You are FORBIDDEN from spawning a Developer " 311 "until the Researcher has delivered a minimum of 1000 words of " | 278 "until the Researcher has delivered a minimum of 1000 words of " 312 "documented facts to `PROJECT_KNOWLEDGE.md`.\n" | 279 "documented facts to `PROJECT_KNOWLEDGE.md`.\n" 313 "3. **Content Depth Guardrail**: For 'Huge' projects, every page MUST " | 280 "3. **Content Depth Guardrail**: For 'Huge' projects, every page MUST " 314 "contain deep, researched info. Placeholder text (e.g., 'Coming soon', " | 281 "contain deep, researched info. Placeholder text (e.g., 'Coming soon', " 315 "'Introduction here') is a failure. You MUST use 'read_file' to audit " | 282 "'Introduction here') is a failure. You MUST use 'read_file' to audit " 316 "sub-agent work before concluding.\n" | 283 "sub-agent work before concluding.\n" 317 "4. **Global Task Registry (GTR)**: Query GTR for every sub-task. If a " | 284 "4. **Global Task Registry (GTR)**: Query GTR for every sub-task. If a " 318 "similar task exists, use its result summary. DUPLICATION IS FORBIDDEN.\n" | 285 "similar task exists, use its result summary. DUPLICATION IS FORBIDDEN.\n" 319 "5. **Fan-Out Architecture (Research)**: Manager calls `web_search` to get " | 286 "5. **Fan-Out Architecture (Research)**: Manager calls `web_search` to get " 320 "URLs, then uses `research_dispatcher` to queue them. Workers use " | 287 "URLs, then uses `research_dispatcher` to queue them. Workers use " 321 "`fetch_and_scrape` for individual URLs. If a Worker finds new links, it " | 288 "`fetch_and_scrape` for individual URLs. If a Worker finds new links, it " 322 "MUST use `suggest_subtask` to escalate. NEVER follow rabbit holes " | 289 "MUST use `suggest_subtask` to escalate. NEVER follow rabbit holes " 323 "yourself.\n\n" | 290 "yourself.\n\n" 324 "### Shared Memory & Data Sharing\n" | 291 "### Shared Memory & Data Sharing\n" 325 "- Every turn, you MUST update `PROJECT_KNOWLEDGE.md` with new findings.\n" | 292 "- Every turn, you MUST update `PROJECT_KNOWLEDGE.md` with new findings.\n" 326 "- All sub-agents MUST receive the full content of `PROJECT_KNOWLEDGE.md` " | 293 "- All sub-agents MUST receive the full content of `PROJECT_KNOWLEDGE.md` " 327 "to ensure a shared organizational history.\n\n" | 294 "to ensure a shared organizational history.\n\n" 328 "## Multi-Agent Orchestration (MANDATORY)\n" | 295 "## Multi-Agent Orchestration (MANDATORY)\n" 329 "## Project Scale Rules\n" | 296 "## Project Scale Rules\n" 330 "- HUGE PROJECTS: If a 'huge' or 'multi-page' project is requested, " | 297 "- HUGE PROJECTS: If a 'huge' or 'multi-page' project is requested, " 331 "delivering a single file is FORBIDDEN. You MUST create a directory " | 298 "delivering a single file is FORBIDDEN. You MUST create a directory " 332 "structure (e.g., assets/, css/, js/) and multiple linked HTML files.\n" | 299 "structure (e.g., assets/, css/, js/) and multiple linked HTML files.\n" 333 "- CHECKLIST PROTOCOL: Your first response to a complex request MUST " | 300 "- CHECKLIST PROTOCOL: Your first response to a complex request MUST " 334 "include the '## Checklist' you intend to fulfill.\n" | 301 "include the '## Checklist' you intend to fulfill.\n" 335 "- NO Lying: Never claim a task is done or a feature exists unless you " | 302 "- NO Lying: Never claim a task is done or a feature exists unless you " 336 "have the tool output to prove it.\n\n" | 303 "have the tool output to prove it.\n\n" 337 "## Multi-Agent Orchestration (MANDATORY)\n" | 304 "## Multi-Agent Orchestration (MANDATORY)\n" 338 "You are the Lead Orchestrator. You MUST delegate specialized work:\n" | 305 "You are the Lead Orchestrator. You MUST delegate specialized work:\n" 339 "- researcher: For ALL information gathering. Never research yourself if " | 306 "- researcher: For ALL information gathering. Never research yourself if " 340 "you can spawn a researcher.\n" | 307 "you can spawn a researcher.\n" 341 "- developer: For ALL coding, testing, and debugging.\n" | 308 "- developer: For ALL coding, testing, and debugging.\n" 342 "- security: For ALL security-related audits.\n\n" | 309 "- security: For ALL security-related audits.\n\n" 343 "IMPORTANT: When a sub-agent returns a result, you MUST read it, " | 310 "IMPORTANT: When a sub-agent returns a result, you MUST read it, " 344 "synthesize it, and then perform any necessary follow-up actions (like " | 311 "synthesize it, and then perform any necessary follow-up actions (like " 345 "writing to a file or spawning another agent). NEVER assume a task is " | 312 "writing to a file or spawning another agent). NEVER assume a task is " 346 "done just because a sub-agent finished; YOU must complete the final " | 313 "done just because a sub-agent finished; YOU must complete the final " 347 "delivery.\n\n" | 314 "delivery.\n\n" 348 "MANDATORY FINAL ACTION: If the user asked to save results to a file, " | 315 "MANDATORY FINAL ACTION: If the user asked to save results to a file, " 349 "YOU must call the write_file tool yourself with the synthesized data " | 316 "YOU must call the write_file tool yourself with the synthesized data " 350 "from the sub-agent. Do not ask for permission.\n\n" | 317 "from the sub-agent. Do not ask for permission.\n\n" 351 "## Tool Usage\n" | 318 "## Tool Usage\n" 352 "- Use tools proactively. If you say you will do something, you MUST " | 319 "- Use tools proactively. If you say you will do something, you MUST " 353 "call the tool in the SAME or NEXT turn.\n" | 320 "call the tool in the SAME or NEXT turn.\n" 354 "- If a tool fails, analyze and retry with a different approach.\n\n" | 321 "- If a tool fails, analyze and retry with a different approach.\n\n" 355 "## CRITICAL OUTPUT RULES\n" | 322 "## CRITICAL OUTPUT RULES\n" 356 "- SHOW THE DATA: Always include the actual content from tool/agent " | 323 "- SHOW THE DATA: Always include the actual content from tool/agent " 357 "results in your response.\n" | 324 "results in your response.\n" 358 "- NO PREMATURE COMPLETION: Do not say 'task complete' until you have " | 325 "- NO PREMATURE COMPLETION: Do not say 'task complete' until you have " 359 "verified all files are written and all steps are finished.\n" | 326 "verified all files are written and all steps are finished.\n" 360 "- SEQUENTIAL EXECUTION: Perform one logical step at a time. If you need " | 327 "- SEQUENTIAL EXECUTION: Perform one logical step at a time. If you need " 361 "to research AND write a file, spawn the researcher first, wait for the " | 328 "to research AND write a file, spawn the researcher first, wait for the " 362 "result, THEN write the file.\n" | 329 "result, THEN write the file.\n" 363 "## EXECUTION OF PYTHON CODE\n" | 330 "## EXECUTION OF PYTHON CODE\n" 364 "Exclusively use native python without 3rd party packages unless you " | 331 "Exclusively use native python without 3rd party packages unless you " 365 "have checked that they're installed on the system.\n" | 332 "have checked that they're installed on the system.\n" 366 "## Local Database\n" | 333 "## Local Database\n" 367 "You have a local SQLite database accessible via db_query, db_get, and " | 334 "You have a local SQLite database accessible via db_query, db_get, and " 368 "db_set tools.\n" | 335 "db_set tools.\n" 369 "Use stemmed, lowercase keys to prevent duplicates.\n" | 336 "Use stemmed, lowercase keys to prevent duplicates.\n" 370 "Schema: %s\n\n" | 337 "Schema: %s\n\n" 371 "## Response Format\n" | 338 "## Response Format\n" 372 "Your response IS the only thing the user sees. Tool outputs are hidden " | 339 "Your response IS the only thing the user sees. Tool outputs are hidden " 373 "from them.\n" | 340 "from them.\n" 374 "You MUST copy/paste relevant data from tool results into your " | 341 "You MUST copy/paste relevant data from tool results into your " 375 "response.\n" | 342 "response.\n" 376 "Bad: 'I searched and found information about X.'\n" | 343 "Bad: 'I searched and found information about X.'\n" 377 "Good: 'Here is what I found: [actual content from search results]'\n" | 344 "Good: 'Here is what I found: [actual content from search results]'\n" 378 "## Tool calls\n" | 345 "## Tool calls\n" 379 "As long you're not done, your last response must be always a tool call " | 346 "As long you're not done, your last response must be always a tool call " 380 "to keep going.\n" | 347 "to keep going.\n" 381 "Complete the whole tasks before bothering or informing user.\n" | 348 "Complete the whole tasks before bothering or informing user.\n" 382 "Backup\n" | 349 "Backup\n" 383 "You have the power to delete and modify files whatever your want to but " | 350 "You have the power to delete and modify files whatever your want to but " 384 "always make a .bak file with backup before editing unless you've " | 351 "always make a .bak file with backup before editing unless you've " 385 "created the file yourself." | 352 "created the file yourself." 386 "## Terminal execution\n" | 353 "## Terminal execution\n" 387 "You are in a bash environment, so can also execute bash commands.\n " | 354 "You are in a bash environment, so can also execute bash commands.\n " 388 "Everything is allowed.\n" | 355 "Everything is allowed.\n" 389 "Be very creative with your bash tool use, use it excessively.\n" | 356 "Be very creative with your bash tool use, use it excessively.\n" 390 "Prefer commands that do not require root access.\n" | 357 "Prefer commands that do not require root access.\n" 391 "## COMMUNICATION\n" | 358 "## COMMUNICATION\n" 392 "You are only allowed to talk once, so do that absolutely last with your " | 359 "You are only allowed to talk once, so do that absolutely last with your " 393 "conclusion.\n", | 360 "conclusion.\n", 394 datetime, cwd, schema ? schema : "{}"); | 361 datetime, cwd, schema ? schema : "{}"); 395 free(schema); | 362 free(schema); 396 fprintf(stderr, "Loading..."); | 363 fprintf(stderr, "Loading..."); 397 -   | 398 if (global_messages) { | 364 if (global_messages) { 399 messages_add(global_messages, "system", payload); | 365 messages_add(global_messages, "system", payload); 400 } | 366 } 401 -   | 402 const char *env_system_msg = r_config_get_system_message(cfg); | 367 const char *env_system_msg = r_config_get_system_message(cfg); 403 if (env_system_msg && *env_system_msg && global_messages) { | 368 if (env_system_msg && *env_system_msg && global_messages) { 404 messages_add(global_messages, "system", env_system_msg); | 369 messages_add(global_messages, "system", env_system_msg); 405 } | 370 } 406 -   | 407 if (!include_file(".rcontext.txt")) { | 371 if (!include_file(".rcontext.txt")) { 408 include_file("~/.rcontext.txt"); | 372 include_file("~/.rcontext.txt"); 409 } | 373 } 410 -   | 411 fprintf(stderr, "\r \r"); | 374 fprintf(stderr, "\r \r"); 412 } | 375 } 413 -   | 414 static void cleanup(void) { | 376 static void cleanup(void) { 415 if (global_messages) { | 377 if (global_messages) { 416 messages_destroy(global_messages); | 378 messages_destroy(global_messages); 417 global_messages = NULL; | 379 global_messages = NULL; 418 } | 380 } 419 if (global_db) { | 381 if (global_db) { 420 db_close(global_db); | 382 db_close(global_db); 421 global_db = NULL; | 383 global_db = NULL; 422 } | 384 } 423 tools_registry_shutdown(); | 385 tools_registry_shutdown(); 424 r_config_destroy(); | 386 r_config_destroy(); 425 } | 387 } 426 -   | 427 static void handle_sigint(int sig) { | 388 static void handle_sigint(int sig) { 428 (void)sig; | 389 (void)sig; 429 time_t current_time = time(NULL); | 390 time_t current_time = time(NULL); 430 printf("\n"); | 391 printf("\n"); 431 if (sigint_count == 0) { | 392 if (sigint_count == 0) { 432 first_sigint_time = current_time; | 393 first_sigint_time = current_time; 433 sigint_count++; | 394 sigint_count++; 434 } else { | 395 } else { 435 if (difftime(current_time, first_sigint_time) <= 1) { | 396 if (difftime(current_time, first_sigint_time) <= 1) { 436 cleanup(); | 397 cleanup(); 437 exit(0); | 398 exit(0); 438 } else { | 399 } else { 439 sigint_count = 1; | 400 sigint_count = 1; 440 first_sigint_time = current_time; | 401 first_sigint_time = current_time; 441 } | 402 } 442 } | 403 } 443 } | 404 } 444 -   | 445 static void parse_session_arg(int argc, char *argv[]) { | 405 static void parse_session_arg(int argc, char *argv[]) { 446 r_config_handle cfg = r_config_get_instance(); | 406 r_config_handle cfg = r_config_get_instance(); 447 -   | 448 for (int i = 1; i < argc; i++) { | 407 for (int i = 1; i < argc; i++) { 449 if (strncmp(argv[i], "--session=", 10) == 0) { | 408 if (strncmp(argv[i], "--session=", 10) == 0) { 450 const char *name = argv[i] + 10; | 409 const char *name = argv[i] + 10; 451 if (!r_config_set_session_id(cfg, name)) { | 410 if (!r_config_set_session_id(cfg, name)) { 452 fprintf(stderr, "Error: Invalid session name '%s'\n", name); | 411 fprintf(stderr, "Error: Invalid session name '%s'\n", name); 453 exit(1); | 412 exit(1); 454 } | 413 } 455 return; | 414 return; 456 } | 415 } 457 if ((strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) && | 416 if ((strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) && 458 i + 1 < argc) { | 417 i + 1 < argc) { 459 const char *name = argv[++i]; | 418 const char *name = argv[++i]; 460 if (!r_config_set_session_id(cfg, name)) { | 419 if (!r_config_set_session_id(cfg, name)) { 461 fprintf(stderr, "Error: Invalid session name '%s'\n", name); | 420 fprintf(stderr, "Error: Invalid session name '%s'\n", name); 462 exit(1); | 421 exit(1); 463 } | 422 } 464 return; | 423 return; 465 } | 424 } 466 } | 425 } 467 } | 426 } 468 -   | 469 int main(int argc, char *argv[]) { | 427 int main(int argc, char *argv[]) { 470 signal(SIGINT, handle_sigint); | 428 signal(SIGINT, handle_sigint); 471 atexit(cleanup); | 429 atexit(cleanup); 472 -   | 473 parse_session_arg(argc, argv); | 430 parse_session_arg(argc, argv); 474 init(); | 431 init(); 475 -   | 476 char *env_string = get_env_string(); | 432 char *env_string = get_env_string(); 477 if (env_string && *env_string && global_messages) { | 433 if (env_string && *env_string && global_messages) { 478 messages_add(global_messages, "system", env_string); | 434 messages_add(global_messages, "system", env_string); 479 free(env_string); | 435 free(env_string); 480 } | 436 } 481 -   | 482 messages_load(global_messages); | 437 messages_load(global_messages); 483 -   | 484 if (try_prompt(argc, argv)) { | 438 if (try_prompt(argc, argv)) { 485 return 0; | 439 return 0; 486 } | 440 } 487 -   | 488 repl(); | 441 repl(); 489 return 0; | 442 return 0; 490 } | 443 } -> Replacing lines in: src/markdown.c   CHANGES: src/markdown.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "markdown.h" | 2 #include "markdown.h" 4 #include | 3 #include 5 #include | 4 #include 6 #include | 5 #include 7 #include | 6 #include 8 #include | 7 #include 9 -   | 10 // --- ANSI Escape Codes --- | 8 // --- ANSI Escape Codes --- 11 #define RESET "\033[0m" | 9 #define RESET "\033[0m" 12 #define BOLD "\033[1m" | 10 #define BOLD "\033[1m" 13 #define ITALIC "\033[3m" | 11 #define ITALIC "\033[3m" 14 #define STRIKETHROUGH "\033[9m" | 12 #define STRIKETHROUGH "\033[9m" 15 -   | 16 #define FG_YELLOW "\033[33m" | 13 #define FG_YELLOW "\033[33m" 17 #define FG_BLUE "\033[34m" | 14 #define FG_BLUE "\033[34m" 18 #define FG_CYAN "\033[36m" | 15 #define FG_CYAN "\033[36m" 19 #define FG_MAGENTA "\033[35m" | 16 #define FG_MAGENTA "\033[35m" 20 -   | 21 #define BG_YELLOW_FG_BLACK "\033[43;30m" | 17 #define BG_YELLOW_FG_BLACK "\033[43;30m" 22 -   | 23 /** | 18 /** 24 * @brief Checks if a given word is a programming language keyword. | 19 * @brief Checks if a given word is a programming language keyword. 25 */ | 20 */ 26 static int is_keyword(const char *word) { | 21 static int is_keyword(const char *word) { 27 const char *keywords[] = { | 22 const char *keywords[] = { 28 "int", "float", "double", "char", "void", "if", "else", "while", "for", | 23 "int", "float", "double", "char", "void", "if", "else", "while", "for", 29 "return", "struct", "printf", "let", "fn", "impl", "match", "enum", "trait", "use", "mod", "pub", | 24 "return", "struct", "printf", "let", "fn", "impl", "match", "enum", "trait", "use", "mod", "pub", 30 "const", "static", "def", "class", "import", "from", "as", "with", "try", "except", | 25 "const", "static", "def", "class", "import", "from", "as", "with", "try", "except", 31 "finally", "lambda", "async", "await", "public", "private", "protected", "interface", "extends", | 26 "finally", "lambda", "async", "await", "public", "private", "protected", "interface", "extends", 32 "implements", "new", "synchronized", "var", "switch", "case", "break", "continue", | 27 "implements", "new", "synchronized", "var", "switch", "case", "break", "continue", 33 "namespace", "template", "typename", "virtual", "override", "friend", "package", "func", "type", "go", "defer", "select", | 28 "namespace", "template", "typename", "virtual", "override", "friend", "package", "func", "type", "go", "defer", "select", 34 "then", "elif", "fi", "esac", "do", "done", "using"}; | 29 "then", "elif", "fi", "esac", "do", "done", "using"}; 35 -   | 36 for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { | 30 for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { 37 if (strcmp(word, keywords[i]) == 0) return 1; | 31 if (strcmp(word, keywords[i]) == 0) return 1; 38 } | 32 } 39 return 0; | 33 return 0; 40 } | 34 } 41 -   | 42 void highlight_code(const char *code) { | 35 void highlight_code(const char *code) { 43 const char *ptr = code; | 36 const char *ptr = code; 44 char buffer[4096]; | 37 char buffer[4096]; 45 size_t index = 0; | 38 size_t index = 0; 46 -   | 47 while (*ptr) { | 39 while (*ptr) { 48 if (isalpha((unsigned char)*ptr) || *ptr == '_') { | 40 if (isalpha((unsigned char)*ptr) || *ptr == '_') { 49 while (isalnum((unsigned char)*ptr) || *ptr == '_') { | 41 while (isalnum((unsigned char)*ptr) || *ptr == '_') { 50 if (index < sizeof(buffer) - 1) buffer[index++] = *ptr++; | 42 if (index < sizeof(buffer) - 1) buffer[index++] = *ptr++; 51 else ptr++; | 43 else ptr++; 52 } | 44 } 53 buffer[index] = '\0'; | 45 buffer[index] = '\0'; 54 if (is_keyword(buffer)) printf(FG_BLUE "%s" RESET FG_YELLOW, buffer); | 46 if (is_keyword(buffer)) printf(FG_BLUE "%s" RESET FG_YELLOW, buffer); 55 else printf("%s", buffer); | 47 else printf("%s", buffer); 56 index = 0; | 48 index = 0; 57 } else if (isdigit((unsigned char)*ptr)) { | 49 } else if (isdigit((unsigned char)*ptr)) { 58 while (isdigit((unsigned char)*ptr)) { | 50 while (isdigit((unsigned char)*ptr)) { 59 if (index < sizeof(buffer) - 1) buffer[index++] = *ptr++; | 51 if (index < sizeof(buffer) - 1) buffer[index++] = *ptr++; 60 else ptr++; | 52 else ptr++; 61 } | 53 } 62 buffer[index] = '\0'; | 54 buffer[index] = '\0'; 63 printf(FG_CYAN "%s" RESET FG_YELLOW, buffer); | 55 printf(FG_CYAN "%s" RESET FG_YELLOW, buffer); 64 index = 0; | 56 index = 0; 65 } else { | 57 } else { 66 putchar(*ptr); | 58 putchar(*ptr); 67 ptr++; | 59 ptr++; 68 } | 60 } 69 } | 61 } 70 } | 62 } 71 -   | 72 void parse_markdown_to_ansi(const char *markdown) { | 63 void parse_markdown_to_ansi(const char *markdown) { 73 const char *ptr = markdown; | 64 const char *ptr = markdown; 74 bool is_start_of_line = true; | 65 bool is_start_of_line = true; 75 -   | 76 while (*ptr) { | 66 while (*ptr) { 77 if (is_start_of_line && strncmp(ptr, "```", 3) == 0) { | 67 if (is_start_of_line && strncmp(ptr, "```", 3) == 0) { 78 ptr += 3; | 68 ptr += 3; 79 while (*ptr && *ptr != '\n') ptr++; | 69 while (*ptr && *ptr != '\n') ptr++; 80 if (*ptr) ptr++; | 70 if (*ptr) ptr++; 81 const char *code_start = ptr; | 71 const char *code_start = ptr; 82 const char *code_end = strstr(code_start, "```"); | 72 const char *code_end = strstr(code_start, "```"); 83 if (code_end) { | 73 if (code_end) { 84 char *block_buffer = strndup(code_start, (size_t)(code_end - code_start)); | 74 char *block_buffer = strndup(code_start, (size_t)(code_end - code_start)); 85 printf(FG_YELLOW); | 75 printf(FG_YELLOW); 86 highlight_code(block_buffer); | 76 highlight_code(block_buffer); 87 printf(RESET); | 77 printf(RESET); 88 free(block_buffer); | 78 free(block_buffer); 89 ptr = code_end + 3; | 79 ptr = code_end + 3; 90 if (*ptr == '\n') ptr++; | 80 if (*ptr == '\n') ptr++; 91 is_start_of_line = true; | 81 is_start_of_line = true; 92 continue; | 82 continue; 93 } else { | 83 } else { 94 printf(FG_YELLOW); | 84 printf(FG_YELLOW); 95 highlight_code(code_start); | 85 highlight_code(code_start); 96 printf(RESET); | 86 printf(RESET); 97 break; | 87 break; 98 } | 88 } 99 } | 89 } 100 -   | 101 if (is_start_of_line) { | 90 if (is_start_of_line) { 102 const char *line_start_ptr = ptr; | 91 const char *line_start_ptr = ptr; 103 int indent_level = 0; | 92 int indent_level = 0; 104 while (*ptr == ' ') { indent_level++; ptr++; } | 93 while (*ptr == ' ') { indent_level++; ptr++; } 105 bool block_processed = true; | 94 bool block_processed = true; 106 if (strncmp(ptr, "###### ", 7) == 0) { printf(BOLD FG_YELLOW); ptr += 7; } | 95 if (strncmp(ptr, "###### ", 7) == 0) { printf(BOLD FG_YELLOW); ptr += 7; } 107 else if (strncmp(ptr, "##### ", 6) == 0) { printf(BOLD FG_YELLOW); ptr += 6; } | 96 else if (strncmp(ptr, "##### ", 6) == 0) { printf(BOLD FG_YELLOW); ptr += 6; } 108 else if (strncmp(ptr, "#### ", 5) == 0) { printf(BOLD FG_YELLOW); ptr += 5; } | 97 else if (strncmp(ptr, "#### ", 5) == 0) { printf(BOLD FG_YELLOW); ptr += 5; } 109 else if (strncmp(ptr, "### ", 4) == 0) { printf(BOLD FG_YELLOW); ptr += 4; } | 98 else if (strncmp(ptr, "### ", 4) == 0) { printf(BOLD FG_YELLOW); ptr += 4; } 110 else if (strncmp(ptr, "## ", 3) == 0) { printf(BOLD FG_YELLOW); ptr += 3; } | 99 else if (strncmp(ptr, "## ", 3) == 0) { printf(BOLD FG_YELLOW); ptr += 3; } 111 else if (strncmp(ptr, "# ", 2) == 0) { printf(BOLD FG_YELLOW); ptr += 2; } | 100 else if (strncmp(ptr, "# ", 2) == 0) { printf(BOLD FG_YELLOW); ptr += 2; } 112 else if ((strncmp(ptr, "---", 3) == 0 || strncmp(ptr, "***", 3) == 0) && (*(ptr + 3) == '\n' || *(ptr + 3) == '\0')) { | 101 else if ((strncmp(ptr, "---", 3) == 0 || strncmp(ptr, "***", 3) == 0) && (*(ptr + 3) == '\n' || *(ptr + 3) == '\0')) { 113 printf(FG_CYAN "─────────────────────────────────────────────────────────────────────────" RESET "\n"); | 102 printf(FG_CYAN "─────────────────────────────────────────────────────────────────────────" RESET "\n"); 114 ptr += 3; if (*ptr == '\n') ptr++; is_start_of_line = true; continue; | 103 ptr += 3; if (*ptr == '\n') ptr++; is_start_of_line = true; continue; 115 } else if (strncmp(ptr, "> ", 2) == 0) { | 104 } else if (strncmp(ptr, "> ", 2) == 0) { 116 for (int i = 0; i < indent_level; i++) putchar(' '); | 105 for (int i = 0; i < indent_level; i++) putchar(' '); 117 printf(ITALIC FG_CYAN "▎ " RESET); ptr += 2; is_start_of_line = false; continue; | 106 printf(ITALIC FG_CYAN "▎ " RESET); ptr += 2; is_start_of_line = false; continue; 118 } else if ((*ptr == '*' || *ptr == '-' || *ptr == '+') && *(ptr + 1) == ' ') { | 107 } else if ((*ptr == '*' || *ptr == '-' || *ptr == '+') && *(ptr + 1) == ' ') { 119 for (int i = 0; i < indent_level; i++) putchar(' '); | 108 for (int i = 0; i < indent_level; i++) putchar(' '); 120 printf(FG_MAGENTA "• " RESET); ptr += 2; is_start_of_line = false; continue; | 109 printf(FG_MAGENTA "• " RESET); ptr += 2; is_start_of_line = false; continue; 121 } else { | 110 } else { 122 const char *temp_ptr = ptr; | 111 const char *temp_ptr = ptr; 123 while (isdigit((unsigned char)*temp_ptr)) temp_ptr++; | 112 while (isdigit((unsigned char)*temp_ptr)) temp_ptr++; 124 if (temp_ptr > ptr && *temp_ptr == '.' && *(temp_ptr + 1) == ' ') { | 113 if (temp_ptr > ptr && *temp_ptr == '.' && *(temp_ptr + 1) == ' ') { 125 for (int i = 0; i < indent_level; i++) putchar(' '); | 114 for (int i = 0; i < indent_level; i++) putchar(' '); 126 printf(FG_MAGENTA); fwrite(ptr, 1, (size_t)(temp_ptr - ptr) + 1, stdout); printf(" " RESET); | 115 printf(FG_MAGENTA); fwrite(ptr, 1, (size_t)(temp_ptr - ptr) + 1, stdout); printf(" " RESET); 127 ptr = temp_ptr + 2; is_start_of_line = false; continue; | 116 ptr = temp_ptr + 2; is_start_of_line = false; continue; 128 } else { block_processed = false; ptr = line_start_ptr; } | 117 } else { block_processed = false; ptr = line_start_ptr; } 129 } | 118 } 130 if (block_processed) { | 119 if (block_processed) { 131 while (*ptr && *ptr != '\n') putchar(*ptr++); | 120 while (*ptr && *ptr != '\n') putchar(*ptr++); 132 printf(RESET "\n"); if (*ptr == '\n') ptr++; | 121 printf(RESET "\n"); if (*ptr == '\n') ptr++; 133 is_start_of_line = true; continue; | 122 is_start_of_line = true; continue; 134 } | 123 } 135 } | 124 } 136 -   | 137 if (strncmp(ptr, "***", 3) == 0 || strncmp(ptr, "___", 3) == 0) { | 125 if (strncmp(ptr, "***", 3) == 0 || strncmp(ptr, "___", 3) == 0) { 138 const char *marker = strncmp(ptr, "***", 3) == 0 ? "***" : "___"; | 126 const char *marker = strncmp(ptr, "***", 3) == 0 ? "***" : "___"; 139 printf(BOLD ITALIC); ptr += 3; | 127 printf(BOLD ITALIC); ptr += 3; 140 const char *end = strstr(ptr, marker); | 128 const char *end = strstr(ptr, marker); 141 if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 3; } | 129 if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 3; } 142 else { fputs(ptr, stdout); ptr += strlen(ptr); } | 130 else { fputs(ptr, stdout); ptr += strlen(ptr); } 143 printf(RESET); continue; | 131 printf(RESET); continue; 144 } | 132 } 145 if (strncmp(ptr, "**", 2) == 0 || strncmp(ptr, "__", 2) == 0) { | 133 if (strncmp(ptr, "**", 2) == 0 || strncmp(ptr, "__", 2) == 0) { 146 const char *marker = strncmp(ptr, "**", 2) == 0 ? "**" : "__"; | 134 const char *marker = strncmp(ptr, "**", 2) == 0 ? "**" : "__"; 147 printf(BOLD); ptr += 2; | 135 printf(BOLD); ptr += 2; 148 const char *end = strstr(ptr, marker); | 136 const char *end = strstr(ptr, marker); 149 if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; } | 137 if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; } 150 else { fputs(ptr, stdout); ptr += strlen(ptr); } | 138 else { fputs(ptr, stdout); ptr += strlen(ptr); } 151 printf(RESET); continue; | 139 printf(RESET); continue; 152 } | 140 } 153 if (strncmp(ptr, "~~", 2) == 0) { | 141 if (strncmp(ptr, "~~", 2) == 0) { 154 printf(STRIKETHROUGH); ptr += 2; | 142 printf(STRIKETHROUGH); ptr += 2; 155 const char *end = strstr(ptr, "~~"); | 143 const char *end = strstr(ptr, "~~"); 156 if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; } | 144 if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; } 157 else { fputs(ptr, stdout); ptr += strlen(ptr); } | 145 else { fputs(ptr, stdout); ptr += strlen(ptr); } 158 printf(RESET); continue; | 146 printf(RESET); continue; 159 } | 147 } 160 if (strncmp(ptr, "==", 2) == 0) { | 148 if (strncmp(ptr, "==", 2) == 0) { 161 printf(BG_YELLOW_FG_BLACK); ptr += 2; | 149 printf(BG_YELLOW_FG_BLACK); ptr += 2; 162 const char *end = strstr(ptr, "=="); | 150 const char *end = strstr(ptr, "=="); 163 if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; } | 151 if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; } 164 else { fputs(ptr, stdout); ptr += strlen(ptr); } | 152 else { fputs(ptr, stdout); ptr += strlen(ptr); } 165 printf(RESET); continue; | 153 printf(RESET); continue; 166 } | 154 } 167 if (*ptr == '`' && *(ptr + 1) != '`') { | 155 if (*ptr == '`' && *(ptr + 1) != '`') { 168 printf(FG_YELLOW); ptr++; const char *start = ptr; | 156 printf(FG_YELLOW); ptr++; const char *start = ptr; 169 while (*ptr && *ptr != '`') ptr++; | 157 while (*ptr && *ptr != '`') ptr++; 170 fwrite(start, 1, (size_t)(ptr - start), stdout); if (*ptr == '`') ptr++; | 158 fwrite(start, 1, (size_t)(ptr - start), stdout); if (*ptr == '`') ptr++; 171 printf(RESET); continue; | 159 printf(RESET); continue; 172 } | 160 } 173 if (*ptr == '[') { | 161 if (*ptr == '[') { 174 const char *text_start = ptr + 1; | 162 const char *text_start = ptr + 1; 175 const char *text_end = strchr(text_start, ']'); | 163 const char *text_end = strchr(text_start, ']'); 176 if (text_end && *(text_end + 1) == '(') { | 164 if (text_end && *(text_end + 1) == '(') { 177 const char *url_start = text_end + 2; | 165 const char *url_start = text_end + 2; 178 const char *url_end = strchr(url_start, ')'); | 166 const char *url_end = strchr(url_start, ')'); 179 if (url_end) { | 167 if (url_end) { 180 printf(FG_BLUE); fwrite(text_start, 1, (size_t)(text_end - text_start), stdout); | 168 printf(FG_BLUE); fwrite(text_start, 1, (size_t)(text_end - text_start), stdout); 181 printf(RESET " ("); printf(ITALIC FG_CYAN); | 169 printf(RESET " ("); printf(ITALIC FG_CYAN); 182 fwrite(url_start, 1, (size_t)(url_end - url_start), stdout); | 170 fwrite(url_start, 1, (size_t)(url_end - url_start), stdout); 183 printf(RESET ")"); ptr = url_end + 1; continue; | 171 printf(RESET ")"); ptr = url_end + 1; continue; 184 } | 172 } 185 } | 173 } 186 } | 174 } 187 -   | 188 if (*ptr == '\n') is_start_of_line = true; | 175 if (*ptr == '\n') is_start_of_line = true; 189 else if (!isspace((unsigned char)*ptr)) is_start_of_line = false; | 176 else if (!isspace((unsigned char)*ptr)) is_start_of_line = false; 190 putchar(*ptr); | 177 putchar(*ptr); 191 ptr++; | 178 ptr++; 192 } | 179 } 193 } | 180 } -> Replacing lines in: src/messages.c   CHANGES: src/messages.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "messages.h" | 2 #include "messages.h" 4 #include "db.h" | 3 #include "db.h" 5 #include | 4 #include 6 #include | 5 #include 7 #include | 6 #include 8 #include | 7 #include 9 #include | 8 #include 10 -   | 11 #define MAX_CONTENT_LENGTH 1048570 | 9 #define MAX_CONTENT_LENGTH 1048570 12 #define MAX_TOOL_RESULT_LENGTH 104000 | 10 #define MAX_TOOL_RESULT_LENGTH 104000 13 -   | 14 struct messages_t { | 11 struct messages_t { 15 struct json_object *array; | 12 struct json_object *array; 16 char *session_id; | 13 char *session_id; 17 db_handle db; | 14 db_handle db; 18 bool loaded; | 15 bool loaded; 19 }; | 16 }; 20 -   | 21 static bool is_valid_session_id(const char *session_id) { | 17 static bool is_valid_session_id(const char *session_id) { 22 if (!session_id || !*session_id) return false; | 18 if (!session_id || !*session_id) return false; 23 if (strlen(session_id) > 200) return false; | 19 if (strlen(session_id) > 200) return false; 24 -   | 25 for (const char *p = session_id; *p; p++) { | 20 for (const char *p = session_id; *p; p++) { 26 if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_' && *p != '.') { | 21 if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_' && *p != '.') { 27 return false; | 22 return false; 28 } | 23 } 29 } | 24 } 30 return true; | 25 return true; 31 } | 26 } 32 -   | 33 static char *generate_default_session_id(void) { | 27 static char *generate_default_session_id(void) { 34 char *session_id = malloc(32); | 28 char *session_id = malloc(32); 35 if (!session_id) return NULL; | 29 if (!session_id) return NULL; 36 -   | 37 snprintf(session_id, 32, "session-%d", getppid()); | 30 snprintf(session_id, 32, "session-%d", getppid()); 38 return session_id; | 31 return session_id; 39 } | 32 } 40 -   | 41 messages_handle messages_create(const char *session_id) { | 33 messages_handle messages_create(const char *session_id) { 42 struct messages_t *msgs = calloc(1, sizeof(struct messages_t)); | 34 struct messages_t *msgs = calloc(1, sizeof(struct messages_t)); 43 if (!msgs) return NULL; | 35 if (!msgs) return NULL; 44 -   | 45 msgs->array = json_object_new_array(); | 36 msgs->array = json_object_new_array(); 46 if (!msgs->array) { | 37 if (!msgs->array) { 47 free(msgs); | 38 free(msgs); 48 return NULL; | 39 return NULL; 49 } | 40 } 50 -   | 51 if (session_id && is_valid_session_id(session_id)) { | 41 if (session_id && is_valid_session_id(session_id)) { 52 msgs->session_id = strdup(session_id); | 42 msgs->session_id = strdup(session_id); 53 } else { | 43 } else { 54 msgs->session_id = generate_default_session_id(); | 44 msgs->session_id = generate_default_session_id(); 55 } | 45 } 56 -   | 57 if (!msgs->session_id) { | 46 if (!msgs->session_id) { 58 json_object_put(msgs->array); | 47 json_object_put(msgs->array); 59 free(msgs); | 48 free(msgs); 60 return NULL; | 49 return NULL; 61 } | 50 } 62 -   | 63 msgs->db = db_open(NULL); | 51 msgs->db = db_open(NULL); 64 -   | 65 return msgs; | 52 return msgs; 66 } | 53 } 67 -   | 68 void messages_destroy(messages_handle msgs) { | 54 void messages_destroy(messages_handle msgs) { 69 if (!msgs) return; | 55 if (!msgs) return; 70 if (msgs->array) json_object_put(msgs->array); | 56 if (msgs->array) json_object_put(msgs->array); 71 if (msgs->db) db_close(msgs->db); | 57 if (msgs->db) db_close(msgs->db); 72 free(msgs->session_id); | 58 free(msgs->session_id); 73 free(msgs); | 59 free(msgs); 74 } | 60 } 75 -   | 76 r_status_t messages_set_session_id(messages_handle msgs, const char *session_id) { | 61 r_status_t messages_set_session_id(messages_handle msgs, const char *session_id) { 77 if (!msgs || !is_valid_session_id(session_id)) return R_ERROR_INVALID_ARG; | 62 if (!msgs || !is_valid_session_id(session_id)) return R_ERROR_INVALID_ARG; 78 -   | 79 free(msgs->session_id); | 63 free(msgs->session_id); 80 msgs->session_id = strdup(session_id); | 64 msgs->session_id = strdup(session_id); 81 msgs->loaded = false; | 65 msgs->loaded = false; 82 return msgs->session_id ? R_SUCCESS : R_ERROR_OUT_OF_MEMORY; | 66 return msgs->session_id ? R_SUCCESS : R_ERROR_OUT_OF_MEMORY; 83 } | 67 } 84 -   | 85 const char *messages_get_session_id(messages_handle msgs) { | 68 const char *messages_get_session_id(messages_handle msgs) { 86 return msgs ? msgs->session_id : NULL; | 69 return msgs ? msgs->session_id : NULL; 87 } | 70 } 88 -   | 89 r_status_t messages_add(messages_handle msgs, const char *role, const char *content) { | 71 r_status_t messages_add(messages_handle msgs, const char *role, const char *content) { 90 if (!msgs || !msgs->array || !role) return R_ERROR_INVALID_ARG; | 72 if (!msgs || !msgs->array || !role) return R_ERROR_INVALID_ARG; 91 -   | 92 struct json_object *message = json_object_new_object(); | 73 struct json_object *message = json_object_new_object(); 93 if (!message) return R_ERROR_OUT_OF_MEMORY; | 74 if (!message) return R_ERROR_OUT_OF_MEMORY; 94 -   | 95 json_object_object_add(message, "role", json_object_new_string(role)); | 75 json_object_object_add(message, "role", json_object_new_string(role)); 96 -   | 97 if (content) { | 76 if (content) { 98 size_t len = strlen(content); | 77 size_t len = strlen(content); 99 if (len > MAX_CONTENT_LENGTH) len = MAX_CONTENT_LENGTH; | 78 if (len > MAX_CONTENT_LENGTH) len = MAX_CONTENT_LENGTH; 100 json_object_object_add(message, "content", | 79 json_object_object_add(message, "content", 101 json_object_new_string_len(content, (int)len)); | 80 json_object_new_string_len(content, (int)len)); 102 } | 81 } 103 -   | 104 json_object_array_add(msgs->array, message); | 82 json_object_array_add(msgs->array, message); 105 -   | 106 if (strcmp(role, "system") != 0) { | 83 if (strcmp(role, "system") != 0) { 107 messages_save(msgs); | 84 messages_save(msgs); 108 } | 85 } 109 -   | 110 return R_SUCCESS; | 86 return R_SUCCESS; 111 } | 87 } 112 -   | 113 r_status_t messages_add_object(messages_handle msgs, struct json_object *message) { | 88 r_status_t messages_add_object(messages_handle msgs, struct json_object *message) { 114 if (!msgs || !msgs->array || !message) return R_ERROR_INVALID_ARG; | 89 if (!msgs || !msgs->array || !message) return R_ERROR_INVALID_ARG; 115 -   | 116 json_object_array_add(msgs->array, message); | 90 json_object_array_add(msgs->array, message); 117 messages_save(msgs); | 91 messages_save(msgs); 118 return R_SUCCESS; | 92 return R_SUCCESS; 119 } | 93 } 120 -   | 121 r_status_t messages_add_tool_call(messages_handle msgs, struct json_object *message) { | 94 r_status_t messages_add_tool_call(messages_handle msgs, struct json_object *message) { 122 if (!msgs || !msgs->array || !message) return R_ERROR_INVALID_ARG; | 95 if (!msgs || !msgs->array || !message) return R_ERROR_INVALID_ARG; 123 -   | 124 json_object_array_add(msgs->array, message); | 96 json_object_array_add(msgs->array, message); 125 messages_save(msgs); | 97 messages_save(msgs); 126 return R_SUCCESS; | 98 return R_SUCCESS; 127 } | 99 } 128 -   | 129 r_status_t messages_add_tool_result(messages_handle msgs, const char *tool_call_id, const char *result) { | 100 r_status_t messages_add_tool_result(messages_handle msgs, const char *tool_call_id, const char *result) { 130 if (!msgs || !msgs->array || !tool_call_id || !result) return R_ERROR_INVALID_ARG; | 101 if (!msgs || !msgs->array || !tool_call_id || !result) return R_ERROR_INVALID_ARG; 131 -   | 132 struct json_object *message = json_object_new_object(); | 102 struct json_object *message = json_object_new_object(); 133 if (!message) return R_ERROR_OUT_OF_MEMORY; | 103 if (!message) return R_ERROR_OUT_OF_MEMORY; 134 -   | 135 json_object_object_add(message, "tool_call_id", json_object_new_string(tool_call_id)); | 104 json_object_object_add(message, "tool_call_id", json_object_new_string(tool_call_id)); 136 -   | 137 size_t len = strlen(result); | 105 size_t len = strlen(result); 138 if (len > MAX_TOOL_RESULT_LENGTH) len = MAX_TOOL_RESULT_LENGTH; | 106 if (len > MAX_TOOL_RESULT_LENGTH) len = MAX_TOOL_RESULT_LENGTH; 139 json_object_object_add(message, "tool_result", | 107 json_object_object_add(message, "tool_result", 140 json_object_new_string_len(result, (int)len)); | 108 json_object_new_string_len(result, (int)len)); 141 -   | 142 json_object_array_add(msgs->array, message); | 109 json_object_array_add(msgs->array, message); 143 messages_save(msgs); | 110 messages_save(msgs); 144 return R_SUCCESS; | 111 return R_SUCCESS; 145 } | 112 } 146 -   | 147 r_status_t messages_remove_last(messages_handle msgs) { | 113 r_status_t messages_remove_last(messages_handle msgs) { 148 if (!msgs || !msgs->array) return R_ERROR_INVALID_ARG; | 114 if (!msgs || !msgs->array) return R_ERROR_INVALID_ARG; 149 -   | 150 int size = json_object_array_length(msgs->array); | 115 int size = json_object_array_length(msgs->array); 151 if (size == 0) return R_ERROR_NOT_FOUND; | 116 if (size == 0) return R_ERROR_NOT_FOUND; 152 -   | 153 json_object_array_del_idx(msgs->array, size - 1, 1); | 117 json_object_array_del_idx(msgs->array, size - 1, 1); 154 messages_save(msgs); | 118 messages_save(msgs); 155 return R_SUCCESS; | 119 return R_SUCCESS; 156 } | 120 } 157 -   | 158 r_status_t messages_remove_range(messages_handle msgs, int start, int count) { | 121 r_status_t messages_remove_range(messages_handle msgs, int start, int count) { 159 if (!msgs || !msgs->array) return R_ERROR_INVALID_ARG; | 122 if (!msgs || !msgs->array) return R_ERROR_INVALID_ARG; 160 int size = json_object_array_length(msgs->array); | 123 int size = json_object_array_length(msgs->array); 161 if (start < 0 || start >= size || count < 0) return R_ERROR_INVALID_ARG; | 124 if (start < 0 || start >= size || count < 0) return R_ERROR_INVALID_ARG; 162 if (start + count > size) count = size - start; | 125 if (start + count > size) count = size - start; 163 -   | 164 json_object_array_del_idx(msgs->array, start, count); | 126 json_object_array_del_idx(msgs->array, start, count); 165 messages_save(msgs); | 127 messages_save(msgs); 166 return R_SUCCESS; | 128 return R_SUCCESS; 167 } | 129 } 168 -   | 169 r_status_t messages_clear(messages_handle msgs) { | 130 r_status_t messages_clear(messages_handle msgs) { 170 if (!msgs) return R_ERROR_INVALID_ARG; | 131 if (!msgs) return R_ERROR_INVALID_ARG; 171 -   | 172 if (msgs->array) json_object_put(msgs->array); | 132 if (msgs->array) json_object_put(msgs->array); 173 msgs->array = json_object_new_array(); | 133 msgs->array = json_object_new_array(); 174 if (!msgs->array) return R_ERROR_OUT_OF_MEMORY; | 134 if (!msgs->array) return R_ERROR_OUT_OF_MEMORY; 175 -   | 176 messages_save(msgs); | 135 messages_save(msgs); 177 return R_SUCCESS; | 136 return R_SUCCESS; 178 } | 137 } 179 -   | 180 r_status_t messages_save(messages_handle msgs) { | 138 r_status_t messages_save(messages_handle msgs) { 181 if (!msgs || !msgs->array || !msgs->db) return R_ERROR_INVALID_ARG; | 139 if (!msgs || !msgs->array || !msgs->db) return R_ERROR_INVALID_ARG; 182 -   | 183 char key[512]; | 140 char key[512]; 184 snprintf(key, sizeof(key), "session:%s", msgs->session_id); | 141 snprintf(key, sizeof(key), "session:%s", msgs->session_id); 185 -   | 186 const char *json_str = json_object_to_json_string_ext(msgs->array, JSON_C_TO_STRING_PLAIN); | 142 const char *json_str = json_object_to_json_string_ext(msgs->array, JSON_C_TO_STRING_PLAIN); 187 if (!json_str) return R_ERROR_OUT_OF_MEMORY; | 143 if (!json_str) return R_ERROR_OUT_OF_MEMORY; 188 -   | 189 return db_save_conversation(msgs->db, key, json_str); | 144 return db_save_conversation(msgs->db, key, json_str); 190 } | 145 } 191 -   | 192 r_status_t messages_load(messages_handle msgs) { | 146 r_status_t messages_load(messages_handle msgs) { 193 if (!msgs || !msgs->db) return R_ERROR_INVALID_ARG; | 147 if (!msgs || !msgs->db) return R_ERROR_INVALID_ARG; 194 if (msgs->loaded) return R_SUCCESS; | 148 if (msgs->loaded) return R_SUCCESS; 195 -   | 196 char key[512]; | 149 char key[512]; 197 snprintf(key, sizeof(key), "session:%s", msgs->session_id); | 150 snprintf(key, sizeof(key), "session:%s", msgs->session_id); 198 -   | 199 char *data = NULL; | 151 char *data = NULL; 200 r_status_t status = db_load_conversation(msgs->db, key, &data); | 152 r_status_t status = db_load_conversation(msgs->db, key, &data); 201 if (status != R_SUCCESS || !data) { | 153 if (status != R_SUCCESS || !data) { 202 if (status == R_SUCCESS) msgs->loaded = true; | 154 if (status == R_SUCCESS) msgs->loaded = true; 203 return status == R_SUCCESS ? R_ERROR_NOT_FOUND : status; | 155 return status == R_SUCCESS ? R_ERROR_NOT_FOUND : status; 204 } | 156 } 205 -   | 206 struct json_object *loaded = json_tokener_parse(data); | 157 struct json_object *loaded = json_tokener_parse(data); 207 free(data); | 158 free(data); 208 -   | 209 if (!loaded || !json_object_is_type(loaded, json_type_array)) { | 159 if (!loaded || !json_object_is_type(loaded, json_type_array)) { 210 if (loaded) json_object_put(loaded); | 160 if (loaded) json_object_put(loaded); 211 return R_ERROR_PARSE; | 161 return R_ERROR_PARSE; 212 } | 162 } 213 -   | 214 int len = json_object_array_length(loaded); | 163 int len = json_object_array_length(loaded); 215 for (int i = 0; i < len; i++) { | 164 for (int i = 0; i < len; i++) { 216 struct json_object *msg = json_object_array_get_idx(loaded, i); | 165 struct json_object *msg = json_object_array_get_idx(loaded, i); 217 struct json_object *role_obj; | 166 struct json_object *role_obj; 218 -   | 219 if (json_object_object_get_ex(msg, "role", &role_obj)) { | 167 if (json_object_object_get_ex(msg, "role", &role_obj)) { 220 const char *role = json_object_get_string(role_obj); | 168 const char *role = json_object_get_string(role_obj); 221 if (role && strcmp(role, "system") != 0) { | 169 if (role && strcmp(role, "system") != 0) { 222 json_object_array_add(msgs->array, json_object_get(msg)); | 170 json_object_array_add(msgs->array, json_object_get(msg)); 223 } | 171 } 224 } else { | 172 } else { 225 json_object_array_add(msgs->array, json_object_get(msg)); | 173 json_object_array_add(msgs->array, json_object_get(msg)); 226 } | 174 } 227 } | 175 } 228 -   | 229 json_object_put(loaded); | 176 json_object_put(loaded); 230 msgs->loaded = true; | 177 msgs->loaded = true; 231 return R_SUCCESS; | 178 return R_SUCCESS; 232 } | 179 } 233 -   | 234 struct json_object *messages_get_object(messages_handle msgs, int index) { | 180 struct json_object *messages_get_object(messages_handle msgs, int index) { 235 if (!msgs || !msgs->array) return NULL; | 181 if (!msgs || !msgs->array) return NULL; 236 return json_object_array_get_idx(msgs->array, index); | 182 return json_object_array_get_idx(msgs->array, index); 237 } | 183 } 238 -   | 239 r_status_t messages_replace_at(messages_handle msgs, int index, struct json_object *message) { | 184 r_status_t messages_replace_at(messages_handle msgs, int index, struct json_object *message) { 240 if (!msgs || !msgs->array || !message) return R_ERROR_INVALID_ARG; | 185 if (!msgs || !msgs->array || !message) return R_ERROR_INVALID_ARG; 241 int size = json_object_array_length(msgs->array); | 186 int size = json_object_array_length(msgs->array); 242 if (index < 0 || index >= size) return R_ERROR_INVALID_ARG; | 187 if (index < 0 || index >= size) return R_ERROR_INVALID_ARG; 243 -   | 244 // json-c doesn't have a direct 'replace' for array by index that handles memory easily | 188 // json-c doesn't have a direct 'replace' for array by index that handles memory easily 245 // We'll use json_object_array_put_idx which replaces and puts the old object | 189 // We'll use json_object_array_put_idx which replaces and puts the old object 246 json_object_array_put_idx(msgs->array, index, message); | 190 json_object_array_put_idx(msgs->array, index, message); 247 messages_save(msgs); | 191 messages_save(msgs); 248 return R_SUCCESS; | 192 return R_SUCCESS; 249 } | 193 } 250 -   | 251 struct json_object *messages_to_json(messages_handle msgs) { | 194 struct json_object *messages_to_json(messages_handle msgs) { 252 return msgs ? msgs->array : NULL; | 195 return msgs ? msgs->array : NULL; 253 } | 196 } 254 -   | 255 char *messages_to_string(messages_handle msgs) { | 197 char *messages_to_string(messages_handle msgs) { 256 if (!msgs || !msgs->array) return NULL; | 198 if (!msgs || !msgs->array) return NULL; 257 return strdup(json_object_to_json_string_ext(msgs->array, JSON_C_TO_STRING_PRETTY)); | 199 return strdup(json_object_to_json_string_ext(msgs->array, JSON_C_TO_STRING_PRETTY)); 258 } | 200 } 259 -   | 260 char *messages_to_json_string(messages_handle msgs) { | 201 char *messages_to_json_string(messages_handle msgs) { 261 if (!msgs || !msgs->array) return NULL; | 202 if (!msgs || !msgs->array) return NULL; 262 return strdup(json_object_to_json_string_ext(msgs->array, JSON_C_TO_STRING_PLAIN)); | 203 return strdup(json_object_to_json_string_ext(msgs->array, JSON_C_TO_STRING_PLAIN)); 263 } | 204 } 264 -   | 265 int messages_count(messages_handle msgs) { | 205 int messages_count(messages_handle msgs) { 266 if (!msgs || !msgs->array) return 0; | 206 if (!msgs || !msgs->array) return 0; 267 return json_object_array_length(msgs->array); | 207 return json_object_array_length(msgs->array); 268 } | 208 } -> Replacing lines in: src/python_repair.c   CHANGES: src/python_repair.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "python_repair.h" | 2 #include "python_repair.h" 4 #include | 3 #include 5 #include | 4 #include 6 #include | 5 #include 7 #include | 6 #include 8 #include | 7 #include 9 #include | 8 #include 10 -   | 11 #define INDENT_WIDTH 4 | 9 #define INDENT_WIDTH 4 12 -   | 13 static char *dedent_code(const char *src) { | 10 static char *dedent_code(const char *src) { 14 if (!src || !*src) return strdup(""); | 11 if (!src || !*src) return strdup(""); 15 -   | 16 int min_indent = -1; | 12 int min_indent = -1; 17 const char *line = src; | 13 const char *line = src; 18 while (line && *line) { | 14 while (line && *line) { 19 int indent = 0; | 15 int indent = 0; 20 while (line[indent] == ' ' || line[indent] == '\t') indent++; | 16 while (line[indent] == ' ' || line[indent] == '\t') indent++; 21 | 17 22 if (line[indent] != '\n' && line[indent] != '\0') { | 18 if (line[indent] != '\n' && line[indent] != '\0') { 23 if (min_indent == -1 || indent < min_indent) min_indent = indent; | 19 if (min_indent == -1 || indent < min_indent) min_indent = indent; 24 } | 20 } 25 | 21 26 line = strchr(line, '\n'); | 22 line = strchr(line, '\n'); 27 if (line) line++; | 23 if (line) line++; 28 } | 24 } 29 -   | 30 if (min_indent <= 0) return strdup(src); | 25 if (min_indent <= 0) return strdup(src); 31 -   | 32 size_t src_len = strlen(src); | 26 size_t src_len = strlen(src); 33 char *result = malloc(src_len + 1); | 27 char *result = malloc(src_len + 1); 34 if (!result) return strdup(src); | 28 if (!result) return strdup(src); 35 | 29 36 char *dst = result; | 30 char *dst = result; 37 const char *curr = src; | 31 const char *curr = src; 38 while (curr && *curr) { | 32 while (curr && *curr) { 39 int to_skip = min_indent; | 33 int to_skip = min_indent; 40 while (to_skip > 0 && (*curr == ' ' || *curr == '\t')) { | 34 while (to_skip > 0 && (*curr == ' ' || *curr == '\t')) { 41 curr++; | 35 curr++; 42 to_skip--; | 36 to_skip--; 43 } | 37 } 44 | 38 45 const char *next_line = strchr(curr, '\n'); | 39 const char *next_line = strchr(curr, '\n'); 46 if (next_line) { | 40 if (next_line) { 47 size_t line_len = (size_t)(next_line - curr + 1); | 41 size_t line_len = (size_t)(next_line - curr + 1); 48 memcpy(dst, curr, line_len); | 42 memcpy(dst, curr, line_len); 49 dst += line_len; | 43 dst += line_len; 50 curr = next_line + 1; | 44 curr = next_line + 1; 51 } else { | 45 } else { 52 strcpy(dst, curr); | 46 strcpy(dst, curr); 53 break; | 47 break; 54 } | 48 } 55 } | 49 } 56 *dst = '\0'; | 50 *dst = '\0'; 57 return result; | 51 return result; 58 } | 52 } 59 -   | 60 static char *normalize_indentation(const char *src) { | 53 static char *normalize_indentation(const char *src) { 61 if (!src) return NULL; | 54 if (!src) return NULL; 62 size_t src_len = strlen(src); | 55 size_t src_len = strlen(src); 63 char *result = malloc(src_len * 2 + 1); // Extra space for normalized indents | 56 char *result = malloc(src_len * 2 + 1); // Extra space for normalized indents 64 if (!result) return NULL; | 57 if (!result) return NULL; 65 -   | 66 char *dst = result; | 58 char *dst = result; 67 const char *line = src; | 59 const char *line = src; 68 while (line && *line) { | 60 while (line && *line) { 69 const char *next_line = strchr(line, '\n'); | 61 const char *next_line = strchr(line, '\n'); 70 size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); | 62 size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); 71 -   | 72 // Check if line is empty or just whitespace | 63 // Check if line is empty or just whitespace 73 bool is_empty = true; | 64 bool is_empty = true; 74 for (size_t i = 0; i < line_len; i++) { | 65 for (size_t i = 0; i < line_len; i++) { 75 if (!isspace((unsigned char)line[i])) { | 66 if (!isspace((unsigned char)line[i])) { 76 is_empty = false; | 67 is_empty = false; 77 break; | 68 break; 78 } | 69 } 79 } | 70 } 80 -   | 81 if (is_empty) { | 71 if (is_empty) { 82 if (next_line) { | 72 if (next_line) { 83 *dst++ = '\n'; | 73 *dst++ = '\n'; 84 line = next_line + 1; | 74 line = next_line + 1; 85 } else { | 75 } else { 86 break; | 76 break; 87 } | 77 } 88 continue; | 78 continue; 89 } | 79 } 90 -   | 91 // Calculate current leading indent | 80 // Calculate current leading indent 92 int leading_spaces = 0; | 81 int leading_spaces = 0; 93 const char *content_ptr = line; | 82 const char *content_ptr = line; 94 while (content_ptr < (line + line_len) && (*content_ptr == ' ' || *content_ptr == '\t')) { | 83 while (content_ptr < (line + line_len) && (*content_ptr == ' ' || *content_ptr == '\t')) { 95 if (*content_ptr == '\t') { | 84 if (*content_ptr == '\t') { 96 leading_spaces += INDENT_WIDTH; | 85 leading_spaces += INDENT_WIDTH; 97 } else { | 86 } else { 98 leading_spaces++; | 87 leading_spaces++; 99 } | 88 } 100 content_ptr++; | 89 content_ptr++; 101 } | 90 } 102 -   | 103 // Round to nearest INDENT_WIDTH | 91 // Round to nearest INDENT_WIDTH 104 int normalized_level = (leading_spaces + INDENT_WIDTH / 2) / INDENT_WIDTH; | 92 int normalized_level = (leading_spaces + INDENT_WIDTH / 2) / INDENT_WIDTH; 105 int target_spaces = normalized_level * INDENT_WIDTH; | 93 int target_spaces = normalized_level * INDENT_WIDTH; 106 -   | 107 for (int i = 0; i < target_spaces; i++) *dst++ = ' '; | 94 for (int i = 0; i < target_spaces; i++) *dst++ = ' '; 108 | 95 109 size_t content_len = (size_t)((line + line_len) - content_ptr); | 96 size_t content_len = (size_t)((line + line_len) - content_ptr); 110 memcpy(dst, content_ptr, content_len); | 97 memcpy(dst, content_ptr, content_len); 111 dst += content_len; | 98 dst += content_len; 112 -   | 113 if (next_line) { | 99 if (next_line) { 114 *dst++ = '\n'; | 100 *dst++ = '\n'; 115 line = next_line + 1; | 101 line = next_line + 1; 116 } else { | 102 } else { 117 break; | 103 break; 118 } | 104 } 119 } | 105 } 120 *dst = '\0'; | 106 *dst = '\0'; 121 return result; | 107 return result; 122 } | 108 } 123 -   | 124 static char *repair_strings_and_brackets(const char *src) { | 109 static char *repair_strings_and_brackets(const char *src) { 125 if (!src) return NULL; | 110 if (!src) return NULL; 126 size_t src_len = strlen(src); | 111 size_t src_len = strlen(src); 127 char *result = malloc(src_len + 2048); // Buffer for extra quotes/brackets | 112 char *result = malloc(src_len + 2048); // Buffer for extra quotes/brackets 128 if (!result) return NULL; | 113 if (!result) return NULL; 129 -   | 130 char *dst = result; | 114 char *dst = result; 131 const char *p = src; | 115 const char *p = src; 132 | 116 133 char bracket_stack[1024]; | 117 char bracket_stack[1024]; 134 int stack_ptr = 0; | 118 int stack_ptr = 0; 135 -   | 136 bool in_string = false; | 119 bool in_string = false; 137 char string_quote = 0; | 120 char string_quote = 0; 138 int quote_type = 0; // 1 for single, 3 for triple | 121 int quote_type = 0; // 1 for single, 3 for triple 139 bool escaped = false; | 122 bool escaped = false; 140 -   | 141 while (*p) { | 123 while (*p) { 142 char ch = *p; | 124 char ch = *p; 143 -   | 144 if (!in_string) { | 125 if (!in_string) { 145 if (ch == '#') { | 126 if (ch == '#') { 146 // Comment, copy until newline | 127 // Comment, copy until newline 147 while (*p && *p != '\n') *dst++ = *p++; | 128 while (*p && *p != '\n') *dst++ = *p++; 148 continue; | 129 continue; 149 } | 130 } 150 if (ch == '\'' || ch == '"') { | 131 if (ch == '\'' || ch == '"') { 151 string_quote = ch; | 132 string_quote = ch; 152 if (strncmp(p, "'''", 3) == 0 || strncmp(p, "\"\"\"", 3) == 0) { | 133 if (strncmp(p, "'''", 3) == 0 || strncmp(p, "\"\"\"", 3) == 0) { 153 quote_type = 3; | 134 quote_type = 3; 154 in_string = true; | 135 in_string = true; 155 *dst++ = *p++; *dst++ = *p++; *dst++ = *p++; | 136 *dst++ = *p++; *dst++ = *p++; *dst++ = *p++; 156 continue; | 137 continue; 157 } else { | 138 } else { 158 quote_type = 1; | 139 quote_type = 1; 159 in_string = true; | 140 in_string = true; 160 *dst++ = *p++; | 141 *dst++ = *p++; 161 continue; | 142 continue; 162 } | 143 } 163 } else if (ch == '(' || ch == '[' || ch == '{') { | 144 } else if (ch == '(' || ch == '[' || ch == '{') { 164 if (stack_ptr < 1024) bracket_stack[stack_ptr++] = ch; | 145 if (stack_ptr < 1024) bracket_stack[stack_ptr++] = ch; 165 } else if (ch == ')' || ch == ']' || ch == '}') { | 146 } else if (ch == ')' || ch == ']' || ch == '}') { 166 char expected = 0; | 147 char expected = 0; 167 if (ch == ')') expected = '('; | 148 if (ch == ')') expected = '('; 168 else if (ch == ']') expected = '['; | 149 else if (ch == ']') expected = '['; 169 else if (ch == '}') expected = '{'; | 150 else if (ch == '}') expected = '{'; 170 | 151 171 if (stack_ptr > 0 && bracket_stack[stack_ptr - 1] == expected) { | 152 if (stack_ptr > 0 && bracket_stack[stack_ptr - 1] == expected) { 172 stack_ptr--; | 153 stack_ptr--; 173 } else { | 154 } else { 174 // Mismatched closing; skip it to prevent syntax errors | 155 // Mismatched closing; skip it to prevent syntax errors 175 p++; | 156 p++; 176 continue; | 157 continue; 177 } | 158 } 178 } | 159 } 179 } else { | 160 } else { 180 if (escaped) { | 161 if (escaped) { 181 escaped = false; | 162 escaped = false; 182 } else if (ch == '\\') { | 163 } else if (ch == '\\') { 183 escaped = true; | 164 escaped = true; 184 } else if (ch == string_quote) { | 165 } else if (ch == string_quote) { 185 if (quote_type == 3) { | 166 if (quote_type == 3) { 186 if (strncmp(p, "'''", 3) == 0 && string_quote == '\'') { | 167 if (strncmp(p, "'''", 3) == 0 && string_quote == '\'') { 187 in_string = false; | 168 in_string = false; 188 *dst++ = *p++; *dst++ = *p++; *dst++ = *p++; | 169 *dst++ = *p++; *dst++ = *p++; *dst++ = *p++; 189 continue; | 170 continue; 190 } else if (strncmp(p, "\"\"\"", 3) == 0 && string_quote == '"') { | 171 } else if (strncmp(p, "\"\"\"", 3) == 0 && string_quote == '"') { 191 in_string = false; | 172 in_string = false; 192 *dst++ = *p++; *dst++ = *p++; *dst++ = *p++; | 173 *dst++ = *p++; *dst++ = *p++; *dst++ = *p++; 193 continue; | 174 continue; 194 } | 175 } 195 } else { | 176 } else { 196 in_string = false; | 177 in_string = false; 197 } | 178 } 198 } | 179 } 199 } | 180 } 200 *dst++ = *p++; | 181 *dst++ = *p++; 201 } | 182 } 202 -   | 203 if (in_string) { | 183 if (in_string) { 204 if (quote_type == 3) { | 184 if (quote_type == 3) { 205 *dst++ = string_quote; *dst++ = string_quote; *dst++ = string_quote; | 185 *dst++ = string_quote; *dst++ = string_quote; *dst++ = string_quote; 206 } else { | 186 } else { 207 *dst++ = string_quote; | 187 *dst++ = string_quote; 208 } | 188 } 209 } | 189 } 210 -   | 211 // Balance brackets | 190 // Balance brackets 212 while (stack_ptr > 0) { | 191 while (stack_ptr > 0) { 213 char opener = bracket_stack[--stack_ptr]; | 192 char opener = bracket_stack[--stack_ptr]; 214 if (opener == '(') *dst++ = ')'; | 193 if (opener == '(') *dst++ = ')'; 215 else if (opener == '[') *dst++ = ']'; | 194 else if (opener == '[') *dst++ = ']'; 216 else if (opener == '{') *dst++ = '}'; | 195 else if (opener == '{') *dst++ = '}'; 217 } | 196 } 218 -   | 219 *dst = '\0'; | 197 *dst = '\0'; 220 return result; | 198 return result; 221 } | 199 } 222 -   | 223 static char *add_missing_passes(const char *src) { | 200 static char *add_missing_passes(const char *src) { 224 if (!src) return NULL; | 201 if (!src) return NULL; 225 size_t src_len = strlen(src); | 202 size_t src_len = strlen(src); 226 char *result = malloc(src_len * 2 + 1); | 203 char *result = malloc(src_len * 2 + 1); 227 if (!result) return NULL; | 204 if (!result) return NULL; 228 -   | 229 char *dst = result; | 205 char *dst = result; 230 const char *line = src; | 206 const char *line = src; 231 | 207 232 while (line && *line) { | 208 while (line && *line) { 233 const char *next_line = strchr(line, '\n'); | 209 const char *next_line = strchr(line, '\n'); 234 size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); | 210 size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); 235 -   | 236 // Copy current line | 211 // Copy current line 237 memcpy(dst, line, line_len); | 212 memcpy(dst, line, line_len); 238 dst += line_len; | 213 dst += line_len; 239 if (next_line) *dst++ = '\n'; | 214 if (next_line) *dst++ = '\n'; 240 -   | 241 // Check if line ends with ':' (ignoring comments/whitespace) | 215 // Check if line ends with ':' (ignoring comments/whitespace) 242 const char *p = line + line_len - 1; | 216 const char *p = line + line_len - 1; 243 while (p >= line && isspace((unsigned char)*p)) p--; | 217 while (p >= line && isspace((unsigned char)*p)) p--; 244 | 218 245 if (p >= line && *p == ':') { | 219 if (p >= line && *p == ':') { 246 // Heuristic: check if next non-empty line is more indented | 220 // Heuristic: check if next non-empty line is more indented 247 const char *lookahead = next_line ? next_line + 1 : NULL; | 221 const char *lookahead = next_line ? next_line + 1 : NULL; 248 bool needs_pass = true; | 222 bool needs_pass = true; 249 | 223 250 while (lookahead && *lookahead) { | 224 while (lookahead && *lookahead) { 251 // Skip empty/whitespace lines | 225 // Skip empty/whitespace lines 252 const char *next_next = strchr(lookahead, '\n'); | 226 const char *next_next = strchr(lookahead, '\n'); 253 size_t look_len = next_next ? (size_t)(next_next - lookahead) : strlen(lookahead); | 227 size_t look_len = next_next ? (size_t)(next_next - lookahead) : strlen(lookahead); 254 | 228 255 bool empty = true; | 229 bool empty = true; 256 for (size_t i = 0; i < look_len; i++) { | 230 for (size_t i = 0; i < look_len; i++) { 257 if (!isspace((unsigned char)lookahead[i])) { | 231 if (!isspace((unsigned char)lookahead[i])) { 258 empty = false; | 232 empty = false; 259 break; | 233 break; 260 } | 234 } 261 } | 235 } 262 | 236 263 if (!empty) { | 237 if (!empty) { 264 // Check indent of this non-empty line | 238 // Check indent of this non-empty line 265 int current_indent = 0; | 239 int current_indent = 0; 266 const char *line_p = line; | 240 const char *line_p = line; 267 while (line_p < (line + line_len) && (*line_p == ' ' || *line_p == '\t')) { | 241 while (line_p < (line + line_len) && (*line_p == ' ' || *line_p == '\t')) { 268 if (*line_p == '\t') current_indent += INDENT_WIDTH; | 242 if (*line_p == '\t') current_indent += INDENT_WIDTH; 269 else current_indent++; | 243 else current_indent++; 270 line_p++; | 244 line_p++; 271 } | 245 } 272 -   | 273 int line_look_indent = 0; | 246 int line_look_indent = 0; 274 const char *look_p = lookahead; | 247 const char *look_p = lookahead; 275 while (look_p < (lookahead + look_len) && (*look_p == ' ' || *look_p == '\t')) { | 248 while (look_p < (lookahead + look_len) && (*look_p == ' ' || *look_p == '\t')) { 276 if (*look_p == '\t') line_look_indent += INDENT_WIDTH; | 249 if (*look_p == '\t') line_look_indent += INDENT_WIDTH; 277 else line_look_indent++; | 250 else line_look_indent++; 278 look_p++; | 251 look_p++; 279 } | 252 } 280 -   | 281 if (line_look_indent > current_indent) { | 253 if (line_look_indent > current_indent) { 282 needs_pass = false; | 254 needs_pass = false; 283 } | 255 } 284 break; | 256 break; 285 } | 257 } 286 | 258 287 if (next_next) lookahead = next_next + 1; | 259 if (next_next) lookahead = next_next + 1; 288 else break; | 260 else break; 289 } | 261 } 290 -   | 291 if (needs_pass) { | 262 if (needs_pass) { 292 // Find current indent to place 'pass' correctly | 263 // Find current indent to place 'pass' correctly 293 int current_indent = 0; | 264 int current_indent = 0; 294 const char *line_p = line; | 265 const char *line_p = line; 295 while (line_p < (line + line_len) && (*line_p == ' ' || *line_p == '\t')) { | 266 while (line_p < (line + line_len) && (*line_p == ' ' || *line_p == '\t')) { 296 if (*line_p == '\t') current_indent += INDENT_WIDTH; | 267 if (*line_p == '\t') current_indent += INDENT_WIDTH; 297 else current_indent++; | 268 else current_indent++; 298 line_p++; | 269 line_p++; 299 } | 270 } 300 | 271 301 int target_indent = current_indent + INDENT_WIDTH; | 272 int target_indent = current_indent + INDENT_WIDTH; 302 for (int i = 0; i < target_indent; i++) *dst++ = ' '; | 273 for (int i = 0; i < target_indent; i++) *dst++ = ' '; 303 memcpy(dst, "pass\n", 5); | 274 memcpy(dst, "pass\n", 5); 304 dst += 5; | 275 dst += 5; 305 } | 276 } 306 } | 277 } 307 -   | 308 if (next_line) line = next_line + 1; | 278 if (next_line) line = next_line + 1; 309 else break; | 279 else break; 310 } | 280 } 311 -   | 312 *dst = '\0'; | 281 *dst = '\0'; 313 return result; | 282 return result; 314 } | 283 } 315 -   | 316 char *python_repair_code(const char *src) { | 284 char *python_repair_code(const char *src) { 317 if (!src) return NULL; | 285 if (!src) return NULL; 318 -   | 319 char *s1 = dedent_code(src); | 286 char *s1 = dedent_code(src); 320 char *s2 = normalize_indentation(s1); | 287 char *s2 = normalize_indentation(s1); 321 free(s1); | 288 free(s1); 322 | 289 323 char *s3 = repair_strings_and_brackets(s2); | 290 char *s3 = repair_strings_and_brackets(s2); 324 free(s2); | 291 free(s2); 325 | 292 326 char *s4 = add_missing_passes(s3); | 293 char *s4 = add_missing_passes(s3); 327 free(s3); | 294 free(s3); 328 -   | 329 return s4; | 295 return s4; 330 } | 296 } -> Replacing lines in: src/r_config.c   CHANGES: src/r_config.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "r_config.h" | 2 #include "r_config.h" 4 #include | 3 #include 5 #include | 4 #include 6 #include | 5 #include 7 #include | 6 #include 8 -   | 9 struct r_config_t { | 7 struct r_config_t { 10 char *api_url; | 8 char *api_url; 11 char *models_url; | 9 char *models_url; 12 char *model; | 10 char *model; 13 char *api_key; | 11 char *api_key; 14 char *db_path; | 12 char *db_path; 15 char *session_id; | 13 char *session_id; 16 char *system_message; | 14 char *system_message; 17 double temperature; | 15 double temperature; 18 int max_tokens; | 16 int max_tokens; 19 bool use_tools; | 17 bool use_tools; 20 bool use_strict; | 18 bool use_strict; 21 bool verbose; | 19 bool verbose; 22 }; | 20 }; 23 -   | 24 static struct r_config_t *instance = NULL; | 21 static struct r_config_t *instance = NULL; 25 -   | 26 static char *strdup_safe(const char *s) { | 22 static char *strdup_safe(const char *s) { 27 return s ? strdup(s) : NULL; | 23 return s ? strdup(s) : NULL; 28 } | 24 } 29 -   | 30 static bool resolve_env_bool(const char *env_name, bool default_val) { | 25 static bool resolve_env_bool(const char *env_name, bool default_val) { 31 const char *val = getenv(env_name); | 26 const char *val = getenv(env_name); 32 if (!val) return default_val; | 27 if (!val) return default_val; 33 if (!strcmp(val, "true") || !strcmp(val, "1")) return true; | 28 if (!strcmp(val, "true") || !strcmp(val, "1")) return true; 34 if (!strcmp(val, "false") || !strcmp(val, "0")) return false; | 29 if (!strcmp(val, "false") || !strcmp(val, "0")) return false; 35 return default_val; | 30 return default_val; 36 } | 31 } 37 -   | 38 static const char *resolve_api_key(void) { | 32 static const char *resolve_api_key(void) { 39 -   | 40 const char * key = getenv("R_KEY"); | 33 const char * key = getenv("R_KEY"); 41 if (key && *key) return key; | 34 if (key && *key) return key; 42 -   | 43 -   | 44 -   | 45 -   | 46 key = getenv("OPENROUTER_API_KEY"); | 35 key = getenv("OPENROUTER_API_KEY"); 47 if (key && *key) return key; | 36 if (key && *key) return key; 48 -   | 49 -   | 50 -   | 51 key = getenv("OPENAI_API_KEY"); | 37 key = getenv("OPENAI_API_KEY"); 52 if (key && *key) return key; | 38 if (key && *key) return key; 53 -   | 54 -   | 55 return "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"; | 39 return "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"; 56 } | 40 } 57 -   | 58 static bool is_valid_session_id(const char *session_id) { | 41 static bool is_valid_session_id(const char *session_id) { 59 if (!session_id || !*session_id) return false; | 42 if (!session_id || !*session_id) return false; 60 if (strlen(session_id) > 255) return false; | 43 if (strlen(session_id) > 255) return false; 61 -   | 62 for (const char *p = session_id; *p; p++) { | 44 for (const char *p = session_id; *p; p++) { 63 if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_' && *p != '.') { | 45 if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_' && *p != '.') { 64 return false; | 46 return false; 65 } | 47 } 66 } | 48 } 67 return true; | 49 return true; 68 } | 50 } 69 -   | 70 r_config_handle r_config_get_instance(void) { | 51 r_config_handle r_config_get_instance(void) { 71 if (instance) return instance; | 52 if (instance) return instance; 72 -   | 73 instance = calloc(1, sizeof(struct r_config_t)); | 53 instance = calloc(1, sizeof(struct r_config_t)); 74 if (!instance) return NULL; | 54 if (!instance) return NULL; 75 -   | 76 const char *base_url = getenv("R_BASE_URL"); | 55 const char *base_url = getenv("R_BASE_URL"); 77 if (base_url && *base_url) { | 56 if (base_url && *base_url) { 78 size_t len = strlen(base_url); | 57 size_t len = strlen(base_url); 79 instance->api_url = malloc(len + 32); | 58 instance->api_url = malloc(len + 32); 80 instance->models_url = malloc(len + 32); | 59 instance->models_url = malloc(len + 32); 81 if (instance->api_url && instance->models_url) { | 60 if (instance->api_url && instance->models_url) { 82 snprintf(instance->api_url, len + 32, "%s/v1/chat/completions", base_url); | 61 snprintf(instance->api_url, len + 32, "%s/v1/chat/completions", base_url); 83 snprintf(instance->models_url, len + 32, "%s/v1/models", base_url); | 62 snprintf(instance->models_url, len + 32, "%s/v1/models", base_url); 84 } | 63 } 85 } else { | 64 } else { 86 instance->api_url = strdup("https://api.openai.com/v1/chat/completions"); | 65 instance->api_url = strdup("https://api.openai.com/v1/chat/completions"); 87 instance->models_url = strdup("https://api.openai.com/v1/models"); | 66 instance->models_url = strdup("https://api.openai.com/v1/models"); 88 } | 67 } 89 -   | 90 const char *model = getenv("R_MODEL"); | 68 const char *model = getenv("R_MODEL"); 91 instance->model = strdup(model && *model ? model : "gpt-4o-mini"); | 69 instance->model = strdup(model && *model ? model : "gpt-4o-mini"); 92 -   | 93 instance->api_key = strdup(resolve_api_key()); | 70 instance->api_key = strdup(resolve_api_key()); 94 instance->db_path = strdup("~/.r.db"); | 71 instance->db_path = strdup("~/.r.db"); 95 instance->temperature = 0.1; | 72 instance->temperature = 0.1; 96 const char *max_tokens_env = getenv("R_MAX_TOKENS"); | 73 const char *max_tokens_env = getenv("R_MAX_TOKENS"); 97 instance->max_tokens = max_tokens_env ? atoi(max_tokens_env) : 4096; | 74 instance->max_tokens = max_tokens_env ? atoi(max_tokens_env) : 4096; 98 instance->use_tools = resolve_env_bool("R_USE_TOOLS", true); | 75 instance->use_tools = resolve_env_bool("R_USE_TOOLS", true); 99 instance->use_strict = resolve_env_bool("R_USE_STRICT", true); | 76 instance->use_strict = resolve_env_bool("R_USE_STRICT", true); 100 instance->verbose = false; | 77 instance->verbose = false; 101 -   | 102 const char *session = getenv("R_SESSION"); | 78 const char *session = getenv("R_SESSION"); 103 if (session && is_valid_session_id(session)) { | 79 if (session && is_valid_session_id(session)) { 104 instance->session_id = strdup(session); | 80 instance->session_id = strdup(session); 105 } else { | 81 } else { 106 instance->session_id = NULL; | 82 instance->session_id = NULL; 107 } | 83 } 108 -   | 109 instance->system_message = strdup_safe(getenv("R_SYSTEM_MESSAGE")); | 84 instance->system_message = strdup_safe(getenv("R_SYSTEM_MESSAGE")); 110 -   | 111 return instance; | 85 return instance; 112 } | 86 } 113 -   | 114 void r_config_destroy(void) { | 87 void r_config_destroy(void) { 115 if (!instance) return; | 88 if (!instance) return; 116 free(instance->api_url); | 89 free(instance->api_url); 117 free(instance->models_url); | 90 free(instance->models_url); 118 free(instance->model); | 91 free(instance->model); 119 free(instance->api_key); | 92 free(instance->api_key); 120 free(instance->db_path); | 93 free(instance->db_path); 121 free(instance->session_id); | 94 free(instance->session_id); 122 free(instance->system_message); | 95 free(instance->system_message); 123 free(instance); | 96 free(instance); 124 instance = NULL; | 97 instance = NULL; 125 } | 98 } 126 -   | 127 const char *r_config_get_api_url(r_config_handle cfg) { | 99 const char *r_config_get_api_url(r_config_handle cfg) { 128 return cfg ? cfg->api_url : NULL; | 100 return cfg ? cfg->api_url : NULL; 129 } | 101 } 130 -   | 131 const char *r_config_get_models_url(r_config_handle cfg) { | 102 const char *r_config_get_models_url(r_config_handle cfg) { 132 return cfg ? cfg->models_url : NULL; | 103 return cfg ? cfg->models_url : NULL; 133 } | 104 } 134 -   | 135 const char *r_config_get_model(r_config_handle cfg) { | 105 const char *r_config_get_model(r_config_handle cfg) { 136 return cfg ? cfg->model : NULL; | 106 return cfg ? cfg->model : NULL; 137 } | 107 } 138 -   | 139 void r_config_set_model(r_config_handle cfg, const char *model) { | 108 void r_config_set_model(r_config_handle cfg, const char *model) { 140 if (!cfg || !model) return; | 109 if (!cfg || !model) return; 141 free(cfg->model); | 110 free(cfg->model); 142 cfg->model = strdup(model); | 111 cfg->model = strdup(model); 143 } | 112 } 144 -   | 145 const char *r_config_get_api_key(r_config_handle cfg) { | 113 const char *r_config_get_api_key(r_config_handle cfg) { 146 return cfg ? cfg->api_key : NULL; | 114 return cfg ? cfg->api_key : NULL; 147 } | 115 } 148 -   | 149 const char *r_config_get_db_path(r_config_handle cfg) { | 116 const char *r_config_get_db_path(r_config_handle cfg) { 150 return cfg ? cfg->db_path : NULL; | 117 return cfg ? cfg->db_path : NULL; 151 } | 118 } 152 -   | 153 bool r_config_use_tools(r_config_handle cfg) { | 119 bool r_config_use_tools(r_config_handle cfg) { 154 return cfg ? cfg->use_tools : true; | 120 return cfg ? cfg->use_tools : true; 155 } | 121 } 156 -   | 157 bool r_config_use_strict(r_config_handle cfg) { | 122 bool r_config_use_strict(r_config_handle cfg) { 158 return cfg ? cfg->use_strict : true; | 123 return cfg ? cfg->use_strict : true; 159 } | 124 } 160 -   | 161 bool r_config_is_verbose(r_config_handle cfg) { | 125 bool r_config_is_verbose(r_config_handle cfg) { 162 return cfg ? cfg->verbose : false; | 126 return cfg ? cfg->verbose : false; 163 } | 127 } 164 -   | 165 void r_config_set_verbose(r_config_handle cfg, bool verbose) { | 128 void r_config_set_verbose(r_config_handle cfg, bool verbose) { 166 if (cfg) cfg->verbose = verbose; | 129 if (cfg) cfg->verbose = verbose; 167 } | 130 } 168 -   | 169 double r_config_get_temperature(r_config_handle cfg) { | 131 double r_config_get_temperature(r_config_handle cfg) { 170 return cfg ? cfg->temperature : 0.1; | 132 return cfg ? cfg->temperature : 0.1; 171 } | 133 } 172 -   | 173 int r_config_get_max_tokens(r_config_handle cfg) { | 134 int r_config_get_max_tokens(r_config_handle cfg) { 174 return cfg ? cfg->max_tokens : 4096; | 135 return cfg ? cfg->max_tokens : 4096; 175 } | 136 } 176 -   | 177 const char *r_config_get_session_id(r_config_handle cfg) { | 137 const char *r_config_get_session_id(r_config_handle cfg) { 178 return cfg ? cfg->session_id : NULL; | 138 return cfg ? cfg->session_id : NULL; 179 } | 139 } 180 -   | 181 bool r_config_set_session_id(r_config_handle cfg, const char *session_id) { | 140 bool r_config_set_session_id(r_config_handle cfg, const char *session_id) { 182 if (!cfg || !is_valid_session_id(session_id)) return false; | 141 if (!cfg || !is_valid_session_id(session_id)) return false; 183 free(cfg->session_id); | 142 free(cfg->session_id); 184 cfg->session_id = strdup(session_id); | 143 cfg->session_id = strdup(session_id); 185 return cfg->session_id != NULL; | 144 return cfg->session_id != NULL; 186 } | 145 } 187 -   | 188 const char *r_config_get_system_message(r_config_handle cfg) { | 146 const char *r_config_get_system_message(r_config_handle cfg) { 189 return cfg ? cfg->system_message : NULL; | 147 return cfg ? cfg->system_message : NULL; 190 } | 148 } -> Replacing lines in: src/r_diff.c   CHANGES: src/r_diff.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "r_diff.h" | 2 #include "r_diff.h" 4 #include | 3 #include 5 #include | 4 #include 6 #include | 5 #include 7 #include | 6 #include 8 #include | 7 #include 9 -   | 10 #define COLOR_RED "\x1b[31m" | 8 #define COLOR_RED "\x1b[31m" 11 #define COLOR_GREEN "\x1b[32m" | 9 #define COLOR_GREEN "\x1b[32m" 12 #define COLOR_CYAN "\x1b[36m" | 10 #define COLOR_CYAN "\x1b[36m" 13 #define COLOR_RESET "\x1b[0m" | 11 #define COLOR_RESET "\x1b[0m" 14 #define COLOR_DIM "\x1b[2m" | 12 #define COLOR_DIM "\x1b[2m" 15 #define COLOR_BG_RED "\x1b[41;37m" | 13 #define COLOR_BG_RED "\x1b[41;37m" 16 #define COLOR_BG_GREEN "\x1b[42;30m" | 14 #define COLOR_BG_GREEN "\x1b[42;30m" 17 -   | 18 typedef struct { | 15 typedef struct { 19 char **lines; | 16 char **lines; 20 size_t count; | 17 size_t count; 21 } line_set_t; | 18 } line_set_t; 22 -   | 23 static line_set_t split_lines(const char *str) { | 19 static line_set_t split_lines(const char *str) { 24 line_set_t set = {NULL, 0}; | 20 line_set_t set = {NULL, 0}; 25 if (!str || !*str) return set; | 21 if (!str || !*str) return set; 26 -   | 27 char *copy = strdup(str); | 22 char *copy = strdup(str); 28 char *p = copy; | 23 char *p = copy; 29 char *line_start = copy; | 24 char *line_start = copy; 30 -   | 31 while (*p) { | 25 while (*p) { 32 if (*p == '\n') { | 26 if (*p == '\n') { 33 *p = '\0'; | 27 *p = '\0'; 34 set.lines = realloc(set.lines, sizeof(char *) * (set.count + 1)); | 28 set.lines = realloc(set.lines, sizeof(char *) * (set.count + 1)); 35 set.lines[set.count++] = strdup(line_start); | 29 set.lines[set.count++] = strdup(line_start); 36 line_start = p + 1; | 30 line_start = p + 1; 37 } | 31 } 38 p++; | 32 p++; 39 } | 33 } 40 // Handle last line if no trailing newline | 34 // Handle last line if no trailing newline 41 if (*line_start) { | 35 if (*line_start) { 42 set.lines = realloc(set.lines, sizeof(char *) * (set.count + 1)); | 36 set.lines = realloc(set.lines, sizeof(char *) * (set.count + 1)); 43 set.lines[set.count++] = strdup(line_start); | 37 set.lines[set.count++] = strdup(line_start); 44 } | 38 } 45 | 39 46 free(copy); | 40 free(copy); 47 return set; | 41 return set; 48 } | 42 } 49 -   | 50 static void free_line_set(line_set_t set) { | 43 static void free_line_set(line_set_t set) { 51 for (size_t i = 0; i < set.count; i++) free(set.lines[i]); | 44 for (size_t i = 0; i < set.count; i++) free(set.lines[i]); 52 free(set.lines); | 45 free(set.lines); 53 } | 46 } 54 -   | 55 static void print_truncated(const char *str, int width, const char *color) { | 47 static void print_truncated(const char *str, int width, const char *color) { 56 if (width <= 0) return; | 48 if (width <= 0) return; 57 int len = (int)strlen(str); | 49 int len = (int)strlen(str); 58 if (color) printf("%s", color); | 50 if (color) printf("%s", color); 59 | 51 60 if (len > width && width > 3) { | 52 if (len > width && width > 3) { 61 char *temp = strndup(str, (size_t)width - 3); | 53 char *temp = strndup(str, (size_t)width - 3); 62 printf("%s...", temp); | 54 printf("%s...", temp); 63 free(temp); | 55 free(temp); 64 } else { | 56 } else { 65 printf("%-*.*s", width, width, str); | 57 printf("%-*.*s", width, width, str); 66 } | 58 } 67 | 59 68 if (color) printf("%s", COLOR_RESET); | 60 if (color) printf("%s", COLOR_RESET); 69 } | 61 } 70 -   | 71 void r_diff_print(const char *path, const char *old_content, const char *new_content) { | 62 void r_diff_print(const char *path, const char *old_content, const char *new_content) { 72 line_set_t old_set = split_lines(old_content); | 63 line_set_t old_set = split_lines(old_content); 73 line_set_t new_set = split_lines(new_content); | 64 line_set_t new_set = split_lines(new_content); 74 -   | 75 struct winsize w; | 65 struct winsize w; 76 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); | 66 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 77 int term_width = w.ws_col > 0 ? w.ws_col : 120; | 67 int term_width = w.ws_col > 0 ? w.ws_col : 120; 78 if (term_width < 40) term_width = 40; // Minimum usable width | 68 if (term_width < 40) term_width = 40; // Minimum usable width 79 int col_width = (term_width - 15) / 2; | 69 int col_width = (term_width - 15) / 2; 80 if (col_width < 5) col_width = 5; | 70 if (col_width < 5) col_width = 5; 81 -   | 82 printf("\n%s %s CHANGES: %s %s\n", COLOR_CYAN, COLOR_DIM, path, COLOR_RESET); | 71 printf("\n%s %s CHANGES: %s %s\n", COLOR_CYAN, COLOR_DIM, path, COLOR_RESET); 83 printf("%4s %-*s | %4s %-*s\n", "LINE", col_width, " OLD", "LINE", col_width, " NEW"); | 72 printf("%4s %-*s | %4s %-*s\n", "LINE", col_width, " OLD", "LINE", col_width, " NEW"); 84 printf("%.*s\n", term_width, "--------------------------------------------------------------------------------------------------------------------------------------------"); | 73 printf("%.*s\n", term_width, "--------------------------------------------------------------------------------------------------------------------------------------------"); 85 -   | 86 size_t o = 0, n = 0; | 74 size_t o = 0, n = 0; 87 while (o < old_set.count || n < new_set.count) { | 75 while (o < old_set.count || n < new_set.count) { 88 bool match = false; | 76 bool match = false; 89 if (o < old_set.count && n < new_set.count) { | 77 if (o < old_set.count && n < new_set.count) { 90 if (strcmp(old_set.lines[o], new_set.lines[n]) == 0) { | 78 if (strcmp(old_set.lines[o], new_set.lines[n]) == 0) { 91 printf("%4zu ", o + 1); | 79 printf("%4zu ", o + 1); 92 print_truncated(old_set.lines[o], col_width - 2, NULL); | 80 print_truncated(old_set.lines[o], col_width - 2, NULL); 93 printf(" | %4zu ", n + 1); | 81 printf(" | %4zu ", n + 1); 94 print_truncated(new_set.lines[n], col_width - 2, NULL); | 82 print_truncated(new_set.lines[n], col_width - 2, NULL); 95 printf("\n"); | 83 printf("\n"); 96 o++; n++; | 84 o++; n++; 97 match = true; | 85 match = true; 98 } | 86 } 99 } | 87 } 100 -   | 101 if (!match) { | 88 if (!match) { 102 bool found_o_later = false; | 89 bool found_o_later = false; 103 if (o < old_set.count) { | 90 if (o < old_set.count) { 104 for (size_t i = n + 1; i < new_set.count && i < n + 5; i++) { | 91 for (size_t i = n + 1; i < new_set.count && i < n + 5; i++) { 105 if (strcmp(old_set.lines[o], new_set.lines[i]) == 0) { | 92 if (strcmp(old_set.lines[o], new_set.lines[i]) == 0) { 106 found_o_later = true; | 93 found_o_later = true; 107 break; | 94 break; 108 } | 95 } 109 } | 96 } 110 } | 97 } 111 -   | 112 if (found_o_later) { | 98 if (found_o_later) { 113 // Line added on NEW side | 99 // Line added on NEW side 114 printf("%4s ", ""); | 100 printf("%4s ", ""); 115 print_truncated("", col_width - 2, NULL); | 101 print_truncated("", col_width - 2, NULL); 116 printf(" | %4zu %s+%s ", n + 1, COLOR_GREEN, COLOR_RESET); | 102 printf(" | %4zu %s+%s ", n + 1, COLOR_GREEN, COLOR_RESET); 117 print_truncated(new_set.lines[n], col_width - 2, COLOR_GREEN); | 103 print_truncated(new_set.lines[n], col_width - 2, COLOR_GREEN); 118 printf("\n"); | 104 printf("\n"); 119 n++; | 105 n++; 120 } else if (o < old_set.count) { | 106 } else if (o < old_set.count) { 121 // Line removed on OLD side | 107 // Line removed on OLD side 122 printf("%4zu %s-%s ", o + 1, COLOR_RED, COLOR_RESET); | 108 printf("%4zu %s-%s ", o + 1, COLOR_RED, COLOR_RESET); 123 print_truncated(old_set.lines[o], col_width - 2, COLOR_RED); | 109 print_truncated(old_set.lines[o], col_width - 2, COLOR_RED); 124 printf(" | %4s ", ""); | 110 printf(" | %4s ", ""); 125 print_truncated("", col_width - 2, NULL); | 111 print_truncated("", col_width - 2, NULL); 126 printf("\n"); | 112 printf("\n"); 127 o++; | 113 o++; 128 } else if (n < new_set.count) { | 114 } else if (n < new_set.count) { 129 // Line added on NEW side | 115 // Line added on NEW side 130 printf("%4s ", ""); | 116 printf("%4s ", ""); 131 print_truncated("", col_width - 2, NULL); | 117 print_truncated("", col_width - 2, NULL); 132 printf(" | %4zu %s+%s ", n + 1, COLOR_GREEN, COLOR_RESET); | 118 printf(" | %4zu %s+%s ", n + 1, COLOR_GREEN, COLOR_RESET); 133 print_truncated(new_set.lines[n], col_width - 2, COLOR_GREEN); | 119 print_truncated(new_set.lines[n], col_width - 2, COLOR_GREEN); 134 printf("\n"); | 120 printf("\n"); 135 n++; | 121 n++; 136 } | 122 } 137 } | 123 } 138 fflush(stdout); | 124 fflush(stdout); 139 usleep(30000); // 30ms delay for streaming effect | 125 usleep(30000); // 30ms delay for streaming effect 140 } | 126 } 141 printf("\n"); | 127 printf("\n"); 142 fflush(stdout); | 128 fflush(stdout); 143 -   | 144 free_line_set(old_set); | 129 free_line_set(old_set); 145 free_line_set(new_set); | 130 free_line_set(new_set); 146 } | 131 } -> Replacing lines in: src/r_error.c   CHANGES: src/r_error.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "r_error.h" | 2 #include "r_error.h" 4 -   | 5 static const char *error_messages[] = { | 3 static const char *error_messages[] = { 6 [R_SUCCESS] = "Success", | 4 [R_SUCCESS] = "Success", 7 [R_ERROR_INVALID_ARG] = "Invalid argument", | 5 [R_ERROR_INVALID_ARG] = "Invalid argument", 8 [R_ERROR_OUT_OF_MEMORY] = "Out of memory", | 6 [R_ERROR_OUT_OF_MEMORY] = "Out of memory", 9 [R_ERROR_NOT_FOUND] = "Not found", | 7 [R_ERROR_NOT_FOUND] = "Not found", 10 [R_ERROR_PARSE] = "Parse error", | 8 [R_ERROR_PARSE] = "Parse error", 11 [R_ERROR_DB_CONNECTION] = "Database connection failed", | 9 [R_ERROR_DB_CONNECTION] = "Database connection failed", 12 [R_ERROR_DB_QUERY] = "Database query failed", | 10 [R_ERROR_DB_QUERY] = "Database query failed", 13 [R_ERROR_DB_NOT_FOUND] = "Database key not found", | 11 [R_ERROR_DB_NOT_FOUND] = "Database key not found", 14 [R_ERROR_HTTP_CONNECTION] = "HTTP connection failed", | 12 [R_ERROR_HTTP_CONNECTION] = "HTTP connection failed", 15 [R_ERROR_HTTP_TIMEOUT] = "HTTP request timed out", | 13 [R_ERROR_HTTP_TIMEOUT] = "HTTP request timed out", 16 [R_ERROR_HTTP_RESPONSE] = "HTTP response error", | 14 [R_ERROR_HTTP_RESPONSE] = "HTTP response error", 17 [R_ERROR_JSON_PARSE] = "JSON parse error", | 15 [R_ERROR_JSON_PARSE] = "JSON parse error", 18 [R_ERROR_FILE_NOT_FOUND] = "File not found", | 16 [R_ERROR_FILE_NOT_FOUND] = "File not found", 19 [R_ERROR_FILE_READ] = "File read error", | 17 [R_ERROR_FILE_READ] = "File read error", 20 [R_ERROR_FILE_WRITE] = "File write error", | 18 [R_ERROR_FILE_WRITE] = "File write error", 21 [R_ERROR_TOOL_NOT_FOUND] = "Tool not found", | 19 [R_ERROR_TOOL_NOT_FOUND] = "Tool not found", 22 [R_ERROR_TOOL_EXECUTION] = "Tool execution failed", | 20 [R_ERROR_TOOL_EXECUTION] = "Tool execution failed", 23 [R_ERROR_API_KEY_MISSING] = "API key missing", | 21 [R_ERROR_API_KEY_MISSING] = "API key missing", 24 [R_ERROR_API_ERROR] = "API error", | 22 [R_ERROR_API_ERROR] = "API error", 25 [R_ERROR_MAX_ITERATIONS] = "Maximum iterations reached", | 23 [R_ERROR_MAX_ITERATIONS] = "Maximum iterations reached", 26 [R_ERROR_SESSION_INVALID] = "Invalid session name", | 24 [R_ERROR_SESSION_INVALID] = "Invalid session name", 27 [R_ERROR_UNKNOWN] = "Unknown error" | 25 [R_ERROR_UNKNOWN] = "Unknown error" 28 }; | 26 }; 29 -   | 30 const char *r_status_string(r_status_t status) { | 27 const char *r_status_string(r_status_t status) { 31 if (status < 0 || status > R_ERROR_UNKNOWN) { | 28 if (status < 0 || status > R_ERROR_UNKNOWN) { 32 return error_messages[R_ERROR_UNKNOWN]; | 29 return error_messages[R_ERROR_UNKNOWN]; 33 } | 30 } 34 return error_messages[status]; | 31 return error_messages[status]; 35 } | 32 } -> Replacing lines in: src/tool_registry.c   CHANGES: src/tool_registry.c  LINE OLD | LINE NEW -------------------------------------------------------------------------------------------------------------------------------------------- 1 // retoor | 1 // retoor 2 -   | 3 #include "tool.h" | 2 #include "tool.h" 4 #include | 3 #include 5 #include | 4 #include 6 #include | 5 #include 7 #include | 6 #include 8 -   | 9 typedef struct { | 7 typedef struct { 10 tool_t *tool; | 8 tool_t *tool; 11 struct json_object *args; | 9 struct json_object *args; 12 char *output; | 10 char *output; 13 } tool_thread_args_t; | 11 } tool_thread_args_t; 14 -   | 15 static void *tool_thread_func(void *ptr) { | 12 static void *tool_thread_func(void *ptr) { 16 tool_thread_args_t *args = (tool_thread_args_t *)ptr; | 13 tool_thread_args_t *args = (tool_thread_args_t *)ptr; 17 if (args->tool->vtable->execute) { | 14 if (args->tool->vtable->execute) { 18 args->output = args->tool->vtable->execute(args->tool, args->args); | 15 args->output = args->tool->vtable->execute(args->tool, args->args); 19 } | 16 } 20 return NULL; | 17 return NULL; 21 } | 18 } 22 -   | 23 tool_registry_t *tool_registry_create(void) { | 19 tool_registry_t *tool_registry_create(void) { 24 tool_registry_t *registry = calloc(1, sizeof(tool_registry_t)); | 20 tool_registry_t *registry = calloc(1, sizeof(tool_registry_t)); 25 if (!registry) return NULL; | 21 if (!registry) return NULL; 26 -   | 27 registry->capacity = 32; | 22 registry->capacity = 32; 28 registry->tools = calloc(registry->capacity, sizeof(tool_t *)); | 23 registry->tools = calloc(registry->capacity, sizeof(tool_t *)); 29 if (!registry->tools) { | 24 if (!registry->tools) { 30 free(registry); | 25 free(registry); 31 return NULL; | 26 return NULL; 32 } | 27 } 33 return registry; | 28 return registry; 34 } | 29 } 35 -   | 36 void tool_registry_destroy(tool_registry_t *registry) { | 30 void tool_registry_destroy(tool_registry_t *registry) { 37 if (!registry) return; | 31 if (!registry) return; 38 free(registry->tools); | 32 free(registry->tools); 39 free(registry); | 33 free(registry); 40 } | 34 } 41 -   | 42 r_status_t tool_registry_register(tool_registry_t *registry, tool_t *tool) { | 35 r_status_t tool_registry_register(tool_registry_t *registry, tool_t *tool) { 43 if (!registry || !tool) return R_ERROR_INVALID_ARG; | 36 if (!registry || !tool) return R_ERROR_INVALID_ARG; 44 -   | 45 if (registry->count >= registry->capacity) { | 37 if (registry->count >= registry->capacity) { 46 size_t new_capacity = registry->capacity * 2; | 38 size_t new_capacity = registry->capacity * 2; 47 tool_t **new_tools = realloc(registry->tools, new_capacity * sizeof(tool_t *)); | 39 tool_t **new_tools = realloc(registry->tools, new_capacity * sizeof(tool_t *)); 48 if (!new_tools) return R_ERROR_OUT_OF_MEMORY; | 40 if (!new_tools) return R_ERROR_OUT_OF_MEMORY; 49 registry->tools = new_tools; | 41 registry->tools = new_tools; 50 registry->capacity = new_capacity; | 42 registry->capacity = new_capacity; 51 } | 43 } 52 -   | 53 registry->tools[registry->count++] = tool; | 44 registry->tools[registry->count++] = tool; 54 return R_SUCCESS; | 45 return R_SUCCESS; 55 } | 46 } 56 -   | 57 tool_t *tool_registry_find(tool_registry_t *registry, const char *name) { | 47 tool_t *tool_registry_find(tool_registry_t *registry, const char *name) { 58 if (!registry || !name) return NULL; | 48 if (!registry || !name) return NULL; 59 -   | 60 for (size_t i = 0; i < registry->count; i++) { | 49 for (size_t i = 0; i < registry->count; i++) { 61 if (strcmp(registry->tools[i]->name, name) == 0) { | 50 if (strcmp(registry->tools[i]->name, name) == 0) { 62 return registry->tools[i]; | 51 return registry->tools[i]; 63 } | 52 } 64 } | 53 } 65 return NULL; | 54 return NULL; 66 } | 55 } 67 -   | 68 struct json_object *tool_registry_get_descriptions(tool_registry_t *registry) { | 56 struct json_object *tool_registry_get_descriptions(tool_registry_t *registry) { 69 if (!registry) return NULL; | 57 if (!registry) return NULL; 70 -   | 71 struct json_object *array = json_object_new_array(); | 58 struct json_object *array = json_object_new_array(); 72 if (!array) return NULL; | 59 if (!array) return NULL; 73 -   | 74 for (size_t i = 0; i < registry->count; i++) { | 60 for (size_t i = 0; i < registry->count; i++) { 75 tool_t *tool = registry->tools[i]; | 61 tool_t *tool = registry->tools[i]; 76 if (tool->vtable->get_description) { | 62 if (tool->vtable->get_description) { 77 struct json_object *desc = tool->vtable->get_description(); | 63 struct json_object *desc = tool->vtable->get_description(); 78 if (desc) { | 64 if (desc) { 79 json_object_array_add(array, desc); | 65 json_object_array_add(array, desc); 80 } | 66 } 81 } | 67 } 82 } | 68 } 83 return array; | 69 return array; 84 } | 70 } 85 -   | 86 struct json_object *tool_registry_execute(tool_registry_t *registry, | 71 struct json_object *tool_registry_execute(tool_registry_t *registry, 87 -   | 88 struct json_object *tool_calls, | 72 struct json_object *tool_calls, 89 -   | 90 bool verbose) { | 73 bool verbose) { 91 -   | 92 if (!registry || !tool_calls) return NULL; | 74 if (!registry || !tool_calls) return NULL; 93 -   | 94 -   | 95 -   | 96 struct json_object *results = json_object_new_array(); | 75 struct json_object *results = json_object_new_array(); 97 -   | 98 if (!results) return NULL; | 76 if (!results) return NULL; 99 -   | 100 -   | 101 -   | 102 int len = json_object_array_length(tool_calls); | 77 int len = json_object_array_length(tool_calls); 103 -   | 104 if (len == 0) return results; | 78 if (len == 0) return results; 105 -   | 106 -   | 107 -   | 108 pthread_t *threads = calloc((size_t)len, sizeof(pthread_t)); | 79 pthread_t *threads = calloc((size_t)len, sizeof(pthread_t)); 109 -   | 110 tool_thread_args_t *t_args = calloc((size_t)len, sizeof(tool_thread_args_t)); | 80 tool_thread_args_t *t_args = calloc((size_t)len, sizeof(tool_thread_args_t)); 111 -   | 112 struct json_object **result_objs = calloc((size_t)len, sizeof(struct json_object *)); | 81 struct json_object **result_objs = calloc((size_t)len, sizeof(struct json_object *)); 113 -   | 114 bool *is_duplicate = calloc((size_t)len, sizeof(bool)); | 82 bool *is_duplicate = calloc((size_t)len, sizeof(bool)); 115 -   | 116 -   | 117 -   | 118 for (int i = 0; i < len; i++) { | 83 for (int i = 0; i < len; i++) { 119 -   | 120 struct json_object *call = json_object_array_get_idx(tool_calls, i); | 84 struct json_object *call = json_object_array_get_idx(tool_calls, i); 121 -   | 122 result_objs[i] = json_object_new_object(); | 85 result_objs[i] = json_object_new_object(); 123 -   | 124 -   | 125 -   | 126 struct json_object *id_obj = json_object_object_get(call, "id"); | 86 struct json_object *id_obj = json_object_object_get(call, "id"); 127 -   | 128 if (id_obj) { | 87 if (id_obj) { 129 -   | 130 json_object_object_add(result_objs[i], "tool_call_id", | 88 json_object_object_add(result_objs[i], "tool_call_id", 131 -   | 132 json_object_new_string(json_object_get_string(id_obj))); | 89 json_object_new_string(json_object_get_string(id_obj))); 133 -   | 134 } | 90 } 135 -   | 136 json_object_object_add(result_objs[i], "role", json_object_new_string("tool")); | 91 json_object_object_add(result_objs[i], "role", json_object_new_string("tool")); 137 -   | 138 -   | 139 -   | 140 struct json_object *function_obj; | 92 struct json_object *function_obj; 141 -   | 142 if (!json_object_object_get_ex(call, "function", &function_obj)) { | 93 if (!json_object_object_get_ex(call, "function", &function_obj)) { 143 -   | 144 json_object_object_add(result_objs[i], "content", json_object_new_string("Error: missing function")); | 94 json_object_object_add(result_objs[i], "content", json_object_new_string("Error: missing function")); 145 -   | 146 continue; | 95 continue; 147 -   | 148 } | 96 } 149 -   | 150 -   | 151 -   | 152 const char *name = json_object_get_string(json_object_object_get(function_obj, "name")); | 97 const char *name = json_object_get_string(json_object_object_get(function_obj, "name")); 153 -   | 154 const char *args_json = json_object_get_string(json_object_object_get(function_obj, "arguments")); | 98 const char *args_json = json_object_get_string(json_object_object_get(function_obj, "arguments")); 155 -   | 156 -   | 157 -   | 158 // DEDUPLICATION LOGIC: Check if this exact call (name + args) appeared earlier in this batch | 99 // DEDUPLICATION LOGIC: Check if this exact call (name + args) appeared earlier in this batch 159 -   | 160 for (int j = 0; j < i; j++) { | 100 for (int j = 0; j < i; j++) { 161 -   | 162 struct json_object *prev_call = json_object_array_get_idx(tool_calls, j); | 101 struct json_object *prev_call = json_object_array_get_idx(tool_calls, j); 163 -   | 164 struct json_object *prev_func; | 102 struct json_object *prev_func; 165 -   | 166 json_object_object_get_ex(prev_call, "function", &prev_func); | 103 json_object_object_get_ex(prev_call, "function", &prev_func); 167 -   | 168 const char *prev_name = json_object_get_string(json_object_object_get(prev_func, "name")); | 104 const char *prev_name = json_object_get_string(json_object_object_get(prev_func, "name")); 169 -   | 170 const char *prev_args = json_object_get_string(json_object_object_get(prev_func, "arguments")); | 105 const char *prev_args = json_object_get_string(json_object_object_get(prev_func, "arguments")); 171 -   | 172 -   | 173 -   | 174 if (strcmp(name, prev_name) == 0 && strcmp(args_json, prev_args) == 0) { | 106 if (strcmp(name, prev_name) == 0 && strcmp(args_json, prev_args) == 0) { 175 -   | 176 is_duplicate[i] = true; | 107 is_duplicate[i] = true; 177 -   | 178 if (verbose) { | 108 if (verbose) { 179 -   | 180 fprintf(stderr, " \033[1;33m[Registry] Redundant call to %s prevented.\033[0m\n", name); | 109 fprintf(stderr, " \033[1;33m[Registry] Redundant call to %s prevented.\033[0m\n", name); 181 -   | 182 } | 110 } 183 -   | 184 break; | 111 break; 185 -   | 186 } | 112 } 187 -   | 188 } | 113 } 189 -   | 190 -   | 191 -   | 192 if (is_duplicate[i]) continue; | 114 if (is_duplicate[i]) continue; 193 -   | 194 -   | 195 -   | 196 tool_t *tool = tool_registry_find(registry, name); | 115 tool_t *tool = tool_registry_find(registry, name); 197 -   | 198 if (!tool) { | 116 if (!tool) { 199 -   | 200 json_object_object_add(result_objs[i], "content", json_object_new_string("Error: tool not found")); | 117 json_object_object_add(result_objs[i], "content", json_object_new_string("Error: tool not found")); 201 -   | 202 continue; | 118 continue; 203 -   | 204 } | 119 } 205 -   | 206 -   | 207 -   | 208 struct json_object *args = json_tokener_parse(args_json); | 120 struct json_object *args = json_tokener_parse(args_json); 209 -   | 210 -   | 211 -   | 212 if (tool->vtable->print_action) { | 121 if (tool->vtable->print_action) { 213 -   | 214 tool->vtable->print_action(tool->name, args); | 122 tool->vtable->print_action(tool->name, args); 215 -   | 216 } | 123 } 217 -   | 218 -   | 219 -   | 220 if (verbose && args) { | 124 if (verbose && args) { 221 -   | 222 fprintf(stderr, " \033[2m[parallel] launching %s\033[0m\n", tool->name); | 125 fprintf(stderr, " \033[2m[parallel] launching %s\033[0m\n", tool->name); 223 -   | 224 } | 126 } 225 -   | 226 -   | 227 -   | 228 t_args[i].tool = tool; | 127 t_args[i].tool = tool; 229 -   | 230 t_args[i].args = args; | 128 t_args[i].args = args; 231 -   | 232 t_args[i].output = NULL; | 129 t_args[i].output = NULL; 233 -   | 234 -   | 235 -   | 236 pthread_create(&threads[i], NULL, tool_thread_func, &t_args[i]); | 130 pthread_create(&threads[i], NULL, tool_thread_func, &t_args[i]); 237 -   | 238 } | 131 } 239 -   | 240 -   | 241 -   | 242 for (int i = 0; i < len; i++) { | 132 for (int i = 0; i < len; i++) { 243 -   | 244 -   | 245 -   | 246 if (is_duplicate[i]) { | 133 if (is_duplicate[i]) { 247 -   | 248 -   | 249 -   | 250 // Find the original result to copy it | 134 // Find the original result to copy it 251 -   | 252 -   | 253 -   | 254 struct json_object *curr_func; | 135 struct json_object *curr_func; 255 -   | 256 -   | 257 -   | 258 json_object_object_get_ex(json_object_array_get_idx(tool_calls, i), "function", &curr_func); | 136 json_object_object_get_ex(json_object_array_get_idx(tool_calls, i), "function", &curr_func); 259 -   | 260 -   | 261 -   | 262 const char *name = json_object_get_string(json_object_object_get(curr_func, "name")); | 137 const char *name = json_object_get_string(json_object_object_get(curr_func, "name")); 263 -   | 264 -   | 265 -   | 266 const char *args_json = json_object_get_string(json_object_object_get(curr_func, "arguments")); | 138 const char *args_json = json_object_get_string(json_object_object_get(curr_func, "arguments")); 267 -   | 268 -   | 269 -   | 270 | 139 271 -   | 272 -   | 273 -   | 274 for (int j = 0; j < i; j++) { | 140 for (int j = 0; j < i; j++) { 275 -   | 276 -   | 277 -   | 278 struct json_object *prev_func; | 141 struct json_object *prev_func; 279 -   | 280 -   | 281 -   | 282 json_object_object_get_ex(json_object_array_get_idx(tool_calls, j), "function", &prev_func); | 142 json_object_object_get_ex(json_object_array_get_idx(tool_calls, j), "function", &prev_func); 283 -   | 284 -   | 285 -   | 286 if (strcmp(name, json_object_get_string(json_object_object_get(prev_func, "name"))) == 0 && | 143 if (strcmp(name, json_object_get_string(json_object_object_get(prev_func, "name"))) == 0 && 287 -   | 288 -   | 289 -   | 290 strcmp(args_json, json_object_get_string(json_object_object_get(prev_func, "arguments"))) == 0) { | 144 strcmp(args_json, json_object_get_string(json_object_object_get(prev_func, "arguments"))) == 0) { 291 -   | 292 -   | 293 -   | 294 | 145 295 -   | 296 | 146 297 -   | 298 struct json_object *orig_content; | 147 struct json_object *orig_content; 299 -   | 300 if (json_object_object_get_ex(result_objs[j], "content", &orig_content)) { | 148 if (json_object_object_get_ex(result_objs[j], "content", &orig_content)) { 301 -   | 302 json_object_object_add(result_objs[i], "content", json_object_get(orig_content)); | 149 json_object_object_add(result_objs[i], "content", json_object_get(orig_content)); 303 -   | 304 } else { | 150 } else { 305 -   | 306 // Original hasn't finished yet or failed | 151 // Original hasn't finished yet or failed 307 -   | 308 json_object_object_add(result_objs[i], "content", json_object_new_string("Result mirrored from previous parallel call.")); | 152 json_object_object_add(result_objs[i], "content", json_object_new_string("Result mirrored from previous parallel call.")); 309 -   | 310 } | 153 } 311 -   | 312 break; | 154 break; 313 -   | 314 } | 155 } 315 -   | 316 } | 156 } 317 -   | 318 } else { | 157 } else { 319 -   | 320 if (threads[i]) { | 158 if (threads[i]) { 321 -   | 322 pthread_join(threads[i], NULL); | 159 pthread_join(threads[i], NULL); 323 -   | 324 } | 160 } 325 -   | 326 | 161 327 -   | 328 char *output = t_args[i].output ? t_args[i].output : ""; | 162 char *output = t_args[i].output ? t_args[i].output : ""; 329 -   | 330 json_object_object_add(result_objs[i], "content", json_object_new_string(output)); | 163 json_object_object_add(result_objs[i], "content", json_object_new_string(output)); 331 -   | 332 | 164 333 -   | 334 if (output && strncmp(output, "Error:", 6) == 0) { | 165 if (output && strncmp(output, "Error:", 6) == 0) { 335 -   | 336 fprintf(stderr, "\033[1;31m[Tool Error] %s\033[0m\n", output); | 166 fprintf(stderr, "\033[1;31m[Tool Error] %s\033[0m\n", output); 337 -   | 338 } | 167 } 339 -   | 340 | 168 341 -   | 342 free(t_args[i].output); | 169 free(t_args[i].output); 343 -   | 344 if (t_args[i].args) json_object_put(t_args[i].args); | 170 if (t_args[i].args) json_object_put(t_args[i].args); 345 -   | 346 } | 171 } 347 -   | 348 json_object_array_add(results, result_objs[i]); | 172 json_object_array_add(results, result_objs[i]); 349 -   | 350 } | 173 } 351 -   | 352 -   | 353 -   | 354 free(threads); | 174 free(threads); 355 -   | 356 free(t_args); | 175 free(t_args); 357 -   | 358 free(result_objs); | 176 free(result_objs); 359 -   | 360 free(is_duplicate); | 177 free(is_duplicate); 361 -   | 362 -   | 363 -   | 364 return results; | 178 return results; 365 -   | 366 } | 179 } All .c and .h files in the src directory now start with the comment // retoor .