#pragma once
#include <cstddef>
#include <optional>
#include <vector>
#include <mw/task.hpp>
#include "message.hpp"
namespace agent_smith
{
/// In-process conversation storage. Access from one agent at a time.
class Memory
{
public:
/// Destroy a memory strategy through its interface.
virtual ~Memory() = default;
/// Record a message in conversation order.
virtual void append(Message message) = 0;
/// Copy history with the supplied system message prepended.
/// Keep base_system alive until the returned task completes.
virtual mw::Task<mw::E<std::vector<Message>>>
snapshot(const SystemMessage& base_system) = 0;
/// Return the retained message count, excluding the supplied system prompt.
virtual std::size_t size() const = 0;
/// Remove all retained messages.
virtual void clear() = 0;
/// Remove tool results without a matching call in the latest assistant
/// turn. Each call ID can match only one result; order is preserved.
static void stripOrphans(std::vector<Message>& messages);
};
/// Retains every appended message without truncation or rewriting.
class FullMemory final : public Memory
{
public:
/// Append a message without imposing a budget.
void append(Message message) override;
/// Return an independent copy of the full history with the system prompt.
mw::Task<mw::E<std::vector<Message>>>
snapshot(const SystemMessage& base_system) override;
/// Return the full history's message count.
std::size_t size() const override;
/// Discard the full history.
void clear() override;
private:
std::vector<Message> messages_;
};
/// Limits on retained history; every configured limit applies.
struct RollingWindowConfig
{
/// Maximum message count; absent means unlimited.
std::optional<std::size_t> max_messages = std::nullopt;
/// Maximum UTF-8 byte count of serialized OpenAI messages (no separators).
/// This conservative character estimate includes tool names and arguments.
std::optional<std::size_t> max_chars = std::nullopt;
/// Approximate token budget, converted to max_chars at four bytes/token.
std::optional<std::size_t> max_tokens = std::nullopt;
};
/// Keeps recent history by dropping whole oldest messages on append.
/// Orphaned tool results are also discarded. Limits exclude the supplied
/// system prompt. A message larger than the entire budget is discarded.
class RollingWindowMemory final : public Memory
{
public:
/// Construct a window constrained by all supplied limits (none: unlimited).
explicit RollingWindowMemory(RollingWindowConfig config);
/// Construct a window limited to the most recent max_messages messages.
explicit RollingWindowMemory(std::size_t max_messages);
/// Append a message, enforce budgets, and remove orphaned tool results.
void append(Message message) override;
/// Copy retained history and prepend the unbudgeted system prompt.
mw::Task<mw::E<std::vector<Message>>>
snapshot(const SystemMessage& base_system) override;
/// Return the retained history's message count.
std::size_t size() const override;
/// Discard retained history while preserving configured limits.
void clear() override;
private:
RollingWindowConfig config_;
std::vector<Message> messages_;
};
} // namespace agent_smith