BareGit

Use the agent loop and rolling memory in the demo CLI

Replace manual LLM and tool orchestration with Agent::send and bounded
conversation memory. Add commands to clear history and toggle streaming.
Author: MetroWind <chris.corsair@gmail.com>
Date: Fri Oct 2 09:49:36 2026 -0700
Commit: 833691e0e507c5af760bbbf86dc34b1885e79765

Changes

diff --git a/demo/main.cpp b/demo/main.cpp
index 21fc201..51d7d87 100644
--- a/demo/main.cpp
+++ b/demo/main.cpp
@@ -1,11 +1,11 @@
 #include <cstdlib>
 #include <iostream>
+#include <memory>
 #include <string>
 #include <string_view>
 #include <type_traits>
 #include <utility>
 #include <variant>
-#include <vector>
 
 #include <agent_smith/agent_smith.hpp>
 
@@ -30,7 +30,7 @@ void printUsage(std::ostream& output)
            << "\n"
            << "The demo provides a tool for current weather by city.\n"
            << "\n"
-           << "Commands: /help, /quit\n";
+           << "Commands: /help, /clear, /stream on, /stream off, /quit\n";
 }
 
 agent_smith::ToolSchema weatherSchema()
@@ -85,52 +85,21 @@ queryWeather(nlohmann::json arguments, agent_smith::ToolContext context)
     };
 }
 
-void appendToolResults(std::vector<agent_smith::Message>& messages,
-                       agent_smith::ToolRegistry& tools,
-                       agent_smith::Runtime& runtime,
-                       const std::vector<agent_smith::ToolCall>& calls)
+void printToolResult(const agent_smith::ToolMessage& result)
 {
-    for(const agent_smith::ToolCall& call : calls)
-    {
-        agent_smith::Tool* tool = tools.find(call.name);
-        if(tool == nullptr)
-        {
-            const std::string content = "unknown tool: " + call.name;
-            std::cout << "[tool error: " << content << "]\n";
-            messages.emplace_back(agent_smith::ToolMessage{
-                .tool_call_id = call.id,
-                .name = call.name,
-                .content = content,
-                .is_error = true,
-            });
-            continue;
-        }
-
-        auto result = syncWait(tool->invoke(
-            call.arguments,
-            agent_smith::ToolContext{
-                .call_id = call.id,
-                .runtime = &runtime,
-            }));
-        if(!result)
-        {
-            std::cout << "[tool error: " << result.error().msg() << "]\n";
-            messages.emplace_back(agent_smith::ToolMessage{
-                .tool_call_id = call.id,
-                .name = call.name,
-                .content = result.error().msg(),
-                .is_error = true,
-            });
-            continue;
-        }
+    std::cout << (result.is_error ? "[tool error: " : "[tool result: ")
+              << result.content << "]\n";
+}
 
-        std::cout << "[tool result: " << result->content << "]\n";
-        messages.emplace_back(agent_smith::ToolMessage{
-            .tool_call_id = call.id,
-            .name = call.name,
-            .content = result->content,
-            .is_error = result->is_error,
-        });
+void printUsageReport(const agent_smith::Message& message)
+{
+    const auto* assistant =
+        std::get_if<agent_smith::AssistantMessage>(&message);
+    if(assistant != nullptr && assistant->usage)
+    {
+        std::cout << "[usage: prompt=" << assistant->usage->prompt
+                  << ", completion=" << assistant->usage->completion
+                  << ", total=" << assistant->usage->total << "]\n";
     }
 }
 
@@ -195,12 +164,30 @@ int main(int argc, char* argv[])
 
     agent_smith::Runtime runtime;
     agent_smith::LlmClient client(runtime, std::move(config));
-    agent_smith::ToolRegistry tools;
-    tools.add(agent_smith::makeTool(weatherSchema(), queryWeather));
-    std::vector<agent_smith::Message> messages;
-    std::cout << "Agent Smith " << agent_smith::VERSION
-              << " provider test CLI. Enter /help for commands.\n";
+    // Retain at most 64 messages and approximately 8K tokens of history.
+    auto memory = std::make_shared<agent_smith::RollingWindowMemory>(
+        agent_smith::RollingWindowConfig{
+            .max_messages = 64,
+            .max_tokens = 8192,
+        });
+    auto tree = std::make_shared<agent_smith::AgentTreeState>();
+    agent_smith::Agent agent(runtime, client, memory,
+        {.max_iterations = 16, .tree_state = tree});
+    auto registered = agent.addTool(
+        agent_smith::makeTool(weatherSchema(), queryWeather));
+    if(!registered)
+    {
+        std::cerr << "Tool registration failed: "
+                  << registered.error().msg() << '\n';
+        return 2;
+    }
+    agent.hooks().on_stream_event = printStreamEvent;
+    agent.hooks().on_tool_result = printToolResult;
+    agent.hooks().on_message = printUsageReport;
 
+    std::cout << "Agent Smith " << agent_smith::VERSION
+              << " agent CLI. Enter /help for commands.\n";
+    bool stream = true;
     std::string prompt;
     while(std::cout << "> " << std::flush, std::getline(std::cin, prompt))
     {
@@ -213,49 +200,40 @@ int main(int argc, char* argv[])
             printUsage(std::cout);
             continue;
         }
+        if(prompt == "/clear")
+        {
+            memory->clear();
+            tree->iterations.store(0);
+            std::cout << "Conversation cleared.\n";
+            continue;
+        }
+        if(prompt == "/stream on" || prompt == "/stream off")
+        {
+            stream = prompt == "/stream on";
+            std::cout << "Streaming " << (stream ? "on" : "off") << ".\n";
+            continue;
+        }
         if(prompt.empty())
         {
             continue;
         }
 
-        const std::size_t message_count = messages.size();
-        messages.emplace_back(agent_smith::UserMessage{.content = prompt});
-        bool request_failed = false;
-        for(std::size_t iteration = 0; iteration < 4; ++iteration)
+        // Each interactive turn starts a fresh tree iteration budget.
+        tree->iterations.store(0);
+        auto result = agent_smith::syncWait(agent.send(
+            agent_smith::AgentRequest{
+                .message = {prompt},
+                .stream = stream,
+            }));
+        if(!result)
         {
-            auto result = syncWait(client.completeStream(
-                {
-                    .messages = messages,
-                    .tools = tools.schemas(),
-                },
-                printStreamEvent));
-            if(!result)
-            {
-                std::cerr << "\nRequest failed: " << result.error().msg()
-                          << '\n';
-                request_failed = true;
-                break;
-            }
-
-            messages.emplace_back(std::move(result->message));
-            const auto& assistant =
-                std::get<agent_smith::AssistantMessage>(messages.back());
-            if(assistant.usage)
-            {
-                std::cout << "[usage: prompt=" << assistant.usage->prompt
-                          << ", completion=" << assistant.usage->completion
-                          << ", total=" << assistant.usage->total << "]\n";
-            }
-            if(assistant.tool_calls.empty())
-            {
-                break;
-            }
-
-            appendToolResults(messages, tools, runtime, assistant.tool_calls);
+            std::cerr << "\nRequest failed: " << result.error().msg()
+                      << "\nUse /clear to discard this conversation.\n";
+            continue;
         }
-        if(request_failed)
+        if(!stream)
         {
-            messages.resize(message_count);
+            std::cout << result->content << '\n';
         }
     }