Update.
This commit is contained in:
parent
c343e17f19
commit
77885e73a9
3
Makefile
3
Makefile
@ -34,7 +34,8 @@ SRC_TOOLS = $(TOOLSDIR)/tools_init.c \
|
||||
$(TOOLSDIR)/tool_network.c \
|
||||
$(TOOLSDIR)/tool_dns.c \
|
||||
$(TOOLSDIR)/tool_automation.c \
|
||||
$(TOOLSDIR)/tool_csv.c
|
||||
$(TOOLSDIR)/tool_csv.c \
|
||||
$(TOOLSDIR)/tool_agent.c
|
||||
|
||||
SRC = $(SRC_CORE) $(SRC_TOOLS)
|
||||
|
||||
|
||||
195
agent_benchmark.py
Executable file
195
agent_benchmark.py
Executable file
@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from typing import List, Dict, Any
|
||||
|
||||
# Configure logging
|
||||
LOG_FILE = "benchmark_results.log"
|
||||
AGENT_OUTPUT_DIR = "test_results"
|
||||
os.makedirs(AGENT_OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s [%(levelname)s] %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler(LOG_FILE),
|
||||
logging.StreamHandler(sys.stdout)
|
||||
]
|
||||
)
|
||||
|
||||
class TestCase:
|
||||
def __init__(self, id: str, name: str, description: str, task: str, validation_fn: Any):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.task = task
|
||||
self.validation_fn = validation_fn
|
||||
self.result = "PENDING"
|
||||
self.output = ""
|
||||
self.execution_time = 0
|
||||
|
||||
def validate_file_exists(path):
|
||||
return os.path.exists(path)
|
||||
|
||||
def validate_file_contains(path, text):
|
||||
if not os.path.exists(path): return False
|
||||
with open(path, 'r') as f:
|
||||
return text.lower() in f.read().lower()
|
||||
|
||||
class AgentBenchmark:
|
||||
def __init__(self, binary_path: str = "./r"):
|
||||
self.binary_path = binary_path
|
||||
self.test_cases: List[TestCase] = []
|
||||
|
||||
def add_test(self, test: TestCase):
|
||||
self.test_cases.append(test)
|
||||
|
||||
def run_all(self):
|
||||
logging.info(f"Starting benchmark with {len(self.test_cases)} tasks...")
|
||||
for test in self.test_cases:
|
||||
self.run_test(test)
|
||||
|
||||
self.summary()
|
||||
|
||||
def run_test(self, test: TestCase):
|
||||
logging.info(f"--- Running Test {test.id}: {test.name} ---")
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
# Execute the agent
|
||||
process = subprocess.Popen(
|
||||
[self.binary_path, "--verbose", test.task],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
bufsize=1
|
||||
)
|
||||
|
||||
full_output = []
|
||||
logging.info(f"Agent executing Task {test.id}...")
|
||||
|
||||
for line in process.stdout:
|
||||
full_output.append(line)
|
||||
print(line, end="", flush=True) # Print to screen in real-time
|
||||
|
||||
process.wait(timeout=600) # 10 minute timeout per task
|
||||
test.execution_time = time.time() - start_time
|
||||
test.output = "".join(full_output)
|
||||
|
||||
# Save raw agent output
|
||||
output_file = os.path.join(AGENT_OUTPUT_DIR, f"{test.id}_output.txt")
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(f"TASK: {test.task}\n")
|
||||
f.write("-" * 40 + "\n")
|
||||
f.write(test.output)
|
||||
|
||||
# Validate
|
||||
if test.validation_fn(test):
|
||||
test.result = "PASSED"
|
||||
logging.info(f"Test {test.id} PASSED in {test.execution_time:.2f}s")
|
||||
else:
|
||||
test.result = "FAILED"
|
||||
logging.error(f"Test {test.id} FAILED validation")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error executing test {test.id}: {str(e)}")
|
||||
test.result = "ERROR"
|
||||
|
||||
def summary(self):
|
||||
logging.info("=" * 50)
|
||||
logging.info("BENCHMARK SUMMARY")
|
||||
logging.info("=" * 50)
|
||||
passed = sum(1 for t in self.test_cases if t.result == "PASSED")
|
||||
for t in self.test_cases:
|
||||
logging.info(f"[{t.result}] {t.id}: {t.name} ({t.execution_time:.2f}s)")
|
||||
|
||||
logging.info("=" * 50)
|
||||
logging.info(f"TOTAL PASSED: {passed}/{len(self.test_cases)}")
|
||||
logging.info("=" * 50)
|
||||
|
||||
# Validation Functions
|
||||
def v01(t): return validate_file_contains("sorting_algo.py", "def quicksort")
|
||||
def v02(t): return validate_file_exists("refactor_report.md")
|
||||
def v03(t): return validate_file_exists("security_scan.txt")
|
||||
def v04(t): return validate_file_exists("data_export.csv")
|
||||
def v05(t): return validate_file_exists("system_monitor.py")
|
||||
def v06(t): return validate_file_exists("cloud_comparison.md")
|
||||
def v07(t): return validate_file_exists("network_report.txt")
|
||||
def v08(t): return validate_file_exists("db_migration.sql")
|
||||
def v09(t): return validate_file_contains("src/main.c", "retoor") # Dummy check
|
||||
def v10(t): return validate_file_exists("CODE_DOCS.md")
|
||||
def v11(t): return validate_file_exists("log_analysis.json")
|
||||
def v12(t): return validate_file_exists("venv_test/bin/python") or validate_file_exists("venv_test/Scripts/python.exe")
|
||||
def v13(t): return validate_file_exists("git_summary.md")
|
||||
def v14(t): return validate_file_exists("research_and_demo.py")
|
||||
def v15(t): return validate_file_exists("stats_summary.txt")
|
||||
|
||||
if __name__ == "__main__":
|
||||
benchmark = AgentBenchmark()
|
||||
|
||||
# 1. Research & Develop
|
||||
benchmark.add_test(TestCase("T01", "Research & Develop", "Research Quicksort and implement it",
|
||||
"Research the Quicksort algorithm and write a robust Python implementation to 'sorting_algo.py'.", v01))
|
||||
|
||||
# 2. Code Analysis & Refactor
|
||||
benchmark.add_test(TestCase("T02", "Refactor Suggestion", "Index project and suggest refactor",
|
||||
"Index the current source directory and identify a complex function in src/agent.c. Suggest a refactor and save it to 'refactor_report.md'.", v02))
|
||||
|
||||
# 3. Security Audit
|
||||
benchmark.add_test(TestCase("T03", "Security Audit", "Scan for security issues",
|
||||
"Perform a security audit of the current directory using your tools. Look for insecure patterns and save findings to 'security_scan.txt'.", v03))
|
||||
|
||||
# 4. Data ETL Pipeline
|
||||
benchmark.add_test(TestCase("T04", "Data ETL", "Fetch, process, store, export",
|
||||
"Fetch data from https://jsonplaceholder.typicode.com/users, process it to extract just names and emails, store it in a local SQLite table named 'bench_users', and export it to 'data_export.csv'.", v04))
|
||||
|
||||
# 5. System Monitoring
|
||||
benchmark.add_test(TestCase("T05", "System Monitor", "Create monitoring script",
|
||||
"Write a Python script 'system_monitor.py' that logs CPU and memory usage to 'usage.log' every 5 seconds. Ensure it handles keyboard interrupts.", v05))
|
||||
|
||||
# 6. Web Research
|
||||
benchmark.add_test(TestCase("T06", "Web Research", "Compare cloud providers",
|
||||
"Research and compare the latest AI offerings from AWS, Azure, and Google Cloud in 2026. Create a comparison table in 'cloud_comparison.md'.", v06))
|
||||
|
||||
# 7. Network Diagnosis
|
||||
benchmark.add_test(TestCase("T07", "Network Diagnosis", "Check connectivity and DNS",
|
||||
"Check network connectivity to google.com and github.com. Perform DNS lookups and save a report with latency to 'network_report.txt'.", v07))
|
||||
|
||||
# 8. DB Migration
|
||||
benchmark.add_test(TestCase("T08", "DB Migration", "Create and migrate schema",
|
||||
"Create an SQLite schema for a library system (books, authors), insert 5 sample records, and generate a SQL dump to 'db_migration.sql'.", v08))
|
||||
|
||||
# 9. Code Maintenance
|
||||
benchmark.add_test(TestCase("T09", "Code Maintenance", "Verify headers",
|
||||
"Ensure all .c and .h files in the src directory start with the comment '// retoor <retoor@molodetz.nl>'. If missing, add it.", v09))
|
||||
|
||||
# 10. Documentation Generator
|
||||
benchmark.add_test(TestCase("T10", "Docs Generator", "Generate markdown docs",
|
||||
"Analyze src/agent.c and include/agent.h to extract public function signatures and generate a professional 'CODE_DOCS.md'.", v10))
|
||||
|
||||
# 11. Log Analysis
|
||||
benchmark.add_test(TestCase("T11", "Log Analysis", "Parse and categorize logs",
|
||||
"Create a dummy log file with 20 lines of mixed INFO and ERROR messages. Parse it using Python to count errors and save a JSON summary to 'log_analysis.json'.", v11))
|
||||
|
||||
# 12. Env Setup
|
||||
benchmark.add_test(TestCase("T12", "Env Setup", "Create virtualenv",
|
||||
"Create a Python virtual environment named 'venv_test' in the current directory.", v12))
|
||||
|
||||
# 13. Git Summary
|
||||
benchmark.add_test(TestCase("T13", "Git Summary", "Summarize git history",
|
||||
"Get the last 5 git commit messages and summarize the changes in 'git_summary.md'.", v13))
|
||||
|
||||
# 14. Multi-agent Collaboration
|
||||
benchmark.add_test(TestCase("T14", "Agent Collaboration", "Research and Code",
|
||||
"Spawn a researcher to find the best way to implement a websocket server in Python, then write a functional demo to 'research_and_demo.py'.", v14))
|
||||
|
||||
# 15. CSV Processing
|
||||
benchmark.add_test(TestCase("T15", "CSV Stats", "Process large CSV",
|
||||
"Create a CSV 'test_data.csv' with 100 rows of random numbers, calculate mean and standard deviation using Python, and save results to 'stats_summary.txt'.", v15))
|
||||
|
||||
benchmark.run_all()
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include "messages.h"
|
||||
#include "r_error.h"
|
||||
#include "tool.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#define AGENT_MAX_ITERATIONS 300
|
||||
@ -26,6 +27,8 @@ void agent_destroy(agent_handle agent);
|
||||
|
||||
void agent_set_max_iterations(agent_handle agent, int max);
|
||||
void agent_set_verbose(agent_handle agent, bool verbose);
|
||||
void agent_set_is_subagent(agent_handle agent, bool is_subagent);
|
||||
void agent_set_tool_registry(agent_handle agent, tool_registry_t *registry);
|
||||
|
||||
agent_state_t agent_get_state(agent_handle agent);
|
||||
const char *agent_get_error(agent_handle agent);
|
||||
|
||||
@ -4,6 +4,6 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
char *r_bash_execute(const char *command, bool interactive);
|
||||
char *r_bash_execute(const char *command, bool interactive, int timeout_seconds);
|
||||
|
||||
#endif
|
||||
|
||||
@ -32,6 +32,7 @@ r_status_t messages_replace_at(messages_handle msgs, int index, struct json_obje
|
||||
|
||||
struct json_object *messages_to_json(messages_handle msgs);
|
||||
char *messages_to_string(messages_handle msgs);
|
||||
char *messages_to_json_string(messages_handle msgs);
|
||||
int messages_count(messages_handle msgs);
|
||||
|
||||
#endif
|
||||
|
||||
@ -24,6 +24,7 @@ bool r_config_is_verbose(r_config_handle cfg);
|
||||
void r_config_set_verbose(r_config_handle cfg, bool verbose);
|
||||
|
||||
double r_config_get_temperature(r_config_handle cfg);
|
||||
int r_config_get_max_tokens(r_config_handle cfg);
|
||||
|
||||
const char *r_config_get_session_id(r_config_handle cfg);
|
||||
bool r_config_set_session_id(r_config_handle cfg, const char *session_id);
|
||||
|
||||
@ -39,4 +39,13 @@ struct json_object *tool_registry_execute(tool_registry_t *registry,
|
||||
tool_registry_t *tools_get_registry(void);
|
||||
void tools_registry_shutdown(void);
|
||||
|
||||
typedef enum {
|
||||
TOOL_TYPE_ALL,
|
||||
TOOL_TYPE_RESEARCHER,
|
||||
TOOL_TYPE_DEVELOPER,
|
||||
TOOL_TYPE_SECURITY
|
||||
} tool_registry_type_t;
|
||||
|
||||
tool_registry_t *tool_registry_get_specialized(tool_registry_type_t type);
|
||||
|
||||
#endif
|
||||
|
||||
102
src/agent.c
102
src/agent.c
@ -21,6 +21,7 @@ struct agent_t {
|
||||
time_t start_time;
|
||||
char *last_error;
|
||||
bool verbose;
|
||||
bool is_subagent;
|
||||
messages_handle messages;
|
||||
bool owns_messages;
|
||||
http_client_handle http;
|
||||
@ -34,11 +35,31 @@ static const char *incomplete_phrases[] = {
|
||||
"Will now", "Proceeding", "Starting to", "About to ",
|
||||
"First, I", "Then I", "After that", "Following that",
|
||||
"Now let me", "Let's check", "Let me check",
|
||||
"Would you like", "Should I", "I can also", "I could",
|
||||
"Do you want", "Shall I",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *incomplete_endings[] = {
|
||||
"...", ":", "files:", "content:", "implementation:",
|
||||
"...", ":", "files:", "content:", "implementation:", "?",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *completion_phrases[] = {
|
||||
"task is complete", "task complete", "tasks complete",
|
||||
"goal is achieved", "goal achieved",
|
||||
"all steps completed", "all steps done",
|
||||
"fully completed", "is now complete",
|
||||
"has been completed", "have been completed",
|
||||
"successfully created", "successfully written",
|
||||
"setup is complete", "is ready to use",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *passive_phrases[] = {
|
||||
"let me know", "feel free", "if you need", "awaiting",
|
||||
"ready for", "standby", "standing by", "happy to help",
|
||||
"do not hesitate", "anything else",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -80,6 +101,51 @@ agent_handle agent_create(const char *goal, messages_handle messages) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *system_msg = r_config_get_system_message(cfg);
|
||||
if (!system_msg || !*system_msg) {
|
||||
bool has_system = false;
|
||||
for (int i = 0; i < messages_count(agent->messages); i++) {
|
||||
struct json_object *msg = messages_get_object(agent->messages, i);
|
||||
struct json_object *role;
|
||||
if (json_object_object_get_ex(msg, "role", &role)) {
|
||||
const char *role_str = json_object_get_string(role);
|
||||
if (role_str && strcmp(role_str, "system") == 0) {
|
||||
has_system = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_system) {
|
||||
messages_add(agent->messages, "system",
|
||||
"You are an autonomous AI agent with full system access through function calling. "
|
||||
"You have the spawn_agent tool to create specialized sub-agents for different tasks. "
|
||||
"Use spawn_agent extensively for: "
|
||||
"- research tasks (researcher persona) "
|
||||
"- development tasks (developer persona) "
|
||||
"- security audits (security persona) "
|
||||
"When web_search returns results with URLs, spawn researcher agents to fetch and analyze the content. "
|
||||
"Always break complex tasks into sub-tasks using agents for better orchestration. "
|
||||
"Continue iterating until goals are fully achieved, using appropriate tools and agents.");
|
||||
}
|
||||
} else if (system_msg && *system_msg) {
|
||||
bool has_system = false;
|
||||
for (int i = 0; i < messages_count(agent->messages); i++) {
|
||||
struct json_object *msg = messages_get_object(agent->messages, i);
|
||||
struct json_object *role;
|
||||
if (json_object_object_get_ex(msg, "role", &role)) {
|
||||
const char *role_str = json_object_get_string(role);
|
||||
if (role_str && strcmp(role_str, "system") == 0) {
|
||||
has_system = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!has_system) {
|
||||
messages_add(agent->messages, "system", system_msg);
|
||||
}
|
||||
}
|
||||
|
||||
agent->http = http_client_create(r_config_get_api_key(cfg));
|
||||
if (!agent->http) {
|
||||
if (agent->owns_messages) {
|
||||
@ -112,6 +178,10 @@ void agent_set_verbose(agent_handle agent, bool verbose) {
|
||||
if (agent) agent->verbose = verbose;
|
||||
}
|
||||
|
||||
void agent_set_is_subagent(agent_handle agent, bool is_subagent) {
|
||||
if (agent) agent->is_subagent = is_subagent;
|
||||
}
|
||||
|
||||
void agent_set_tool_registry(agent_handle agent, tool_registry_t *registry) {
|
||||
if (agent && registry) agent->tools = registry;
|
||||
}
|
||||
@ -155,6 +225,8 @@ static char *agent_build_request(agent_handle agent, const char *role, const cha
|
||||
json_object_get(messages_to_json(agent->messages)));
|
||||
json_object_object_add(root, "temperature",
|
||||
json_object_new_double(r_config_get_temperature(cfg)));
|
||||
json_object_object_add(root, "max_tokens",
|
||||
json_object_new_int(r_config_get_max_tokens(cfg)));
|
||||
|
||||
char *result = strdup(json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY));
|
||||
json_object_put(root);
|
||||
@ -181,9 +253,12 @@ static struct json_object *agent_process_response(agent_handle agent, const char
|
||||
const char *err_str = json_object_to_json_string(error_obj);
|
||||
|
||||
// Smart error detection for context overflow
|
||||
if (strstr(err_str, "too long") ||
|
||||
strstr(err_str, "context_length_exceeded") ||
|
||||
strstr(err_str, "Input is too long")) {
|
||||
if (strcasestr(err_str, "too long") ||
|
||||
strcasestr(err_str, "context_length_exceeded") ||
|
||||
strcasestr(err_str, "maximum context length") ||
|
||||
strcasestr(err_str, "reduce the length") ||
|
||||
strcasestr(err_str, "context limit") ||
|
||||
strcasestr(err_str, "Input is too long")) {
|
||||
agent_set_error(agent, "CONTEXT_OVERFLOW");
|
||||
} else {
|
||||
fprintf(stderr, "API Error: %s\n", err_str);
|
||||
@ -250,8 +325,19 @@ static char *agent_get_content(struct json_object *choice) {
|
||||
static bool agent_response_indicates_incomplete(const char *content) {
|
||||
if (!content) return false;
|
||||
|
||||
// Check for explicit completion phrases first (Overrides incomplete indicators)
|
||||
for (int i = 0; completion_phrases[i]; i++) {
|
||||
if (strcasestr(content, completion_phrases[i])) return false;
|
||||
}
|
||||
|
||||
// Check for passive/closing phrases (Overrides incomplete indicators)
|
||||
// Example: "I will be here if you need me." -> Contains "I will" but is passive.
|
||||
for (int i = 0; passive_phrases[i]; i++) {
|
||||
if (strcasestr(content, passive_phrases[i])) return false;
|
||||
}
|
||||
|
||||
for (int i = 0; incomplete_phrases[i]; i++) {
|
||||
if (strstr(content, incomplete_phrases[i])) return true;
|
||||
if (strcasestr(content, incomplete_phrases[i])) return true;
|
||||
}
|
||||
|
||||
size_t len = strlen(content);
|
||||
@ -356,10 +442,12 @@ char *agent_run(agent_handle agent, const char *user_message) {
|
||||
|
||||
char *content = agent_get_content(choice);
|
||||
if (content && *content) {
|
||||
// Print content immediately to the user
|
||||
// Print content immediately to the user (only if NOT a sub-agent)
|
||||
if (!agent->is_subagent) {
|
||||
extern void parse_markdown_to_ansi(const char *content);
|
||||
parse_markdown_to_ansi(content);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
size_t content_len = strlen(content);
|
||||
char *new_acc = realloc(accumulated_response, accumulated_len + content_len + 2);
|
||||
@ -423,7 +511,7 @@ char *agent_run(agent_handle agent, const char *user_message) {
|
||||
}
|
||||
} else {
|
||||
agent->state = AGENT_STATE_COMPLETED;
|
||||
if (agent->verbose) {
|
||||
if (agent->verbose && !agent->is_subagent) {
|
||||
fprintf(stderr, "[Agent] Completed in %d iteration(s)\n",
|
||||
agent->iteration_count);
|
||||
}
|
||||
|
||||
@ -8,12 +8,24 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
char *r_bash_execute(const char *command, bool interactive) {
|
||||
#define DEFAULT_TIMEOUT 300
|
||||
|
||||
char *r_bash_execute(const char *command, bool interactive, int timeout_seconds) {
|
||||
if (!command) {
|
||||
return strdup("Error: null command");
|
||||
}
|
||||
|
||||
if (timeout_seconds <= 0) {
|
||||
timeout_seconds = DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
size_t len = strlen(command);
|
||||
char *cmd_with_nl = malloc(len + 2);
|
||||
if (!cmd_with_nl) {
|
||||
@ -30,77 +42,156 @@ char *r_bash_execute(const char *command, bool interactive) {
|
||||
}
|
||||
|
||||
char tmp_script[] = "/tmp/r_bash_XXXXXX.sh";
|
||||
int fd = mkstemps(tmp_script, 3);
|
||||
if (fd == -1) {
|
||||
int script_fd = mkstemps(tmp_script, 3);
|
||||
if (script_fd == -1) {
|
||||
free(cmd_with_nl);
|
||||
perror("mkstemps");
|
||||
return strdup("Error: failed to create temp script");
|
||||
}
|
||||
|
||||
if (write(fd, cmd_with_nl, strlen(cmd_with_nl)) == -1) {
|
||||
close(fd);
|
||||
if (write(script_fd, cmd_with_nl, strlen(cmd_with_nl)) == -1) {
|
||||
close(script_fd);
|
||||
free(cmd_with_nl);
|
||||
unlink(tmp_script);
|
||||
return strdup("Error: failed to write to temp script");
|
||||
}
|
||||
|
||||
close(fd);
|
||||
close(script_fd);
|
||||
free(cmd_with_nl);
|
||||
|
||||
char *output = NULL;
|
||||
size_t total_size = 0;
|
||||
|
||||
if (interactive) {
|
||||
// For interactive mode, we still use system() but it doesn't easily support capturing output while timing out
|
||||
// Given the requirement, we'll try to use a simple timeout for interactive too if we can,
|
||||
// but typically interactive means user is at it.
|
||||
// However, user said "prevent hanging processes".
|
||||
|
||||
char *run_cmd = NULL;
|
||||
if (asprintf(&run_cmd, "bash %s", tmp_script) == -1) {
|
||||
if (asprintf(&run_cmd, "timeout %ds bash %s", timeout_seconds, tmp_script) == -1) {
|
||||
unlink(tmp_script);
|
||||
return strdup("Error: asprintf failed");
|
||||
}
|
||||
int status = system(run_cmd);
|
||||
free(run_cmd);
|
||||
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 124) {
|
||||
output = strdup("Error: Command timed out in interactive mode.");
|
||||
} else {
|
||||
if (asprintf(&output, "Command exited with status %d", status) == -1) {
|
||||
output = strdup("Command completed (asprintf failed for status).");
|
||||
output = strdup("Command completed.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
char *run_cmd = NULL;
|
||||
if (asprintf(&run_cmd, "bash %s 2>&1", tmp_script) == -1) {
|
||||
int pipe_fds[2];
|
||||
if (pipe(pipe_fds) == -1) {
|
||||
unlink(tmp_script);
|
||||
return strdup("Error: asprintf failed");
|
||||
return strdup("Error: pipe failed");
|
||||
}
|
||||
|
||||
FILE *fp = popen(run_cmd, "r");
|
||||
free(run_cmd);
|
||||
|
||||
if (!fp) {
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
close(pipe_fds[0]);
|
||||
close(pipe_fds[1]);
|
||||
unlink(tmp_script);
|
||||
return strdup("Error: popen failed");
|
||||
return strdup("Error: fork failed");
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
size_t total_size = 0;
|
||||
if (pid == 0) {
|
||||
// Child
|
||||
close(pipe_fds[0]);
|
||||
dup2(pipe_fds[1], STDOUT_FILENO);
|
||||
dup2(pipe_fds[1], STDERR_FILENO);
|
||||
close(pipe_fds[1]);
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), fp)) {
|
||||
// Print to stderr for the user in a subtle dim/gray color
|
||||
char *args[] = {"bash", tmp_script, NULL};
|
||||
execvp("bash", args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Parent
|
||||
close(pipe_fds[1]);
|
||||
int out_fd = pipe_fds[0];
|
||||
|
||||
struct poll_pfd {
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
} pfd;
|
||||
pfd.fd = out_fd;
|
||||
pfd.events = POLLIN;
|
||||
|
||||
time_t start_time = time(NULL);
|
||||
bool timed_out = false;
|
||||
|
||||
while (true) {
|
||||
time_t now = time(NULL);
|
||||
int remaining = timeout_seconds - (int)(now - start_time);
|
||||
if (remaining <= 0) {
|
||||
timed_out = true;
|
||||
break;
|
||||
}
|
||||
|
||||
int ret = poll((struct pollfd *)&pfd, 1, remaining * 1000);
|
||||
if (ret == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
break;
|
||||
}
|
||||
if (ret == 0) {
|
||||
timed_out = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pfd.revents & POLLIN) {
|
||||
char buffer[4096];
|
||||
ssize_t bytes = read(out_fd, buffer, sizeof(buffer) - 1);
|
||||
if (bytes <= 0) break;
|
||||
buffer[bytes] = '\0';
|
||||
|
||||
// Print to stderr for user
|
||||
fprintf(stderr, "\033[2m%s\033[0m", buffer);
|
||||
fflush(stderr);
|
||||
|
||||
size_t chunk_len = strlen(buffer);
|
||||
char *new_output = realloc(output, total_size + chunk_len + 1);
|
||||
char *new_output = realloc(output, total_size + (size_t)bytes + 1);
|
||||
if (!new_output) {
|
||||
free(output);
|
||||
pclose(fp);
|
||||
unlink(tmp_script);
|
||||
return strdup("Error: memory allocation failed");
|
||||
break;
|
||||
}
|
||||
output = new_output;
|
||||
strcpy(output + total_size, buffer);
|
||||
total_size += chunk_len;
|
||||
memcpy(output + total_size, buffer, (size_t)bytes);
|
||||
total_size += (size_t)bytes;
|
||||
output[total_size] = '\0';
|
||||
} else if (pfd.revents & (POLLHUP | POLLERR)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (timed_out) {
|
||||
kill(-pid, SIGKILL); // Kill process group if possible
|
||||
kill(pid, SIGKILL);
|
||||
|
||||
const char *timeout_msg = "\n[Error: Command timed out after %d seconds]\n";
|
||||
char *msg = NULL;
|
||||
if (asprintf(&msg, timeout_msg, timeout_seconds) != -1) {
|
||||
size_t msg_len = strlen(msg);
|
||||
char *new_output = realloc(output, total_size + msg_len + 1);
|
||||
if (new_output) {
|
||||
output = new_output;
|
||||
strcpy(output + total_size, msg);
|
||||
total_size += msg_len;
|
||||
}
|
||||
free(msg);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\033[1;31m%s\033[0m", "\n[Timeout reached, process terminated]\n");
|
||||
}
|
||||
|
||||
close(out_fd);
|
||||
waitpid(pid, NULL, WNOHANG);
|
||||
}
|
||||
pclose(fp);
|
||||
|
||||
if (!output) {
|
||||
output = strdup("");
|
||||
}
|
||||
}
|
||||
|
||||
unlink(tmp_script);
|
||||
return output;
|
||||
|
||||
@ -5,20 +5,40 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TRUNCATE_THRESHOLD 2000
|
||||
#define TRUNCATE_KEEP_START 800
|
||||
#define TRUNCATE_KEEP_END 800
|
||||
#define MIN_KEEP_CHARS 500
|
||||
#define TRUNCATE_MARKER "\n\n[... content truncated for context management ...]\n\n"
|
||||
|
||||
static bool is_system_message(struct json_object *msg) {
|
||||
static const char *get_message_role(struct json_object *msg) {
|
||||
struct json_object *role_obj;
|
||||
if (json_object_object_get_ex(msg, "role", &role_obj)) {
|
||||
return strcmp(json_object_get_string(role_obj), "system") == 0;
|
||||
return json_object_get_string(role_obj);
|
||||
}
|
||||
return false;
|
||||
return "";
|
||||
}
|
||||
|
||||
static r_status_t truncate_middle(messages_handle msgs, int index) {
|
||||
static size_t get_message_content_len(struct json_object *msg) {
|
||||
struct json_object *content_obj;
|
||||
const char *content = NULL;
|
||||
|
||||
if (json_object_object_get_ex(msg, "content", &content_obj)) {
|
||||
content = json_object_get_string(content_obj);
|
||||
} else if (json_object_object_get_ex(msg, "tool_result", &content_obj)) {
|
||||
content = json_object_get_string(content_obj);
|
||||
}
|
||||
|
||||
return content ? strlen(content) : 0;
|
||||
}
|
||||
|
||||
static size_t calculate_total_size(messages_handle msgs) {
|
||||
size_t total = 0;
|
||||
int count = messages_count(msgs);
|
||||
for (int i = 0; i < count; i++) {
|
||||
total += get_message_content_len(messages_get_object(msgs, i));
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static r_status_t perform_truncate(messages_handle msgs, int index, double ratio) {
|
||||
struct json_object *msg = messages_get_object(msgs, index);
|
||||
if (!msg) return R_ERROR_NOT_FOUND;
|
||||
|
||||
@ -36,17 +56,21 @@ static r_status_t truncate_middle(messages_handle msgs, int index) {
|
||||
if (!content) return R_SUCCESS;
|
||||
|
||||
size_t len = strlen(content);
|
||||
if (len <= TRUNCATE_THRESHOLD) return R_SUCCESS;
|
||||
size_t target_len = (size_t)(len * ratio);
|
||||
if (target_len < MIN_KEEP_CHARS * 2) target_len = MIN_KEEP_CHARS * 2;
|
||||
if (target_len >= len) return R_SUCCESS;
|
||||
|
||||
char *new_content = malloc(TRUNCATE_KEEP_START + strlen(TRUNCATE_MARKER) + TRUNCATE_KEEP_END + 1);
|
||||
size_t keep_each = target_len / 2;
|
||||
|
||||
char *new_content = malloc(keep_each * 2 + strlen(TRUNCATE_MARKER) + 1);
|
||||
if (!new_content) return R_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
strncpy(new_content, content, TRUNCATE_KEEP_START);
|
||||
new_content[TRUNCATE_KEEP_START] = '\0';
|
||||
strncpy(new_content, content, keep_each);
|
||||
new_content[keep_each] = '\0';
|
||||
strcat(new_content, TRUNCATE_MARKER);
|
||||
strcat(new_content, content + len - TRUNCATE_KEEP_END);
|
||||
strcat(new_content, content + len - keep_each);
|
||||
|
||||
struct json_object *new_msg = json_object_get(msg); // Increments ref count
|
||||
struct json_object *new_msg = json_object_get(msg);
|
||||
if (is_tool_result) {
|
||||
json_object_object_add(new_msg, "tool_result", json_object_new_string(new_content));
|
||||
} else {
|
||||
@ -61,51 +85,71 @@ r_status_t context_manager_shrink(messages_handle msgs) {
|
||||
if (!msgs) return R_ERROR_INVALID_ARG;
|
||||
|
||||
int count = messages_count(msgs);
|
||||
if (count <= 1) return R_ERROR_API_ERROR; // Cannot shrink further
|
||||
if (count <= 1) return R_ERROR_API_ERROR;
|
||||
|
||||
fprintf(stderr, " \033[2m-> Context limit reached, compressing history...\033[0m\n");
|
||||
size_t initial_size = calculate_total_size(msgs);
|
||||
// Target 40% of initial size to be safe and avoid immediate re-overflow
|
||||
size_t target_size = (size_t)(initial_size * 0.4);
|
||||
if (target_size < 10000) target_size = 10000; // Don't shrink too much if it's already small
|
||||
|
||||
fprintf(stderr, " \033[2m-> Context overflow (approx %zu chars). Shrinking to %zu...\033[0m\n",
|
||||
initial_size, target_size);
|
||||
|
||||
int iterations = 0;
|
||||
while (calculate_total_size(msgs) > target_size && iterations < 50) {
|
||||
iterations++;
|
||||
count = messages_count(msgs);
|
||||
|
||||
// Strategy 1: Find largest non-system, non-last message and truncate it
|
||||
int largest_idx = -1;
|
||||
size_t largest_size = 0;
|
||||
|
||||
// Phase 1: Truncate large messages in history (middle-cut)
|
||||
// We skip the last message as it's usually the one we just added or the latest prompt
|
||||
bool truncated_any = false;
|
||||
for (int i = 0; i < count - 1; i++) {
|
||||
struct json_object *msg = messages_get_object(msgs, i);
|
||||
if (is_system_message(msg)) continue;
|
||||
if (strcmp(get_message_role(msg), "system") == 0) continue;
|
||||
|
||||
struct json_object *content_obj;
|
||||
const char *content = NULL;
|
||||
if (json_object_object_get_ex(msg, "content", &content_obj)) {
|
||||
content = json_object_get_string(content_obj);
|
||||
} else if (json_object_object_get_ex(msg, "tool_result", &content_obj)) {
|
||||
content = json_object_get_string(content_obj);
|
||||
}
|
||||
|
||||
if (content && strlen(content) > TRUNCATE_THRESHOLD) {
|
||||
if (truncate_middle(msgs, i) == R_SUCCESS) {
|
||||
truncated_any = true;
|
||||
}
|
||||
size_t s = get_message_content_len(msg);
|
||||
if (s > largest_size) {
|
||||
largest_size = s;
|
||||
largest_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (truncated_any) return R_SUCCESS;
|
||||
if (largest_idx != -1 && largest_size > 1000) {
|
||||
perform_truncate(msgs, largest_idx, 0.3); // Cut to 30% of its size
|
||||
continue;
|
||||
}
|
||||
|
||||
// Phase 2: Historical Eviction (remove oldest non-system pairs)
|
||||
// We look for the first non-system message
|
||||
// Strategy 2: Remove oldest removable messages (keep sequence)
|
||||
int first_removable = -1;
|
||||
for (int i = 0; i < count - 1; i++) {
|
||||
struct json_object *msg = messages_get_object(msgs, i);
|
||||
if (!is_system_message(msg)) {
|
||||
if (strcmp(get_message_role(msg), "system") != 0) {
|
||||
first_removable = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_removable != -1 && first_removable < count - 1) {
|
||||
// Remove 2 messages to keep user/assistant pairs if possible,
|
||||
// or just one if it's a tool sequence
|
||||
int to_remove = (count - first_removable > 2) ? 2 : 1;
|
||||
return messages_remove_range(msgs, first_removable, to_remove);
|
||||
// Remove 1 message at a time from the front
|
||||
messages_remove_range(msgs, first_removable, 1);
|
||||
} else {
|
||||
// Nothing left to remove but system or last message
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return R_ERROR_API_ERROR;
|
||||
// Last Resort: If still too big, truncate the last message
|
||||
size_t final_size = calculate_total_size(msgs);
|
||||
if (final_size > target_size) {
|
||||
count = messages_count(msgs);
|
||||
if (count > 0) {
|
||||
perform_truncate(msgs, count - 1, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
size_t shrunk_size = calculate_total_size(msgs);
|
||||
fprintf(stderr, " \033[2m-> Context shrunk to approx %zu chars.\033[0m\n", shrunk_size);
|
||||
|
||||
return R_SUCCESS;
|
||||
}
|
||||
@ -119,7 +119,9 @@ r_status_t http_post(http_client_handle client, const char *url,
|
||||
|
||||
*response = NULL;
|
||||
|
||||
if (client->show_spinner) {
|
||||
bool actually_show_spinner = client->show_spinner && isatty(STDERR_FILENO);
|
||||
|
||||
if (actually_show_spinner) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &spinner_start_time);
|
||||
spinner_running = 1;
|
||||
pthread_create(&spinner_tid, NULL, spinner_thread, NULL);
|
||||
@ -175,7 +177,7 @@ r_status_t http_post(http_client_handle client, const char *url,
|
||||
|
||||
retry_count++;
|
||||
|
||||
if (client->show_spinner) {
|
||||
if (actually_show_spinner) {
|
||||
spinner_running = 0;
|
||||
pthread_join(spinner_tid, NULL);
|
||||
spinner_tid = 0;
|
||||
@ -189,7 +191,7 @@ r_status_t http_post(http_client_handle client, const char *url,
|
||||
fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000);
|
||||
usleep(HTTP_RETRY_DELAY_MS * 1000);
|
||||
|
||||
if (client->show_spinner) {
|
||||
if (actually_show_spinner) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &spinner_start_time);
|
||||
spinner_running = 1;
|
||||
pthread_create(&spinner_tid, NULL, spinner_thread, NULL);
|
||||
@ -200,7 +202,7 @@ r_status_t http_post(http_client_handle client, const char *url,
|
||||
status = R_ERROR_HTTP_TIMEOUT;
|
||||
|
||||
cleanup:
|
||||
if (client->show_spinner && spinner_tid) {
|
||||
if (actually_show_spinner && spinner_tid) {
|
||||
spinner_running = 0;
|
||||
pthread_join(spinner_tid, NULL);
|
||||
fprintf(stderr, "\r \r");
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
#include "config.h"
|
||||
// retoor <retoor@molodetz.nl>
|
||||
#include "util/path.h"
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct config_t {
|
||||
char *api_url;
|
||||
char *models_url;
|
||||
@ -18,13 +17,10 @@ struct config_t {
|
||||
bool use_strict;
|
||||
bool verbose;
|
||||
};
|
||||
|
||||
static struct config_t *instance = NULL;
|
||||
|
||||
static char *strdup_safe(const char *s) {
|
||||
return s ? strdup(s) : NULL;
|
||||
}
|
||||
|
||||
static bool resolve_env_bool(const char *env_name, bool default_val) {
|
||||
const char *val = getenv(env_name);
|
||||
if (!val) return default_val;
|
||||
@ -32,24 +28,18 @@ static bool resolve_env_bool(const char *env_name, bool default_val) {
|
||||
if (!strcmp(val, "false") || !strcmp(val, "0")) return false;
|
||||
return default_val;
|
||||
}
|
||||
|
||||
static const char *resolve_api_key(void) {
|
||||
const char *key = getenv("OPENROUTER_API_KEY");
|
||||
if (key && *key) return key;
|
||||
|
||||
key = getenv("R_KEY");
|
||||
if (key && *key) return key;
|
||||
|
||||
key = getenv("OPENAI_API_KEY");
|
||||
if (key && *key) return key;
|
||||
|
||||
return "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";
|
||||
}
|
||||
|
||||
static bool is_valid_session_id(const char *session_id) {
|
||||
if (!session_id || !*session_id) return false;
|
||||
if (strlen(session_id) > 255) return false;
|
||||
|
||||
for (const char *p = session_id; *p; p++) {
|
||||
if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_' && *p != '.') {
|
||||
return false;
|
||||
@ -57,13 +47,10 @@ static bool is_valid_session_id(const char *session_id) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
config_handle config_create(void) {
|
||||
if (instance) return instance;
|
||||
|
||||
struct config_t *cfg = calloc(1, sizeof(struct config_t));
|
||||
if (!cfg) return NULL;
|
||||
|
||||
const char *base_url = getenv("R_BASE_URL");
|
||||
if (base_url && *base_url) {
|
||||
size_t len = strlen(base_url);
|
||||
@ -77,34 +64,27 @@ config_handle config_create(void) {
|
||||
cfg->api_url = strdup("https://api.openai.com/v1/chat/completions");
|
||||
cfg->models_url = strdup("https://api.openai.com/v1/models");
|
||||
}
|
||||
|
||||
const char *model = getenv("R_MODEL");
|
||||
cfg->model = strdup(model && *model ? model : "gpt-4o-mini");
|
||||
|
||||
cfg->api_key = strdup(resolve_api_key());
|
||||
cfg->db_path = strdup("~/.r.db");
|
||||
cfg->temperature = 0.1;
|
||||
cfg->use_tools = resolve_env_bool("R_USE_TOOLS", true);
|
||||
cfg->use_strict = resolve_env_bool("R_USE_STRICT", true);
|
||||
cfg->verbose = false;
|
||||
|
||||
const char *session = getenv("R_SESSION");
|
||||
if (session && is_valid_session_id(session)) {
|
||||
cfg->session_id = strdup(session);
|
||||
} else {
|
||||
cfg->session_id = strdup("default");
|
||||
}
|
||||
|
||||
const char *system_msg = getenv("R_SYSTEM_MESSAGE");
|
||||
cfg->system_message = strdup_safe(system_msg);
|
||||
|
||||
instance = cfg;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
void config_destroy(config_handle cfg) {
|
||||
if (!cfg) return;
|
||||
|
||||
if (cfg != instance) {
|
||||
if (cfg->api_url) free(cfg->api_url);
|
||||
if (cfg->models_url) free(cfg->models_url);
|
||||
@ -116,73 +96,54 @@ void config_destroy(config_handle cfg) {
|
||||
free(cfg);
|
||||
}
|
||||
}
|
||||
|
||||
const char *config_get_api_url(config_handle cfg) {
|
||||
return cfg ? cfg->api_url : NULL;
|
||||
}
|
||||
|
||||
const char *config_get_models_url(config_handle cfg) {
|
||||
return cfg ? cfg->models_url : NULL;
|
||||
}
|
||||
|
||||
const char *config_get_api_key(config_handle cfg) {
|
||||
return cfg ? cfg->api_key : NULL;
|
||||
}
|
||||
|
||||
const char *config_get_model(config_handle cfg) {
|
||||
return cfg ? cfg->model : NULL;
|
||||
}
|
||||
|
||||
const char *config_get_db_path(config_handle cfg) {
|
||||
return cfg ? cfg->db_path : NULL;
|
||||
}
|
||||
|
||||
const char *config_get_session_id(config_handle cfg) {
|
||||
return cfg ? cfg->session_id : NULL;
|
||||
}
|
||||
|
||||
const char *config_get_system_message(config_handle cfg) {
|
||||
return cfg ? cfg->system_message : NULL;
|
||||
}
|
||||
|
||||
double config_get_temperature(config_handle cfg) {
|
||||
return cfg ? cfg->temperature : 0.0;
|
||||
}
|
||||
|
||||
bool config_use_tools(config_handle cfg) {
|
||||
return cfg ? cfg->use_tools : true;
|
||||
}
|
||||
|
||||
bool config_use_strict(config_handle cfg) {
|
||||
return cfg ? cfg->use_strict : true;
|
||||
}
|
||||
|
||||
bool config_is_verbose(config_handle cfg) {
|
||||
return cfg ? cfg->verbose : false;
|
||||
}
|
||||
|
||||
r_status_t config_set_model(config_handle cfg, const char *model) {
|
||||
if (!cfg || !model) return R_ERROR_INVALID_ARG;
|
||||
|
||||
if (cfg->model) free(cfg->model);
|
||||
cfg->model = strdup(model);
|
||||
|
||||
return R_SUCCESS;
|
||||
}
|
||||
|
||||
r_status_t config_set_session_id(config_handle cfg, const char *session_id) {
|
||||
if (!cfg || !session_id) return R_ERROR_INVALID_ARG;
|
||||
|
||||
if (!is_valid_session_id(session_id)) {
|
||||
return R_ERROR_SESSION_INVALID;
|
||||
}
|
||||
|
||||
if (cfg->session_id) free(cfg->session_id);
|
||||
cfg->session_id = strdup(session_id);
|
||||
|
||||
return R_SUCCESS;
|
||||
}
|
||||
|
||||
void config_set_verbose(config_handle cfg, bool verbose) {
|
||||
if (cfg) cfg->verbose = verbose;
|
||||
}
|
||||
|
||||
@ -1,28 +1,21 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#ifndef R_INTERFACES_HTTP_H
|
||||
#define R_INTERFACES_HTTP_H
|
||||
|
||||
#include "r_error.h"
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct http_client_t *http_client_handle;
|
||||
|
||||
typedef enum {
|
||||
HTTP_METHOD_GET,
|
||||
HTTP_METHOD_POST,
|
||||
HTTP_METHOD_PUT,
|
||||
HTTP_METHOD_DELETE
|
||||
} http_method_t;
|
||||
|
||||
http_client_handle http_create(const char *base_url);
|
||||
void http_destroy(http_client_handle client);
|
||||
|
||||
void http_set_bearer_token(http_client_handle client, const char *token);
|
||||
void http_set_timeout(http_client_handle client, long timeout_seconds);
|
||||
void http_set_connect_timeout(http_client_handle client, long timeout_seconds);
|
||||
|
||||
r_status_t http_request(
|
||||
http_client_handle client,
|
||||
http_method_t method,
|
||||
@ -32,9 +25,7 @@ r_status_t http_request(
|
||||
char **response,
|
||||
int *response_code
|
||||
);
|
||||
|
||||
r_status_t http_get(http_client_handle client, const char *path, char **response);
|
||||
r_status_t http_post(http_client_handle client, const char *path, const char *body, char **response);
|
||||
r_status_t http_post_json(http_client_handle client, const char *path, const char *json, char **response);
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "logger.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
struct logger_t {
|
||||
log_level_t level;
|
||||
};
|
||||
|
||||
static const char *level_strings[] = {
|
||||
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
|
||||
};
|
||||
@ -18,49 +15,37 @@ static const char *level_colors[] = {
|
||||
"\033[90m", "\033[36m", "\033[32m", "\033[33m", "\033[31m", "\033[35m"
|
||||
};
|
||||
static const char *color_reset = "\033[0m";
|
||||
|
||||
logger_handle logger_create(log_level_t level) {
|
||||
struct logger_t *logger = malloc(sizeof(struct logger_t));
|
||||
if (!logger) return NULL;
|
||||
|
||||
logger->level = level;
|
||||
return logger;
|
||||
}
|
||||
|
||||
void logger_destroy(logger_handle logger) {
|
||||
if (logger) free(logger);
|
||||
}
|
||||
|
||||
void logger_set_level(logger_handle logger, log_level_t level) {
|
||||
if (logger) logger->level = level;
|
||||
}
|
||||
|
||||
log_level_t logger_get_level(logger_handle logger) {
|
||||
return logger ? logger->level : LOG_LEVEL_INFO;
|
||||
}
|
||||
|
||||
void logger_log(logger_handle logger, log_level_t level,
|
||||
const char *file, int line, const char *fmt, ...) {
|
||||
if (!logger || !fmt) return;
|
||||
|
||||
if (level < logger->level) return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm *tm_info = localtime(&now);
|
||||
char time_buf[32];
|
||||
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
|
||||
const char *basename = strrchr(file, '/');
|
||||
basename = basename ? basename + 1 : file;
|
||||
|
||||
fprintf(stderr, "%s[%s%s%s %s:%d] ",
|
||||
level_colors[level], level_strings[level], color_reset,
|
||||
basename, line);
|
||||
vfprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "%s\n", color_reset);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
46
src/main.c
46
src/main.c
@ -293,35 +293,25 @@ static void init(void) {
|
||||
"select and execute tools when needed, observe results, and continue "
|
||||
"until the goal is achieved.\n\n"
|
||||
"## Reasoning Pattern (ReAct)\n"
|
||||
"For complex tasks, think step-by-step:\n"
|
||||
"1. Thought: What do I need to accomplish? What information do I have?\n"
|
||||
"2. Action: Which tool should I use? With what parameters?\n"
|
||||
"3. Observation: What did the tool return? What does this tell me?\n"
|
||||
"4. Repeat until the goal is complete.\n\n"
|
||||
"5. Do not ask questions, do assumptions autonomously."
|
||||
"For EVERY task, you MUST follow this sequence:\n"
|
||||
"1. Plan: Break the task into logical sub-tasks. Decide which specialized agents to spawn.\n"
|
||||
"2. Execute: Spawn agents or use tools. INTEGRATE their results immediately.\n"
|
||||
"3. Verify: Ensure the integrated results meet the goal. Perform any final actions (like saving to a file).\n"
|
||||
"4. Conclude: Only after ALL sub-tasks and final actions are done, provide your final response.\n\n"
|
||||
"## Multi-Agent Orchestration (MANDATORY)\n"
|
||||
"You are the Lead Orchestrator. You MUST delegate specialized work:\n"
|
||||
"- researcher: For ALL information gathering. Never research yourself if you can spawn a researcher.\n"
|
||||
"- developer: For ALL coding, testing, and debugging.\n"
|
||||
"- security: For ALL security-related audits.\n\n"
|
||||
"IMPORTANT: When a sub-agent returns a result, you MUST read it, synthesize it, and then perform any necessary follow-up actions (like writing to a file or spawning another agent). NEVER assume a task is done just because a sub-agent finished; YOU must complete the final delivery.\n\n"
|
||||
"MANDATORY FINAL ACTION: If the user asked to save results to a file, YOU must call the write_file tool yourself with the synthesized data from the sub-agent. Do not ask for permission.\n\n"
|
||||
"## Tool Usage\n"
|
||||
"- Use tools proactively to gather information and take actions\n"
|
||||
"- If a tool fails, analyze the error and try a different approach\n"
|
||||
"- You can call multiple tools in sequence to accomplish complex "
|
||||
"tasks\n\n"
|
||||
"- Take decissions based on tool output yourself autonomously."
|
||||
"## Multi-Agent Orchestration\n"
|
||||
"You can delegate tasks to specialized agents using the `spawn_agent` tool.\n"
|
||||
"Personas:\n"
|
||||
"- researcher: Best for web search and data gathering.\n"
|
||||
"- developer: Best for writing and testing code.\n"
|
||||
"- security: Best for security audits and vulnerability analysis.\n\n"
|
||||
"Use the `blackboard` table in the database to share state and pass detailed instructions or results between agents if they exceed tool parameter limits.\n\n"
|
||||
"- Use tools proactively. If you say you will do something, you MUST call the tool in the SAME or NEXT turn.\n"
|
||||
"- If a tool fails, analyze and retry with a different approach.\n\n"
|
||||
"## CRITICAL OUTPUT RULES\n"
|
||||
"- You MUST include the actual content/data from tool results in your "
|
||||
"response\n"
|
||||
"- When you search the web, QUOTE the relevant information found\n"
|
||||
"- When you run a command, SHOW the output\n"
|
||||
"- NEVER say 'I found information' without showing what you found\n"
|
||||
"- NEVER say 'task complete' or 'report provided' - SHOW THE ACTUAL "
|
||||
"DATA\n"
|
||||
"- The user cannot see tool results - only YOUR response. Include "
|
||||
"everything relevant.\n\n"
|
||||
"- SHOW THE DATA: Always include the actual content from tool/agent results in your response.\n"
|
||||
"- NO PREMATURE COMPLETION: Do not say 'task complete' until you have verified all files are written and all steps are finished.\n"
|
||||
"- SEQUENTIAL EXECUTION: Perform one logical step at a time. If you need to research AND write a file, spawn the researcher first, wait for the result, THEN write the file.\n"
|
||||
"## Local Database\n"
|
||||
"You have a local SQLite database accessible via db_query, db_get, and "
|
||||
"db_set tools.\n"
|
||||
@ -345,7 +335,7 @@ static void init(void) {
|
||||
"## Terminal execution\n"
|
||||
"You are in a bash environment, so can also execute bash commands.\n "
|
||||
"Everything is allowed.\n"
|
||||
"Be very creative with your bash tool use, use it exessively.\n"
|
||||
"Be very creative with your bash tool use, use it excessively.\n"
|
||||
"Prefer commands that do not require root access.\n"
|
||||
"## COMMUNICATION\n"
|
||||
"You are only allowed to talk once, so do that absolutely last with your "
|
||||
|
||||
@ -257,6 +257,11 @@ char *messages_to_string(messages_handle msgs) {
|
||||
return strdup(json_object_to_json_string_ext(msgs->array, JSON_C_TO_STRING_PRETTY));
|
||||
}
|
||||
|
||||
char *messages_to_json_string(messages_handle msgs) {
|
||||
if (!msgs || !msgs->array) return NULL;
|
||||
return strdup(json_object_to_json_string_ext(msgs->array, JSON_C_TO_STRING_PLAIN));
|
||||
}
|
||||
|
||||
int messages_count(messages_handle msgs) {
|
||||
if (!msgs || !msgs->array) return 0;
|
||||
return json_object_array_length(msgs->array);
|
||||
|
||||
@ -15,6 +15,7 @@ struct r_config_t {
|
||||
char *session_id;
|
||||
char *system_message;
|
||||
double temperature;
|
||||
int max_tokens;
|
||||
bool use_tools;
|
||||
bool use_strict;
|
||||
bool verbose;
|
||||
@ -36,11 +37,13 @@ static bool resolve_env_bool(const char *env_name, bool default_val) {
|
||||
|
||||
static const char *resolve_api_key(void) {
|
||||
|
||||
const char * key = getenv("OPENROUTER_API_KEY");
|
||||
const char * key = getenv("R_KEY");
|
||||
if (key && *key) return key;
|
||||
|
||||
|
||||
key = getenv("R_KEY");
|
||||
|
||||
|
||||
key = getenv("OPENROUTER_API_KEY");
|
||||
if (key && *key) return key;
|
||||
|
||||
|
||||
@ -90,6 +93,8 @@ r_config_handle r_config_get_instance(void) {
|
||||
instance->api_key = strdup(resolve_api_key());
|
||||
instance->db_path = strdup("~/.r.db");
|
||||
instance->temperature = 0.1;
|
||||
const char *max_tokens_env = getenv("R_MAX_TOKENS");
|
||||
instance->max_tokens = max_tokens_env ? atoi(max_tokens_env) : 4096;
|
||||
instance->use_tools = resolve_env_bool("R_USE_TOOLS", true);
|
||||
instance->use_strict = resolve_env_bool("R_USE_STRICT", true);
|
||||
instance->verbose = false;
|
||||
@ -165,6 +170,10 @@ double r_config_get_temperature(r_config_handle cfg) {
|
||||
return cfg ? cfg->temperature : 0.1;
|
||||
}
|
||||
|
||||
int r_config_get_max_tokens(r_config_handle cfg) {
|
||||
return cfg ? cfg->max_tokens : 4096;
|
||||
}
|
||||
|
||||
const char *r_config_get_session_id(r_config_handle cfg) {
|
||||
return cfg ? cfg->session_id : NULL;
|
||||
}
|
||||
|
||||
@ -148,8 +148,14 @@ struct json_object *tool_registry_execute(tool_registry_t *registry,
|
||||
if (threads[i]) {
|
||||
pthread_join(threads[i], NULL);
|
||||
}
|
||||
json_object_object_add(result_objs[i], "content",
|
||||
json_object_new_string(t_args[i].output ? t_args[i].output : ""));
|
||||
|
||||
char *output = t_args[i].output ? t_args[i].output : "";
|
||||
json_object_object_add(result_objs[i], "content", json_object_new_string(output));
|
||||
|
||||
if (output && strncmp(output, "Error:", 6) == 0) {
|
||||
fprintf(stderr, "\033[1;31m[Tool Error] %s\033[0m\n", output);
|
||||
}
|
||||
|
||||
free(t_args[i].output);
|
||||
if (t_args[i].args) json_object_put(t_args[i].args);
|
||||
json_object_array_add(results, result_objs[i]);
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "agent.h"
|
||||
#include "messages.h"
|
||||
@ -8,139 +7,177 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct {
|
||||
tool_t tool;
|
||||
} tool_agent_t;
|
||||
|
||||
static struct json_object *tool_spawn_agent_get_description(void) {
|
||||
struct json_object *obj = json_object_new_object();
|
||||
json_object_object_add(obj, "name", json_object_new_string("spawn_agent"));
|
||||
json_object_object_add(obj, "description", json_object_new_string("Spawn a specialized sub-agent to handle a specific task."));
|
||||
|
||||
struct json_object *params = json_object_new_object();
|
||||
json_object_object_add(params, "type", json_object_new_string("object"));
|
||||
|
||||
struct json_object *props = json_object_new_object();
|
||||
|
||||
struct json_object *persona = json_object_new_object();
|
||||
json_object_object_add(persona, "type", json_object_new_string("string"));
|
||||
json_object_object_add(persona, "description", json_object_new_string("The persona of the agent (researcher, developer, security)."));
|
||||
json_object_object_add(persona, "description", json_object_new_string("The persona of the agent (researcher, developer, security, fetcher)."));
|
||||
struct json_object *persona_enum = json_object_new_array();
|
||||
json_object_array_add(persona_enum, json_object_new_string("researcher"));
|
||||
json_object_array_add(persona_enum, json_object_new_string("developer"));
|
||||
json_object_array_add(persona_enum, json_object_new_string("security"));
|
||||
json_object_array_add(persona_enum, json_object_new_string("fetcher"));
|
||||
json_object_object_add(persona, "enum", persona_enum);
|
||||
json_object_object_add(props, "persona", persona);
|
||||
|
||||
struct json_object *goal = json_object_new_object();
|
||||
json_object_object_add(goal, "type", json_object_new_string("string"));
|
||||
json_object_object_add(goal, "description", json_object_new_string("The specific task or goal for the sub-agent."));
|
||||
json_object_object_add(props, "goal", goal);
|
||||
|
||||
struct json_object *max_subagents = json_object_new_object();
|
||||
json_object_object_add(max_subagents, "type", json_object_new_string("integer"));
|
||||
json_object_object_add(max_subagents, "description", json_object_new_string("Remaining budget for spawning recursive sub-agents. Decrement this by 1 when spawning a sub-agent. Default is 2."));
|
||||
json_object_object_add(max_subagents, "default", json_object_new_int(2));
|
||||
json_object_object_add(props, "max_subagents", max_subagents);
|
||||
json_object_object_add(params, "properties", props);
|
||||
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("persona"));
|
||||
json_object_array_add(required, json_object_new_string("goal"));
|
||||
json_object_array_add(required, json_object_new_string("max_subagents"));
|
||||
json_object_object_add(params, "required", required);
|
||||
|
||||
json_object_object_add(params, "additionalProperties", json_object_new_boolean(0));
|
||||
json_object_object_add(obj, "parameters", params);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(obj, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
struct json_object *full_obj = json_object_new_object();
|
||||
json_object_object_add(full_obj, "type", json_object_new_string("function"));
|
||||
json_object_object_add(full_obj, "function", obj);
|
||||
|
||||
return full_obj;
|
||||
}
|
||||
|
||||
static char *tool_spawn_agent_execute(tool_t *self, struct json_object *args) {
|
||||
(void)self;
|
||||
struct json_object *persona_obj, *goal_obj;
|
||||
|
||||
struct json_object *persona_obj, *goal_obj, *max_subagents_obj;
|
||||
if (!json_object_object_get_ex(args, "persona", &persona_obj) ||
|
||||
!json_object_object_get_ex(args, "goal", &goal_obj)) {
|
||||
return strdup("Error: Missing persona or goal");
|
||||
}
|
||||
|
||||
int max_subagents = 2;
|
||||
if (json_object_object_get_ex(args, "max_subagents", &max_subagents_obj)) {
|
||||
max_subagents = json_object_get_int(max_subagents_obj);
|
||||
}
|
||||
if (max_subagents <= 0) {
|
||||
return strdup("Error: Spawning limit reached. You are not allowed to spawn more sub-agents. Perform the task yourself using existing tools.");
|
||||
}
|
||||
const char *persona_str = json_object_get_string(persona_obj);
|
||||
const char *goal_str = json_object_get_string(goal_obj);
|
||||
|
||||
tool_registry_type_t type = TOOL_TYPE_ALL;
|
||||
const char *system_prompt = NULL;
|
||||
|
||||
const char *system_prompt_base = NULL;
|
||||
if (strcmp(persona_str, "researcher") == 0) {
|
||||
type = TOOL_TYPE_RESEARCHER;
|
||||
system_prompt = "You are a specialized Research Agent. Your goal is to find, extract, and summarize information. "
|
||||
system_prompt_base = "You are a specialized Research Agent. Your goal is to find, extract, and summarize information. "
|
||||
"Do not attempt to write or execute code unless it's for data analysis. "
|
||||
"Focus on using web search, http fetch, and reading files.";
|
||||
"Focus on using web search, http fetch, and reading files. "
|
||||
"## Sub-Agent Rules\n"
|
||||
"- Your output is for a master agent, not the final user.\n"
|
||||
"- DO NOT ask questions or for permission.\n"
|
||||
"- Provide RAW DATA and summaries.\n"
|
||||
"- Do not say 'task complete'.\n"
|
||||
"## Hierarchical Research Workflow\n"
|
||||
"When web_search returns URLs with content:\n"
|
||||
"1. Spawn 'fetcher' agents in parallel to fetch URL contents\n"
|
||||
"2. Each fetcher should use http_fetch tool for individual URLs\n"
|
||||
"3. Aggregate results and synthesize with citations\n"
|
||||
"Citation format: [Source N] Title (URL)\n"
|
||||
"Use spawn_agent extensively for URL fetching from search results.";
|
||||
} else if (strcmp(persona_str, "developer") == 0) {
|
||||
type = TOOL_TYPE_DEVELOPER;
|
||||
system_prompt = "You are a specialized Developer Agent. Your goal is to write, test, and debug code. "
|
||||
system_prompt_base = "You are a specialized Developer Agent. Your goal is to write, test, and debug code. "
|
||||
"## Sub-Agent Rules\n"
|
||||
"- Your output is for a master agent, not the final user.\n"
|
||||
"- DO NOT ask questions or for permission.\n"
|
||||
"- Just perform the requested development task and report results.\n"
|
||||
"Use the terminal, file editing tools, and python execution to fulfill your task. "
|
||||
"Always verify your changes by running tests or the code itself.";
|
||||
} else if (strcmp(persona_str, "security") == 0) {
|
||||
type = TOOL_TYPE_SECURITY;
|
||||
system_prompt = "You are a specialized Security Auditor Agent. Your goal is to find vulnerabilities and perform security analysis. "
|
||||
system_prompt_base = "You are a specialized Security Auditor Agent. Your goal is to find vulnerabilities and perform security analysis. "
|
||||
"## Sub-Agent Rules\n"
|
||||
"- Your output is for a master agent, not the final user.\n"
|
||||
"- DO NOT ask questions or for permission.\n"
|
||||
"Be pedantic and thorough. Use fuzzing, port scanning, and code analysis tools. "
|
||||
"Report any findings clearly with potential impact.";
|
||||
} else if (strcmp(persona_str, "fetcher") == 0) {
|
||||
type = TOOL_TYPE_ALL;
|
||||
system_prompt_base = "You are a specialized URL Fetcher Agent. Your goal is to fetch and extract content from multiple URLs. "
|
||||
"## Sub-Agent Rules\n"
|
||||
"- Just return the content of the URLs.\n"
|
||||
"- Do not provide commentary unless necessary.\n"
|
||||
"Use http_fetch tool to retrieve content from each URL. "
|
||||
"Handle errors gracefully and continue with other URLs. "
|
||||
"Clean HTML/extract text suitable for LLM analysis. "
|
||||
"Truncate content to ~10K chars per URL to stay within token limits.";
|
||||
} else {
|
||||
return strdup("Error: Invalid persona");
|
||||
}
|
||||
|
||||
time_t now = time(NULL);
|
||||
struct tm *tm_info = localtime(&now);
|
||||
char datetime[64];
|
||||
strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S %Z", tm_info);
|
||||
size_t prompt_size = strlen(system_prompt_base) + 1024;
|
||||
char *system_prompt = malloc(prompt_size);
|
||||
if (!system_prompt) return strdup("Error: Out of memory");
|
||||
snprintf(system_prompt, prompt_size,
|
||||
"Current date/time: %s\n\n%s\n\n"
|
||||
"CRITICAL: It is currently %s.\n"
|
||||
"ORCHESTRATION BUDGET: You are allowed to spawn up to %d more levels of sub-agents. "
|
||||
"When using spawn_agent, you MUST pass 'max_subagents' as %d.\n",
|
||||
datetime, system_prompt_base, datetime, max_subagents, max_subagents - 1);
|
||||
char session_id[256];
|
||||
snprintf(session_id, sizeof(session_id), "subagent-%s-%u", persona_str, (unsigned int)time(NULL));
|
||||
|
||||
messages_handle msgs = messages_create(session_id);
|
||||
messages_add(msgs, "system", system_prompt);
|
||||
|
||||
free(system_prompt);
|
||||
agent_handle agent = agent_create(goal_str, msgs);
|
||||
if (!agent) {
|
||||
messages_destroy(msgs);
|
||||
return strdup("Error: Failed to create sub-agent");
|
||||
}
|
||||
|
||||
agent_set_is_subagent(agent, true);
|
||||
tool_registry_t *specialized_tools = tool_registry_get_specialized(type);
|
||||
if (specialized_tools) {
|
||||
agent_set_tool_registry(agent, specialized_tools);
|
||||
}
|
||||
|
||||
agent_set_max_iterations(agent, 50); // Sub-agents have lower limit
|
||||
|
||||
char *result = agent_run(agent, goal_str);
|
||||
|
||||
char *agent_response = agent_run(agent, goal_str);
|
||||
char *result = messages_to_json_string(msgs);
|
||||
agent_destroy(agent);
|
||||
// specialized_tools should be destroyed?
|
||||
// In tool_registry_get_specialized we create a new one.
|
||||
tool_registry_destroy(specialized_tools);
|
||||
free(agent_response);
|
||||
|
||||
if (specialized_tools) {
|
||||
tool_registry_destroy(specialized_tools);
|
||||
}
|
||||
if (!result) {
|
||||
return strdup("Sub-agent failed to provide a result.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void tool_spawn_agent_print_action(const char *name, struct json_object *args) {
|
||||
struct json_object *persona_obj, *goal_obj;
|
||||
const char *persona = "unknown";
|
||||
const char *goal = "unknown";
|
||||
|
||||
if (json_object_object_get_ex(args, "persona", &persona_obj)) persona = json_object_get_string(persona_obj);
|
||||
if (json_object_object_get_ex(args, "goal", &goal_obj)) goal = json_object_get_string(goal_obj);
|
||||
|
||||
printf("\033[1;34m[Agent] Spawning %s agent for: %s\033[0m\n", persona, goal);
|
||||
}
|
||||
|
||||
static const tool_vtable_t tool_spawn_agent_vtable = {
|
||||
.get_description = tool_spawn_agent_get_description,
|
||||
.execute = tool_spawn_agent_execute,
|
||||
.print_action = tool_spawn_agent_print_action
|
||||
};
|
||||
|
||||
tool_t *tool_spawn_agent_create(void) {
|
||||
tool_agent_t *tool = calloc(1, sizeof(tool_agent_t));
|
||||
if (!tool) return NULL;
|
||||
|
||||
tool->tool.vtable = &tool_spawn_agent_vtable;
|
||||
tool->tool.name = "spawn_agent";
|
||||
|
||||
return &tool->tool;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "r_config.h"
|
||||
#include "bash_executor.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -139,12 +140,31 @@ static struct json_object *automation_fuzz_get_description(void) {
|
||||
json_object_object_add(port, "type", json_object_new_string("integer"));
|
||||
json_object_object_add(properties, "port", port);
|
||||
|
||||
struct json_object *template = json_object_new_object();
|
||||
json_object_object_add(template, "type", json_object_new_string("string"));
|
||||
json_object_object_add(template, "description", json_object_new_string("Base template for fuzzing."));
|
||||
json_object_object_add(properties, "template", template);
|
||||
|
||||
struct json_object *iterations = json_object_new_object();
|
||||
json_object_object_add(iterations, "type", json_object_new_string("integer"));
|
||||
json_object_object_add(iterations, "description", json_object_new_string("Number of iterations."));
|
||||
json_object_object_add(properties, "iterations", iterations);
|
||||
|
||||
json_object_object_add(parameters, "properties", properties);
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("host"));
|
||||
json_object_array_add(required, json_object_new_string("port"));
|
||||
json_object_array_add(required, json_object_new_string("template"));
|
||||
json_object_array_add(required, json_object_new_string("iterations"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
@ -164,8 +184,32 @@ static struct json_object *automation_exploit_gen_get_description(void) {
|
||||
json_object_object_add(type, "description", json_object_new_string("Type of exploit: reverse, bind."));
|
||||
json_object_object_add(properties, "type", type);
|
||||
|
||||
struct json_object *lhost = json_object_new_object();
|
||||
json_object_object_add(lhost, "type", json_object_new_string("string"));
|
||||
json_object_object_add(lhost, "description", json_object_new_string("Local host for reverse shell."));
|
||||
json_object_object_add(properties, "lhost", lhost);
|
||||
|
||||
struct json_object *lport = json_object_new_object();
|
||||
json_object_object_add(lport, "type", json_object_new_string("integer"));
|
||||
json_object_object_add(lport, "description", json_object_new_string("Local port for reverse/bind shell."));
|
||||
json_object_object_add(properties, "lport", lport);
|
||||
|
||||
json_object_object_add(parameters, "properties", properties);
|
||||
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("type"));
|
||||
json_object_array_add(required, json_object_new_string("lhost"));
|
||||
json_object_array_add(required, json_object_new_string("lport"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "r_config.h"
|
||||
#include "bash_executor.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -54,7 +55,7 @@ static char *code_grep_execute(tool_t *self, struct json_object *args) {
|
||||
|
||||
char command[4096];
|
||||
snprintf(command, sizeof(command), "grep -rnH --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=build -E \"%s\" %s | head -n 100", pattern, path);
|
||||
return r_bash_execute(command, false);
|
||||
return r_bash_execute(command, false, 300);
|
||||
}
|
||||
|
||||
static void code_symbol_find_print_action(const char *name, struct json_object *args) {
|
||||
@ -80,7 +81,7 @@ static char *code_symbol_find_execute(tool_t *self, struct json_object *args) {
|
||||
"-E \"(function|class|def|struct|enum)[[:space:]]+%s[[:space:]]*[(:{]|^[[:space:]]*%s[[:space:]]*=[[:space:]]*[(]|%s[[:space:]]*\\(\" . | head -n 50",
|
||||
symbol, symbol, symbol);
|
||||
|
||||
return r_bash_execute(command, false);
|
||||
return r_bash_execute(command, false, 300);
|
||||
}
|
||||
|
||||
static struct json_object *code_grep_get_description(void) {
|
||||
@ -103,8 +104,17 @@ static struct json_object *code_grep_get_description(void) {
|
||||
json_object_object_add(parameters, "properties", properties);
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("pattern"));
|
||||
json_object_array_add(required, json_object_new_string("path"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
@ -126,7 +136,15 @@ static struct json_object *code_symbol_find_get_description(void) {
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("symbol"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "r_config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -70,10 +71,27 @@ static struct json_object *csv_export_get_description(void) {
|
||||
json_object_object_add(data, "type", json_object_new_string("array"));
|
||||
struct json_object *items = json_object_new_object();
|
||||
json_object_object_add(items, "type", json_object_new_string("object"));
|
||||
struct json_object *item_props = json_object_new_object();
|
||||
json_object_object_add(items, "properties", item_props);
|
||||
json_object_object_add(items, "required", json_object_new_array()); // Allow empty objects or define later if known
|
||||
json_object_object_add(items, "additionalProperties", json_object_new_boolean(0));
|
||||
json_object_object_add(data, "items", items);
|
||||
json_object_object_add(properties, "data", data);
|
||||
json_object_object_add(parameters, "properties", properties);
|
||||
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("path"));
|
||||
json_object_array_add(required, json_object_new_string("data"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "r_config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -471,11 +472,33 @@ static struct json_object *dns_lookup_get_description(void) {
|
||||
json_object_object_add(type, "description", json_object_new_string("Record type for 'lookup' (A, NS, MX, etc)."));
|
||||
json_object_object_add(properties, "type", type);
|
||||
|
||||
struct json_object *dns_server = json_object_new_object();
|
||||
json_object_object_add(dns_server, "type", json_object_new_string("string"));
|
||||
json_object_object_add(dns_server, "description", json_object_new_string("DNS server to use."));
|
||||
json_object_object_add(properties, "dns_server", dns_server);
|
||||
|
||||
struct json_object *enumerate = json_object_new_object();
|
||||
json_object_object_add(enumerate, "type", json_object_new_string("boolean"));
|
||||
json_object_object_add(enumerate, "description", json_object_new_string("Whether to enumerate all record types."));
|
||||
json_object_object_add(properties, "enumerate", enumerate);
|
||||
|
||||
json_object_object_add(parameters, "properties", properties);
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("hostname"));
|
||||
json_object_array_add(required, json_object_new_string("action"));
|
||||
json_object_array_add(required, json_object_new_string("type"));
|
||||
json_object_array_add(required, json_object_new_string("dns_server"));
|
||||
json_object_array_add(required, json_object_new_string("enumerate"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
|
||||
@ -583,9 +583,16 @@ static struct json_object *getpwd_get_description(void) {
|
||||
struct json_object *parameters = json_object_new_object();
|
||||
json_object_object_add(parameters, "type", json_object_new_string("object"));
|
||||
json_object_object_add(parameters, "properties", json_object_new_object());
|
||||
json_object_object_add(parameters, "required", json_object_new_array());
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "r_config.h"
|
||||
#include "r_diff.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -222,7 +223,14 @@ static struct json_object *file_line_replace_get_description(void) {
|
||||
json_object_array_add(required, json_object_new_string("end_line"));
|
||||
json_object_array_add(required, json_object_new_string("replacement"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
@ -250,7 +258,15 @@ static struct json_object *file_apply_patch_get_description(void) {
|
||||
json_object_array_add(required, json_object_new_string("path"));
|
||||
json_object_array_add(required, json_object_new_string("patch"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ static char *do_web_search(const char *query) {
|
||||
if (!q_encoded) return strdup("Failed to encode query.");
|
||||
|
||||
char url[4096];
|
||||
snprintf(url, sizeof(url), "https://static.molodetz.nl/search.cgi?query=%s", q_encoded);
|
||||
snprintf(url, sizeof(url), "https://rexa.molodetz.nl/ai?q=%s", q_encoded);
|
||||
curl_free(q_encoded);
|
||||
|
||||
http_client_handle client = http_client_create(NULL);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "r_config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -191,8 +192,17 @@ static struct json_object *network_port_scan_get_description(void) {
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("host"));
|
||||
json_object_array_add(required, json_object_new_string("start_port"));
|
||||
json_object_array_add(required, json_object_new_string("end_port"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
@ -308,8 +318,17 @@ static struct json_object *network_check_get_description(void) {
|
||||
json_object_object_add(parameters, "properties", properties);
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("host"));
|
||||
json_object_array_add(required, json_object_new_string("port"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ static char *python_execute_execute(tool_t *self, struct json_object *args) {
|
||||
char command[4096];
|
||||
snprintf(command, sizeof(command), "python3 '%s'", tmp_file);
|
||||
|
||||
output = r_bash_execute(command, false);
|
||||
output = r_bash_execute(command, false, 300);
|
||||
|
||||
unlink(tmp_file);
|
||||
return output;
|
||||
@ -98,6 +98,11 @@ static struct json_object *python_execute_get_description(void) {
|
||||
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "r_config.h"
|
||||
#include "bash_executor.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -37,7 +38,7 @@ static char *process_monitor_execute(tool_t *self, struct json_object *args) {
|
||||
const char *action = json_object_get_string(action_obj);
|
||||
|
||||
if (strcmp(action, "list") == 0) {
|
||||
return r_bash_execute("ps aux | head -n 50", false);
|
||||
return r_bash_execute("ps aux | head -n 50", false, 300);
|
||||
} else if (strcmp(action, "kill") == 0) {
|
||||
struct json_object *pid_obj;
|
||||
if (!json_object_object_get_ex(args, "pid", &pid_obj)) {
|
||||
@ -45,7 +46,7 @@ static char *process_monitor_execute(tool_t *self, struct json_object *args) {
|
||||
}
|
||||
char cmd[256];
|
||||
snprintf(cmd, sizeof(cmd), "kill -9 %d", json_object_get_int(pid_obj));
|
||||
return r_bash_execute(cmd, false);
|
||||
return r_bash_execute(cmd, false, 300);
|
||||
}
|
||||
|
||||
return strdup("Error: unknown action");
|
||||
@ -74,8 +75,17 @@ static struct json_object *process_monitor_get_description(void) {
|
||||
json_object_object_add(parameters, "properties", properties);
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("action"));
|
||||
json_object_array_add(required, json_object_new_string("pid"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
json_object_object_add(function, "parameters", parameters);
|
||||
|
||||
r_config_handle cfg = r_config_get_instance();
|
||||
if (r_config_use_strict(cfg)) {
|
||||
json_object_object_add(function, "strict", json_object_new_boolean(1));
|
||||
}
|
||||
|
||||
json_object_object_add(root, "function", function);
|
||||
return root;
|
||||
}
|
||||
|
||||
@ -49,12 +49,17 @@ static void terminal_print_action(const char *name, struct json_object *args) {
|
||||
fprintf(stderr, " -> %s\n", name);
|
||||
return;
|
||||
}
|
||||
struct json_object *cmd;
|
||||
struct json_object *cmd, *timeout_obj;
|
||||
int timeout = 300;
|
||||
if (json_object_object_get_ex(args, "timeout", &timeout_obj)) {
|
||||
timeout = json_object_get_int(timeout_obj);
|
||||
}
|
||||
|
||||
if (json_object_object_get_ex(args, "command", &cmd)) {
|
||||
if (strcmp(name, "linux_terminal_execute_interactive") == 0) {
|
||||
fprintf(stderr, " \033[1m-> Running interactive:\033[0m %s\n", json_object_get_string(cmd));
|
||||
fprintf(stderr, " \033[1m-> Running interactive (timeout %ds):\033[0m %s\n", timeout, json_object_get_string(cmd));
|
||||
} else {
|
||||
fprintf(stderr, " \033[1m-> Running command:\033[0m %s\n", json_object_get_string(cmd));
|
||||
fprintf(stderr, " \033[1m-> Running command (timeout %ds):\033[0m %s\n", timeout, json_object_get_string(cmd));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,25 +67,35 @@ static void terminal_print_action(const char *name, struct json_object *args) {
|
||||
static char *terminal_execute(tool_t *self, struct json_object *args) {
|
||||
(void)self;
|
||||
|
||||
struct json_object *cmd_obj;
|
||||
struct json_object *cmd_obj, *timeout_obj;
|
||||
if (!json_object_object_get_ex(args, "command", &cmd_obj)) {
|
||||
return strdup("Error: missing 'command' argument");
|
||||
}
|
||||
|
||||
int timeout = 300;
|
||||
if (json_object_object_get_ex(args, "timeout", &timeout_obj)) {
|
||||
timeout = json_object_get_int(timeout_obj);
|
||||
}
|
||||
|
||||
const char *command = json_object_get_string(cmd_obj);
|
||||
return r_bash_execute(command, false);
|
||||
return r_bash_execute(command, false, timeout);
|
||||
}
|
||||
|
||||
static char *terminal_interactive_execute(tool_t *self, struct json_object *args) {
|
||||
(void)self;
|
||||
|
||||
struct json_object *cmd_obj;
|
||||
struct json_object *cmd_obj, *timeout_obj;
|
||||
if (!json_object_object_get_ex(args, "command", &cmd_obj)) {
|
||||
return strdup("Error: missing 'command' argument");
|
||||
}
|
||||
|
||||
int timeout = 300;
|
||||
if (json_object_object_get_ex(args, "timeout", &timeout_obj)) {
|
||||
timeout = json_object_get_int(timeout_obj);
|
||||
}
|
||||
|
||||
const char *command = json_object_get_string(cmd_obj);
|
||||
return r_bash_execute(command, true);
|
||||
return r_bash_execute(command, true, timeout);
|
||||
}
|
||||
|
||||
static struct json_object *terminal_get_description(void) {
|
||||
@ -91,7 +106,7 @@ static struct json_object *terminal_get_description(void) {
|
||||
json_object_object_add(function, "name",
|
||||
json_object_new_string("linux_terminal_execute"));
|
||||
json_object_object_add(function, "description",
|
||||
json_object_new_string("Execute a linux_terminal command on user terminal."));
|
||||
json_object_new_string("Execute a linux_terminal command on user terminal with a timeout."));
|
||||
|
||||
struct json_object *parameters = json_object_new_object();
|
||||
json_object_object_add(parameters, "type", json_object_new_string("object"));
|
||||
@ -103,10 +118,18 @@ static struct json_object *terminal_get_description(void) {
|
||||
json_object_new_string("Bash command to execute."));
|
||||
json_object_object_add(properties, "command", cmd);
|
||||
|
||||
struct json_object *timeout = json_object_new_object();
|
||||
json_object_object_add(timeout, "type", json_object_new_string("integer"));
|
||||
json_object_object_add(timeout, "description",
|
||||
json_object_new_string("Timeout in seconds (default 300)."));
|
||||
json_object_object_add(timeout, "default", json_object_new_int(300));
|
||||
json_object_object_add(properties, "timeout", timeout);
|
||||
|
||||
json_object_object_add(parameters, "properties", properties);
|
||||
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("command"));
|
||||
json_object_array_add(required, json_object_new_string("timeout"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
@ -129,7 +152,7 @@ static struct json_object *terminal_interactive_get_description(void) {
|
||||
json_object_object_add(function, "name",
|
||||
json_object_new_string("linux_terminal_execute_interactive"));
|
||||
json_object_object_add(function, "description",
|
||||
json_object_new_string("Executes interactive terminal for user. You will not be able to read the result. Do not use if you need to know output."));
|
||||
json_object_new_string("Executes interactive terminal for user with a timeout. You will not be able to read the result. Do not use if you need to know output."));
|
||||
|
||||
struct json_object *parameters = json_object_new_object();
|
||||
json_object_object_add(parameters, "type", json_object_new_string("object"));
|
||||
@ -141,10 +164,18 @@ static struct json_object *terminal_interactive_get_description(void) {
|
||||
json_object_new_string("Executable with parameters to execute interactively."));
|
||||
json_object_object_add(properties, "command", cmd);
|
||||
|
||||
struct json_object *timeout = json_object_new_object();
|
||||
json_object_object_add(timeout, "type", json_object_new_string("integer"));
|
||||
json_object_object_add(timeout, "description",
|
||||
json_object_new_string("Timeout in seconds (default 300)."));
|
||||
json_object_object_add(timeout, "default", json_object_new_int(300));
|
||||
json_object_object_add(properties, "timeout", timeout);
|
||||
|
||||
json_object_object_add(parameters, "properties", properties);
|
||||
|
||||
struct json_object *required = json_object_new_array();
|
||||
json_object_array_add(required, json_object_new_string("command"));
|
||||
json_object_array_add(required, json_object_new_string("timeout"));
|
||||
json_object_object_add(parameters, "required", required);
|
||||
json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0));
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@ extern tool_t *tool_network_port_scan_create(void);
|
||||
extern tool_t *tool_automation_fuzz_create(void);
|
||||
extern tool_t *tool_automation_exploit_gen_create(void);
|
||||
extern tool_t *tool_csv_export_create(void);
|
||||
extern tool_t *tool_spawn_agent_create(void);
|
||||
|
||||
static tool_registry_t *global_registry = NULL;
|
||||
|
||||
@ -68,10 +69,61 @@ tool_registry_t *tools_get_registry(void) {
|
||||
tool_registry_register(global_registry, tool_automation_fuzz_create());
|
||||
tool_registry_register(global_registry, tool_automation_exploit_gen_create());
|
||||
tool_registry_register(global_registry, tool_csv_export_create());
|
||||
tool_registry_register(global_registry, tool_spawn_agent_create());
|
||||
|
||||
return global_registry;
|
||||
}
|
||||
|
||||
tool_registry_t *tool_registry_get_specialized(tool_registry_type_t type) {
|
||||
tool_registry_t *reg = tool_registry_create();
|
||||
if (!reg) return NULL;
|
||||
|
||||
if (type == TOOL_TYPE_RESEARCHER) {
|
||||
tool_registry_register(reg, tool_web_search_create());
|
||||
tool_registry_register(reg, tool_web_search_news_create());
|
||||
tool_registry_register(reg, tool_http_fetch_create());
|
||||
tool_registry_register(reg, tool_read_file_create());
|
||||
tool_registry_register(reg, tool_db_get_create());
|
||||
tool_registry_register(reg, tool_db_set_create());
|
||||
tool_registry_register(reg, tool_db_query_create());
|
||||
tool_registry_register(reg, tool_directory_glob_create());
|
||||
tool_registry_register(reg, tool_csv_export_create());
|
||||
tool_registry_register(reg, tool_spawn_agent_create());
|
||||
} else if (type == TOOL_TYPE_DEVELOPER) {
|
||||
tool_registry_register(reg, tool_terminal_create());
|
||||
tool_registry_register(reg, tool_read_file_create());
|
||||
tool_registry_register(reg, tool_write_file_create());
|
||||
tool_registry_register(reg, tool_directory_glob_create());
|
||||
tool_registry_register(reg, tool_mkdir_create());
|
||||
tool_registry_register(reg, tool_chdir_create());
|
||||
tool_registry_register(reg, tool_getpwd_create());
|
||||
tool_registry_register(reg, tool_python_execute_create());
|
||||
tool_registry_register(reg, tool_code_grep_create());
|
||||
tool_registry_register(reg, tool_code_symbol_find_create());
|
||||
tool_registry_register(reg, tool_file_line_replace_create());
|
||||
tool_registry_register(reg, tool_file_apply_patch_create());
|
||||
tool_registry_register(reg, tool_spawn_agent_create());
|
||||
} else if (type == TOOL_TYPE_SECURITY) {
|
||||
tool_registry_register(reg, tool_terminal_create());
|
||||
tool_registry_register(reg, tool_network_check_create());
|
||||
tool_registry_register(reg, tool_dns_lookup_create());
|
||||
tool_registry_register(reg, tool_network_port_scan_create());
|
||||
tool_registry_register(reg, tool_automation_fuzz_create());
|
||||
tool_registry_register(reg, tool_automation_exploit_gen_create());
|
||||
tool_registry_register(reg, tool_web_search_create());
|
||||
tool_registry_register(reg, tool_http_fetch_create());
|
||||
tool_registry_register(reg, tool_process_monitor_create());
|
||||
tool_registry_register(reg, tool_spawn_agent_create());
|
||||
} else {
|
||||
// Fallback or TOOL_TYPE_ALL
|
||||
tool_registry_register(reg, tool_terminal_create());
|
||||
tool_registry_register(reg, tool_read_file_create());
|
||||
tool_registry_register(reg, tool_spawn_agent_create());
|
||||
}
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
void tools_registry_shutdown(void) {
|
||||
if (global_registry) {
|
||||
tool_registry_destroy(global_registry);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user