@@ -643,6 +643,21 @@ static bool write_file(const std::string &path, const std::string &data) {
643643 return f.good ();
644644}
645645
646+ static bool make_dir (const std::string &path) {
647+ try {
648+ std::filesystem::path p (path);
649+ if (fs::exists (p)) {
650+ return true ;
651+ }
652+ std::error_code ec;
653+ return fs::create_directories (p, ec);
654+ }
655+ catch (const std::filesystem::filesystem_error &e) {
656+ log_write (" mkdir failed [%s]" , e.what ());
657+ return false ;
658+ }
659+ }
660+
646661//
647662// System prompt
648663//
@@ -659,6 +674,7 @@ static std::string build_system_prompt(const std::vector<std::string> &knowledge
659674 " TOOL:LIST [dir] list files (default: sandbox root)\n "
660675 " TOOL:READ <file> read file contents\n "
661676 " TOOL:WRITE <file> <text> write text to file\n "
677+ " TOOL:MKDIR <dir> create a subfolder inside the sandbox\n "
662678 " TOOL:EXISTS <file> YES or NO\n "
663679 " TOOL:RUN <prog> [args] run program inside sandbox\n "
664680 " TOOL:DATE current date\n "
@@ -1768,7 +1784,7 @@ std::string AgentState::process_tool(const std::string &cmd, const NitroConfig &
17681784 const std::vector<std::string> &run_allowed = cfg.run_allowed ;
17691785
17701786 std::string op, arg1, arg2;
1771- auto sp1 = cmd.find ( ' ' );
1787+ auto sp1 = cmd.find_first_of ( " \n " );
17721788 if (sp1 == std::string::npos) {
17731789 op = trim (cmd);
17741790 } else {
@@ -1839,32 +1855,33 @@ std::string AgentState::process_tool(const std::string &cmd, const NitroConfig &
18391855 if (!tui.confirm_dialog (std::format (" Allow model to write {}?" , p))) {
18401856 return " ERROR: action prevented by user" ;
18411857 }
1842- std::string content = disclose (strip_code_fences (arg1, arg2), ' `' , ' `' );
1858+ std::string content = disclose (disclose ( strip_code_fences (arg1, arg2), ' `' , ' `' ), ' " ' , ' " ' );
18431859 return write_file (p, content) ? " OK: written to " + arg1 : " ERROR: write failed for " + arg1;
18441860 }
1861+ if (op == " TOOL:MKDIR" ) {
1862+ std::string p = resolve (arg1);
1863+ if (!path_in_sandbox (sandbox, p)) {
1864+ return " ERROR: path outside sandbox" ;
1865+ }
1866+ return make_dir (p) ? " OK: created " + arg1 : " ERROR: mkdir failed for " + arg1;
1867+ }
18451868 if (op == " TOOL:CURL" ) {
18461869 return tool_curl (arg1);
18471870 }
18481871 if (op == " TOOL:INTROSPECT" ) {
18491872 return introspect (cfg);
18501873 }
18511874 if (op == " TOOL:RUN" ) {
1852- std::string prog = resolve (arg1);
1853- if (!path_in_sandbox (sandbox, prog)) {
1854- return " ERROR: path outside sandbox" ;
1855- }
18561875 if (!run_allowed.empty ()) {
1857- std::string basename = fs::path (prog).filename ().string ();
1858- bool permitted = ranges::any_of (run_allowed,
1859- [&](const std::string &a){ return a == basename; });
1876+ bool permitted = ranges::any_of (run_allowed, [&](const std::string &a) {return a == arg1;});
18601877 if (!permitted) {
1861- return " ERROR: '" + basename + " ' is not in the TOOL:RUN allowlist. "
1878+ return " ERROR: '" + arg1 + " ' is not in the TOOL:RUN allowlist. "
18621879 " Use /set run_allowed <name> to permit it." ;
18631880 }
1864- } else if (!tui.confirm_dialog (std::format (" Allow {} to run?" , prog ))) {
1881+ } else if (!tui.confirm_dialog (std::format (" Allow {} {} to run?" , arg1, arg2 ))) {
18651882 return " ERROR: prevented by user" ;
18661883 }
1867- std::string command = prog + " " + arg2 + " 2>&1" ;
1884+ std::string command = arg1 + " " + arg2 + " 2>&1" ;
18681885 FILE *fp = popen (command.c_str (), " r" );
18691886 if (!fp) {
18701887 return " ERROR: popen failed" ;
0 commit comments