Skip to content

Context Contract

Context managers handle conversation memory and state.

Purpose

Context managers:

  • Store conversation history
  • Manage token limits
  • Implement compaction strategies
  • Support persistence

Protocol

from typing import Protocol, runtime_checkable

@runtime_checkable
class ContextManager(Protocol):
    async def add_message(self, message: dict) -> None:
        """Add a message to context."""
        ...

    async def get_messages(self) -> list[dict]:
        """Get all messages."""
        ...

    async def should_compact(self) -> bool:
        """Check if compaction is needed."""
        ...

    async def compact(self) -> None:
        """Compact the context to fit token limits."""
        ...

    async def clear(self) -> None:
        """Clear all messages."""
        ...

Mount Function

async def mount(coordinator, config=None):
    config = config or {}
    context = MyContext(config)
    await coordinator.mount("context", context)
    return cleanup_function  # or None

Configuration

Common configuration:

Option Type Description
max_tokens int Maximum context tokens
compaction_strategy string How to compact ("truncate", "summarize")
preserve_system bool Keep system messages on compact

Events

Context managers should emit:

Event When Data
context:pre_compact Before compaction message_count, tokens
context:post_compact After compaction message_count, tokens
context:include Context injected source, content

Message Format

Messages follow this structure:

{
    "role": "user" | "assistant" | "system" | "tool",
    "content": str | list,  # Text or content blocks
    "tool_call_id": str | None,  # For tool results
    "name": str | None,  # Tool name for tool messages
}

Example Implementation

class SimpleContext:
    def __init__(self, config):
        self.max_tokens = config.get("max_tokens", 100000)
        self.messages = []

    async def add_message(self, message):
        self.messages.append(message)

    async def get_messages(self):
        return self.messages.copy()

    async def should_compact(self):
        token_count = self._estimate_tokens()
        return token_count > self.max_tokens * 0.9

    async def compact(self):
        # Simple truncation: remove oldest non-system messages
        system_messages = [m for m in self.messages if m["role"] == "system"]
        other_messages = [m for m in self.messages if m["role"] != "system"]

        # Keep last N messages that fit
        while self._estimate_tokens() > self.max_tokens * 0.7:
            if other_messages:
                other_messages.pop(0)
            else:
                break

        self.messages = system_messages + other_messages

    async def clear(self):
        self.messages = []

    def _estimate_tokens(self):
        # Rough estimate: 4 chars per token
        total_chars = sum(len(str(m.get("content", ""))) for m in self.messages)
        return total_chars // 4

Persistent Context

For session resumption, implement persistence:

class PersistentContext:
    def __init__(self, config):
        self.storage_path = Path(config.get("storage_path"))
        self.messages = self._load()

    async def add_message(self, message):
        self.messages.append(message)
        self._save()

    def _load(self):
        if self.storage_path.exists():
            return json.loads(self.storage_path.read_text())
        return []

    def _save(self):
        self.storage_path.write_text(json.dumps(self.messages))

Authoritative Reference

Context Contract - Complete specification