agent-context 0.1.3

Multi-backend agent context manager with three-zone memory model
Documentation
# agent-context

Backend-agnostic LLM conversation context manager built on the [kameo](https://crates.io/crates/kameo) actor model.

## Three-Zone + Scratch Memory

Messages are split into three storage zones plus a transient scratch layer:

| Zone | Mutability | Purpose |
|------|-----------|---------|
| **immutable** | Read-only | System prompts, preset context |
| **compressed** | Write-once (during compression) | Summaries of older messages |
| **incremental** | Full CRUD | Active conversation |
| **scratch** | Not stored | Per-turn metadata (time, cwd, etc.) via `CommonOpts` |

Concatenation order: immutable → compressed → incremental → scratch.

## Change Notification

Subscribe via `RequestSubscribeChange` to receive `NotifyChange` notifications. Every mutation to the incremental zone triggers a notification. Unsubscribe anytime with `RequestUnsubscribeChange`. Use `RequestSubscribeCompressed` to register a subscriber that can modify the summary and kept messages after compression.

## Usage

```rust
use agent_context::{
    AgentContext, RequestAppend, RequestSend, RequestSendStream, RequestMessages,
    RequestSubscribeChange, ContextBackend, CommonOpts, NotifyChange,
};
use futures::StreamExt;

// 1. Implement ContextBackend for your LLM backend
// 2. Spawn the actor and subscribe to change notifications
let ctx = AgentContext::new(backend, vec![backend.system_message("You are a helpful assistant.")]);
let actor = AgentContext::spawn(ctx);
actor.ask(RequestSubscribeChange { recipient: app_ref.recipient() }).await?;

// 3. Non-streaming conversation
actor.ask(RequestAppend { message: user_msg }).await?;
let response = actor.ask(RequestSend { opts: my_opts }).await?;

// 4. Streaming conversation
let mut stream = actor.ask(RequestSendStream { opts: my_opts }).await??;
while let Some(chunk) = stream.next().await { /* e.g. render chunk in UI */ }
let chunks = stream.take_chunks();
let msg = backend.merge_chunks(&chunks).expect("chunk merge failed");
let msgs = backend.to_request_messages(vec![msg])?;
for m in msgs {
    actor.ask(RequestAppend { message: m }).await?;
}

// 5. Read all messages
let all: Vec<_> = actor.ask(RequestMessages).await?;
```

## ContextBackend

The core trait — implement it to support any LLM provider (DeepSeek, Zhipu, OpenAI, etc.):

- **Message factories**: `user_message`, `system_message`, `tool_message`
- **Format conversion**: `to_system_message`, `to_request_messages`
- **Response parsing**: `extract_messages`, `merge_chunks`
- **Model interaction**: `send`, `send_stream`, `estimate_tokens`

Each backend's `Opts` type must embed `CommonOpts` (model, context_window, max_tokens, auto_compress, scratch) via `AsRef<CommonOpts>`.

## Compression

Automatic compression is triggered before each `RequestSend`/`RequestSendStream` when `CommonOpts::auto_compress` is `true` and the estimated tokens exceed `context_window`. Use `RequestCompress` with `CompressStrategy::Summarize` for manual compression. Register a compressed subscriber via `RequestSubscribeCompressed` to customize the result.

## License

MIT OR Apache-2.0