43 Commits

Author SHA1 Message Date
hal9000 8f50dd324b Remove unused fcntl.h include from shell_tool.cpp
The fcntl() calls for FD_CLOEXEC were removed in a previous commit but
the include statement was left behind. This removes the dead include.
2026-08-12 16:07:19 -05:00
darthrubik 4fbf19dd3b Removing extra slop the AI added 2026-08-12 16:00:43 -05:00
hal9000 0e2986b562 Fix shell tool hanging when command spawns background processes
The shell tool would hang indefinitely when running commands that spawn
background processes (e.g., 'sleep 10 &'). This was because:

1. Background processes inherit stdout, which points to our pipe
2. The original code read in a loop when POLLIN was detected
3. After reading available data, the second read() would block waiting
   for more data that never comes (since background process just sleeps)

The fix changes the POLLIN handler to read only once, then continue
polling. If POLLHUP is eventually set (when the background process is
killed by timeout), we'll read all remaining data then.

Additionally, FD_CLOEXEC is set on pipe file descriptors to prevent
inheritance during exec() calls (though this doesn't help with fork()
which is how shells spawn background processes).

Fixes issue 205 - failing unit tests.
2026-08-12 15:57:00 -05:00
darthrubik 6c1906679d Merge remote-tracking branch 'origin/master' into feature/save-model-between-chats 2026-08-12 15:41:23 -05:00
hal9000 304a1ba133 Add cancellation check to grep_search tool
Add periodic ctrlc_request checks during directory traversal and file
processing in the grep_search tool. This allows the tool to respect user
cancellation (Ctrl+C) during recursive directory scanning of large codebases.

- Changed process_file to return bool indicating cancellation
- Added ctrlc_request check in directory iteration loop
- Added ctrlc_request check in file processing loop
- Returns early with cancellation message when cancelled

Closes issue #189
2026-08-05 23:39:59 +00:00
darthrubik d2451f3484 Add ctrlc cancel to shell tool 2026-08-05 18:25:53 -05:00
hal9000 3e99b37683 Move base_url and model into HarnessConfig (issue 186)
- Added base_url and model fields to HarnessConfig in json_types.hpp
- Removed base_url and model from OllamaConfig in ollama_client.hpp
- Updated OllamaClient constructor to accept base_url and model as separate parameters
- Updated main.cpp to use HarnessConfig for base_url and model
- Updated agent_tool.cpp to use new OllamaClient constructor
- Added test_harness_config.sh to verify the new config fields
- Updated README.md to document the config fields
2026-08-04 15:13:37 -05:00
darthrubik d655a9d953 Merge remote-tracking branch 'origin/master' into feature/rebase 2026-07-23 16:59:40 -05:00
hal9000 5680e8c6a9 Fix shell tool truncating output larger than buffer size
The shell tool was only reading once when data was available, which caused
data loss when the child process produced more output than the 4KB buffer.

