@@ -235,6 +235,7 @@ struct TuiState {
235235 InputHistory history;
236236 // Advance spinner by one frame and redraw the header.
237237 void tick_spinner ();
238+
238239 // Toggle thinking mode; redraws header immediately.
239240 void set_thinking (bool on);
240241 void update_usage (int tokens_sec, const LlamaMemoryInfo &mem);
@@ -288,21 +289,23 @@ struct AgentState {
288289 std::unique_ptr<Llama> llama;
289290 std::unique_ptr<LlamaIter> iter;
290291 std::unique_ptr<Llama> embed_llama;
291- std::unique_ptr<RagDB> rag_db;
292+ std::unique_ptr<RagDB> rag_db;
292293 std::unique_ptr<RagSession> rag_session;
293294 bool model_loaded = false ;
294295 std::string system_prompt;
295296
296297 bool rag_index (const std::string &path, const NitroConfig &cfg, TuiState &tui) const ;
297298 bool rag_load_index (const std::string &path, TuiState &tui) const ;
298- bool run_turn (const std::string &user_message, const NitroConfig &cfg, TuiState &tui) const ;
299+ bool run_turn (const std::string &user_message, const NitroConfig &cfg, TuiState &tui);
299300 bool setup_embed (const std::string &path, TuiState &tui);
300301 bool setup_model (const NitroConfig &cfg, TuiState &tui);
301302 void apply_generation_params (const NitroConfig &cfg) const ;
302303 void reset_conversation (const std::string &sysprompt, TuiState &tui);
304+ std::string memory_info_status () const ;
303305 std::string memory_info_text () const ;
304- std::string process_tool (const std::string &cmd, const NitroConfig &cfg, TuiState &tui) const ;
306+ std::string process_tool (const std::string &cmd, const NitroConfig &cfg, TuiState &tui);
305307 std::string rag_tool (const NitroConfig &cfg, const std::string &agent_query) const ;
308+ std::string restart (const NitroConfig &cfg, TuiState &tui);
306309 float tokens_per_sec () const ;
307310};
308311
@@ -826,7 +829,7 @@ static bool make_dir(const std::string &path) {
826829//
827830// System prompt
828831//
829- static std::string build_system_prompt (NitroConfig &cfg) {
832+ static std::string build_system_prompt (const NitroConfig &cfg) {
830833 std::string p;
831834 p +=
832835 " You are Nitro, an agentic AI assistant for software development. "
@@ -881,7 +884,8 @@ static std::string build_system_prompt(NitroConfig &cfg) {
881884 " TOOL:ASK <query> ask the user for clarification or additional context\n "
882885 " TOOL:INTROSPECT show current model settings\n "
883886 " TOOL:CURL <url> HTTP GET, returns response body (max 32 KB)\n "
884- " TOOL:PERMISSION ask user for explicit permission\n\n "
887+ " TOOL:PERMISSION ask user for explicit permission\n "
888+ " TOOL:RESTART restart after writing current task context to `SESSION.md`\n\n "
885889
886890 " ## Tool Decision Rules\n "
887891 " Use tools ONLY if:\n "
@@ -912,7 +916,27 @@ static std::string build_system_prompt(NitroConfig &cfg) {
912916 " - Ask clarifying questions if the request is ambiguous or missing parameters\n "
913917 " - Prefer direct answers when no tools are needed\n "
914918 " - After each tool result, explain in plain English what was done\n "
915- " - If no user request is provided, respond with a brief readiness message\n\n " ;
919+ " - If no user request is provided, respond with a brief readiness message\n\n "
920+
921+ " ## Auto-Restart Protocol\n "
922+ " **When:** - When KV >= 80% (as reported in the tool results footer).\n "
923+ " **Steps:**\n "
924+ " 1. **Save State:** Write current task context to `SESSION.md` using `TOOL:WRITE`.\n "
925+ " - Include: Timestamp, KV usage, current task description, pending actions, and last conversation summary.\n "
926+ " - Don't check if SESSION.md already exists from another session. just use TOOL:WRITE.\n "
927+ " 2. **Trigger Restart:** Call `TOOL:RESTART`.\n "
928+ " **Example `SESSION.md` Content:**\n "
929+ " ```markdown\n "
930+ " # Session State Snapshot\n "
931+ " **Timestamp:** <date> <time>\n "
932+ " **KV Usage:** <percentage>%\n "
933+ " **Current Task:** <task description>\n "
934+ " **Pending Actions:**\n "
935+ " - <action 1>\n "
936+ " - <action 2>\n "
937+ " **Last Output:**\n "
938+ " <last few lines of conversation>\n "
939+ " ```\n\n " ;
916940
917941 for (const auto &kf : cfg.knowledge_files ) {
918942 std::ifstream f (kf);
@@ -1954,6 +1978,12 @@ float AgentState::tokens_per_sec() const {
19541978 return (float )(iter->_tokens_generated / elapsed);
19551979}
19561980
1981+ std::string AgentState::memory_info_status () const {
1982+ LlamaMemoryInfo m = llama->memory_info ();
1983+ auto message = m.kv_percent > 75 ? " (Warning: Approaching limit)" : " " ;
1984+ return std::format (" \n [INFO] KV Cache: {}%{}" , m.kv_percent , message);
1985+ }
1986+
19571987std::string AgentState::memory_info_text () const {
19581988 if (!model_loaded) return " No model loaded." ;
19591989 LlamaMemoryInfo m = llama->memory_info ();
@@ -2037,10 +2067,22 @@ bool AgentState::rag_index(const std::string &path, const NitroConfig &cfg, TuiS
20372067 return true ;
20382068}
20392069
2070+ std::string AgentState::restart (const NitroConfig &cfg, TuiState &tui) {
2071+ if (fs::exists (" SESSION.md" )) {
2072+ std::vector<std::string> knowledge_files;
2073+ std::string sysp = build_system_prompt (cfg);
2074+ reset_conversation (sysp, tui);
2075+ tui.append_line (ICON_ERR + " Session restarted" );
2076+ tui.redraw_all ();
2077+ return " continue the pending actions found SESSION.md" ;
2078+ }
2079+ return " save work context to SESSION.md then try again" ;
2080+ }
2081+
20402082//
20412083// Tool dispatch
20422084//
2043- std::string AgentState::process_tool (const std::string &cmd, const NitroConfig &cfg, TuiState &tui) const {
2085+ std::string AgentState::process_tool (const std::string &cmd, const NitroConfig &cfg, TuiState &tui) {
20442086 const std::string &sandbox = cfg.sandbox ;
20452087 const std::vector<std::string> &run_allowed = cfg.run_allowed ;
20462088
@@ -2159,6 +2201,10 @@ std::string AgentState::process_tool(const std::string &cmd, const NitroConfig &
21592201 show_tool (" asking: " + arg1 + " " + arg2);
21602202 return tui.readline_blocking ();
21612203 }
2204+ if (op == " TOOL:RESTART" ) {
2205+ show_tool (" restart ..." );
2206+ return restart (cfg, tui);
2207+ }
21622208 if (op == " TOOL:PERMISSION" ) {
21632209 tui.set_thinking (false );
21642210 show_tool (" asking permission: " + arg1 + " " + arg2);
@@ -2197,7 +2243,7 @@ std::string AgentState::process_tool(const std::string &cmd, const NitroConfig &
21972243//
21982244// Agent turn
21992245//
2200- bool AgentState::run_turn (const std::string &user_message, const NitroConfig &cfg, TuiState &tui) const {
2246+ bool AgentState::run_turn (const std::string &user_message, const NitroConfig &cfg, TuiState &tui) {
22012247 if (!model_loaded) {
22022248 tui.append_line (ICON_ERR + " No model loaded. Use /model <path>" );
22032249 tui.redraw_all ();
@@ -2250,9 +2296,11 @@ bool AgentState::run_turn(const std::string &user_message, const NitroConfig &cf
22502296
22512297 log_write (" tool request: mode:[%d] [%s]" , think_mode, tool.c_str ());
22522298 std::string result = process_tool (tool, cfg, tui);
2253- std::string content = TOOL_RESULT + std::vformat (template_str, std::make_format_args (result));
2299+ if (result.empty ()) {
2300+ return ;
2301+ }
2302+ std::string content = TOOL_RESULT + std::vformat (template_str, std::make_format_args (result)) + memory_info_status ();
22542303 log_write (" tool: [%s] result: [%s]" , tool.c_str (), result.c_str ());
2255-
22562304 if (content.size () > llama->max_tool_result_size ()) {
22572305 // Index the content into RAG and tell the model where to find it
22582306 if (embed_llama && rag_db && rag_session) {
@@ -2711,7 +2759,9 @@ int main(int argc, char **argv) {
27112759
27122760 // ── Auto-discover knowledge files ─────────────────────────────────
27132761 for (const char *kf : {" nitro.md" , " AGENTS.md" , " README.md" }) {
2714- if (fs::exists (kf)) cfg.knowledge_files .emplace_back (kf);
2762+ if (fs::exists (kf)) {
2763+ cfg.knowledge_files .emplace_back (kf);
2764+ }
27152765 }
27162766
27172767 // ── Init curl globally ────────────────────────────────────────────
0 commit comments