Fixed by reading in a loop until EOF is reached in both the POLLHUP and
POLLIN handlers.
2026-06-18 15:14:13 -05:00
hal9000 da919f9f6e Merge origin/master into work branch (issue #169)
Resolved merge conflicts:
- CMakeLists.txt: Added both rebase_engine/utility and base64_util sources
- src/harness.cpp: Combined Content type with proper error handling
- src/main.cpp: Merged includes, test flags, and CLI parsing
  - Renamed conflicting flags: -B→-Z (build_rebase), -T→-W (edit_prompt), -b→-K (base64_decode)
- tests/CMakeLists.txt: Added all test targets from both branches
- src/rebase_engine.cpp: Fixed to use get_content_string() for Content variant type
- Updated test files to use new flag names (-Z, -W, -K)
2026-06-15 22:52:29 +00:00
hal9000 8ddee0f621 Fix parameter name mismatch in read_multi_modal tool schema (issue 163)
Change the required parameter name from 'filePath' to 'path' in the
read_multi_modal tool's JSON schema to match the property definition
and the ReadFileToolParams struct field name.
2026-06-15 22:20:13 +00:00
darthrubik 0020669eb0 Adding read multi-modal tool 2026-06-15 16:53:42 -05:00
darthrubik c46ebb665b Issue 101: Update file operation tool descriptions to indicate paths can be relative or absolute 2026-05-29 16:25:22 -05:00
darthrubik b327df20a2 refactor: remove std::any from codebase (issue #90)
Replace std::any-based parameter passing with compile-time type-safe adapter pattern.

- Add create_tool_execute<T>() template function in tool.hpp
- Remove convert_params field from Tool struct
- Change execute field type from std::function<std::string(const std::any&)>
  to std::function<std::string(const nlohmann::json&)>
- Change ToolRegistry::invoke() return type from std::expected to std::string
- Update all tool implementations to use create_tool_execute<ParamsType>()
- Remove #include <any> from tool.hpp and json_types.hpp
- Simplify harness.cpp invoke caller

All 27 tests pass.
2026-05-29 14:11:09 -05:00
darthrubik 6a37f312c8 Issue 64: Convert tool parameter types to json_to<T>() template syntax
Convert EditToolParams, ReadFileToolParams, SkillToolParams, and
AgentTaskParams from using json_to_##Type() function calls to the
json_to<Type>() template specialization syntax.

Updated files:
- tools/edit_tool.cpp
- tools/file_read_tool.cpp
- src/skill.cpp
- tools/agent_tool.cpp
- src/main.cpp (-R flag handler)
2026-05-28 07:35:21 -05:00
darthrubik 25b815cebe Issue #60: Convert remaining optional types to json_to<T>() template syntax
Update ListFilesToolParams, GrepToolParams, and ShellToolParams
call sites to use the template json_to<T>() function instead of
the old json_to_##Type() style.

- tools/file_list_tool.cpp: Use json_to<ListFilesToolParams>()
- tools/grep_tool.cpp: Use json_to<GrepToolParams>()
- tools/shell_tool.cpp: Use json_to<ShellToolParams>()
- src/main.cpp: Update -R flag handler for these three types
2026-05-28 07:18:47 -05:00
darthrubik 7514557b94 Remove try/catch from grep_tool.cpp (issue #58)
- Add make_regex_safely() helper to json_types.hpp that wraps std::regex
  construction and returns std::expected<std::regex, std::string>
- Replace try/catch for regex compilation with make_regex_safely()
- Replace try/catch for filesystem operations with std::error_code overloads
  (following the pattern in skill.cpp)

All acceptance criteria met:
- No try/catch blocks in tools/grep_tool.cpp
- Invalid regex patterns produce clear error messages
- Filesystem errors during directory iteration produce clear error messages
- test_tool_grep.sh passes
- No throw statements in modified file
2026-05-27 18:26:51 -05:00
darthrubik 2a927dc503 issue 57: Convert remaining tool parameters to json_to<T>()
Apply the parameter conversion pattern from issue 55 to all remaining
tools. Each tool's convert_params lambda now uses json_to_<Type>()
instead of .get<T>() for exception-free parameter parsing.

Tools converted:
- shell_tool -> json_to_ShellToolParams()
- file_read_tool -> json_to_ReadFileToolParams()
- file_list_tool -> json_to_ListFilesToolParams()
- grep_tool -> json_to_GrepToolParams()
- skill_tool -> json_to_SkillToolParams()

All 28 tests pass.
2026-05-27 18:18:27 -05:00
darthrubik d2b94485c4 Issue 55: Tool Parameter Error Handling Infrastructure
Change the Tool interface to support error handling during parameter conversion:
- ToolParamsConverter now returns std::expected<std::any, std::string>
- ToolRegistry::invoke() handles conversion errors and returns them as tool result strings
- Replace NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE with DRH_DEFINE_TYPE_REQUIRED for SkillToolParams and AgentTaskParams
- edit_tool uses json_to_EditToolParams() instead of args.get<EditToolParams>()
- agent_tool uses json_to_AgentTaskParams() instead of args.get<AgentTaskParams>()
- Add test for missing required field error handling
2026-05-27 18:09:20 -05:00
darthrubik 4bce22b66f Implement issue 51: Harness::chat() returns std::expected with complete error handling
- Updated Harness::chat() to return std::expected<std::string, std::string>
- Client errors are retried up to 20 times, then returns 'max retries exceeded'
- Tool invocation errors are returned as tool result string to LLM
- Max tool iterations returns 'max tool iterations exceeded' error
- Removed try/catch block for JSON parsing, using parse_json_safely() instead
- Updated main.cpp to handle expected return type from chat()
- Updated agent_tool.cpp to handle expected return type from chat()
- Added tests for client retry exhaustion, max tool iterations, and tool invocation errors
2026-05-27 17:40:04 -05:00
darthrubik f85abd85b6 Issue #48: Update ToolRegistry methods to return std::expected
- register_tool() returns std::expected<void, std::string> for duplicate detection
- invoke() returns std::expected<std::string, std::string> for unknown tool errors
- create_subregistry() returns std::expected<ToolRegistry, std::string> for missing tools
- All throw statements removed from tool_registry.cpp
- Updated main.cpp to use expect_or_exit() for startup tool registration
- Updated harness.cpp and agent_tool.cpp to handle new return types

All 26 existing tests pass.
2026-05-27 17:11:26 -05:00
darthrubik 5d6a102b30 Safe shell 2026-05-12 14:15:55 -05:00
darthrubik 6ede248f50 Refactoring a lot more json stuff 2026-05-11 15:01:55 -05:00
darthrubik 757835758d Fixing non-determinism bug in shell tool 2026-05-09 12:44:50 -05:00
darthrubik 3b176a39c3 Merge pull request 'Better timeout logic' (#4) from feature/shell-timeout into master
Reviewed-on: https://192.168.8.63/git/darthrubik/drharness/pulls/4
2026-05-08 18:51:21 -05:00
darthrubik 237cd3e1c7 Making timeout a 'true' timeout 2026-05-08 18:42:12 -05:00
darthrubik 7932c098cb Fixing bug with ms timeout 2026-05-08 18:36:14 -05:00
darthrubik 09407cf7c9 Removing extra waitpid 2026-05-08 18:30:11 -05:00
darthrubik 96803e8a16 Better timeout logic
The original timeout logic was kindof a hack because it just called
timeout (<your command here>), which didn't always work as expected.
2026-05-08 18:16:09 -05:00
darthrubik 4772c4d655 Adding virtual class 'client' 2026-05-08 17:11:01 -05:00
darthrubik a7229648d0 Revert "Refactor to respond with 'HarnessResponse' instead of string"
This reverts commit b6b4f99b10.
2026-05-08 14:41:35 -05:00
darthrubik 9a7dcc9668 Adding max matches to grep 2026-05-06 13:12:29 -05:00
darthrubik 4f1210baea Adding grep tool 2026-05-06 08:47:26 -05:00
darthrubik 49653f304d Fixing bug with file list tool 2026-05-04 13:44:39 -05:00
darthrubik 3ce89b690a Adding read file tool 2026-05-04 13:36:53 -05:00
darthrubik 1732b68dcc Adding file list tool 2026-05-04 13:30:39 -05:00
darthrubik 46a98ebcfd Adding edit tool basically cloned from opencode 2026-04-29 06:42:51 -05:00
darthrubik 1854a3fb9b Adding timeout to shell commands 2026-04-28 15:33:37 -05:00
darthrubik b6b4f99b10 Refactor to respond with 'HarnessResponse' instead of string 2026-04-23 17:14:22 -05:00
darthrubik 8abb8c9e67 Refactoring to put the create_subregistry code in the tool registry class 2026-04-18 19:21:10 -05:00
darthrubik f05cfd4a7e Add depth-aware visibility for subagent execution
Subagents now print their activity to stderr with a '>' prefix per nesting
level, making it easy to trace arbitrarily deep agent chains. A file-scope
depth counter (RAII-guarded) tracks the current call depth at runtime so
the correct prefix is always computed regardless of how deeply agents nest.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 18:46:17 -05:00
darthrubik 07e978f8f9 Add subagent mode via agent-as-tool pattern
Each AgentProfile defines a model and a subset of tools. Profiles are
registered as callable tools in the main harness so the orchestrator LLM
can delegate tasks to specialized subagents. Each invocation runs an
isolated Harness with fresh history; only the final answer is returned,
keeping the orchestrator context clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 18:45:43 -05:00
darthrubik 5a40ebd8d8 Initial implementation of drharness AI harness
C++20 agentic loop targeting a local Ollama instance via the OpenAI-compatible
/v1/chat/completions API. Includes an extensible tool registry pattern and a
shell command runner as the first tool. Builds with CMake + system libcurl +
nlohmann-json.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 18:09:34 -05:00