Skip to main content

Context Management

Context management in Egregore provides DOM-like manipulation of agent memory with powerful lifecycle controls. This page covers the fundamental operations for inserting, updating, deleting, and querying context components.

Core Operations

Inserting Components

Use pact_insert() to add components at specific coordinates:
from egregore.core.context_management.pact.components import TextContent

# Insert at specific position
component = TextContent(content="User prefers concise responses")
context.pact_insert("d0, 1, 0", component)

# Components can have keys for easy lookup
tagged_component = TextContent(
    content="Important metadata",
    key="metadata",
    tags=["user", "preferences"]
)
context.pact_insert("d0, 1, 1", tagged_component)
Insert behavior:
  • Creates component at exact coordinates
  • Shifts existing components if needed
  • Respects Core Offset Layout Rule

Updating Components

Use pact_update() with different modes:
# Replace mode: Replace component at exact position
context.pact_update(
    "d0, 1, 0",
    updated_component,
    mode="replace"
)

# Append mode: Add using offset rule (next available offset)
context.pact_update(
    "d0, 1",
    new_component,
    mode="append"
)
Replace overwrites at exact coordinates. Append finds the next available offset automatically.

Deleting Components

Use pact_delete() to remove components:
# Delete by coordinates
context.pact_delete("d0, 1, 0")

# Delete by key
context.pact_delete(key="metadata")

# Deleting core (offset 0) may trigger container reorganization
context.pact_delete("d0, 0, 0")  # May affect entire container
Deleting the core (offset 0) may cause the entire container to be removed if it becomes empty.

Querying Components

Multiple ways to access components:
# Index access (requires 2+ coordinates)
component = context[0, 1]        # Depth 0, position 1
component = context[0, 1, 0]     # Full 3-coordinate access

# String selector
component = context["d0, 1, 0"]

# By key
component = context.get_by_key("metadata")

# By tags
components = context.get_by_tags(["user", "important"])

Component Lifecycle Types

Egregore provides four lifecycle types based on TTL and cadence parameters:

1. Permanent (ttl=None)

Never expires - most common for message content and persistent metadata.
# Permanent component
note = TextContent(
    content="User's name is Alice",
    ttl=None  # or omit ttl parameter
)
context.pact_insert("d0, 1, 0", note)

# Participates in ODI - moves with message history
# (0,1,0) → (1,1,0) → (2,1,0) as messages are added
Use cases:
  • User preferences that shouldn’t expire
  • Important metadata that needs to persist
  • Historical conversation context

2. Temporary (ttl=N, no cadence)

Expires after N turns, gone forever.
# Temporary reminder - expires after 3 turns
reminder = TextContent(
    content="Ask about preferences in 3 turns",
    ttl=3
)
context.pact_insert("d0, 1, 0", reminder)

# Turn 1: age=0, still alive
# Turn 2: age=1, still alive
# Turn 3: age=2, still alive
# Turn 4: age=3, EXPIRES and removed
Use cases:
  • Temporary reminders
  • Short-term task tracking
  • Contextual hints that expire

3. Sticky (ttl=1, cadence=1)

Expires and reappears each turn at the same relative coordinates. Follows parent through ODI.
# Sticky component - always at same relative position
sticky_note = TextContent(
    content="Current task: Research",
    ttl=1,
    cadence=1
)
context.pact_insert("d0, 1, 0", sticky_note)

# Every turn:
# - Expires at current position
# - Reappears at same relative coordinates
# - Follows parent message through ODI movements
Use cases:
  • Current task/status indicators
  • Persistent UI-like elements
  • Context that needs to stay visible but refresh each turn

4. Cyclic (ttl=N, cadence=M)

Expires after N turns, reappears every M turns.
# Cyclic reminder - appears every 5 turns
cyclic = TextContent(
    content="Check in with user",
    ttl=2,       # Lives for 2 turns
    cadence=5    # Reappears every 5 turns
)
context.pact_insert("d0, 1, 0", cyclic)

# Turn 1-2: Visible
# Turn 3: Expires, hidden
# Turn 4-5: Hidden
# Turn 6-7: Reappears, visible
# Turn 8: Expires, hidden
# And so on...
Use cases:
  • Periodic check-ins
  • Recurring reminders
  • Scheduled context injection

TTL Lifecycle Management

How TTL Works

TTL (Time-To-Live) is managed by the MessageScheduler:
  1. Episode advancement - Each turn increments current_episode
  2. Age calculation - age = current_episode - component.created_at_episode
  3. Expiration check - If age >= ttl, component expires
  4. Removal or rehydration - Component removed or rehydrated based on cadence
# Component lifecycle tracking
component = TextContent(content="Expires in 3 turns", ttl=3)
context.pact_insert("d0, 1, 0", component)

# Internal tracking:
# - created_at_episode = 10 (current episode)
# - Episode 11: age=1, alive
# - Episode 12: age=2, alive
# - Episode 13: age=3, EXPIRES

Cadence and Rehydration

When a component has both TTL and cadence:
# ttl=2, cadence=5
component = TextContent(content="Cyclic", ttl=2, cadence=5)

# Episode 0: Created, visible (age=0)
# Episode 1: Visible (age=1)
# Episode 2: EXPIRES (age=2 >= ttl)
# Episode 3-4: Hidden
# Episode 5: REHYDRATES (cadence triggers)
# Episode 6: Visible (age=1 after rehydration)
# Episode 7: EXPIRES (age=2)
# Cycle repeats...
Rehydration creates a new component with the same content at the same coordinates. The original component is permanently removed.

Message Container Pattern

Egregore separates message content from context components:

Message Structure

Depth 0 (Active Message)
├── Position 0 (Message Core) - d0,0,0
│   ├── Offset 0: User/Assistant message text
│   ├── Offset 1: Additional message content
│   └── Offset -1: Alternative content
└── Position 1+ (Components) - d0,1+,*
    ├── d0,1,0: Metadata component
    ├── d0,1,1: Another component
    └── d0,2,0: Additional attachment

Accessing Messages vs Components

# Access message content
message = context["d0, 0, 0"]  # Core message text

# Access attached components
metadata = context["d0, 1, 0"]
notes = context["d0, 1, 1"]

# Messages at position 0, components at position 1+

Render Lifecycle System

Advanced pattern for dynamic component positioning through stages.

Basic Render Lifecycle

from egregore.core.context_management.pact.components import TextContent
from egregore.core.context_management.pact.context.position import Pos

# Component moves through 3 stages
alert = TextContent(content="Important alert")
alert.render_lifecycle([
    Pos("d0, 0, 1", ttl=2),   # Stage 1: Offset +1 for 2 turns
    Pos("d0, 0, -1", ttl=3),  # Stage 2: Offset -1 for 3 turns
    Pos("d0, 0, -2")          # Stage 3: Offset -2 (permanent)
])

context.pact_insert("d0, 0, 1", alert)

# Turn 1-2: At offset 1
# Turn 3-5: Moves to offset -1
# Turn 6+: Moves to offset -2 permanently

Cycling Lifecycles

# Infinite cycling between positions
indicator = TextContent(content="Status")
indicator.render_lifecycle([
    Pos("d0, 0, 1", ttl=1),
    Pos("d0, 0, -1", ttl=1)
], cycle=True)  # Repeats forever

# Limited cycles
indicator.render_lifecycle([
    Pos("d0, 0, 1", ttl=1),
    Pos("d0, 0, -1", ttl=1)
], cycle=3)  # Stops after 3 complete cycles

Learn More

Complete render lifecycle documentation with advanced patterns

Context API Methods

Core Methods

# Insert with shifting
context.pact_insert(selector: str, component: ContextComponent)

# Update existing
context.pact_update(selector: str, component: ContextComponent, mode: str)

# Delete component
context.pact_delete(selector: str)

# Query by coordinates
component = context[depth, position, offset]

# Query by key
component = context.get_by_key(key: str)

# Query by tags
components = context.get_by_tags(tags: List[str])

# Serialize to PACT format
pact_dict = context.model_dump()

Snapshot Management

# Create snapshot
snapshot_id = context.seal(trigger="checkpoint")

# Access historical state (via agent.history)
historical_context = agent.history.at_snapshot(snapshot_id)

Best Practices

  • Permanent for most content (user info, preferences)
  • Temporary for short-term reminders or hints
  • Sticky for persistent status indicators
  • Cyclic for periodic check-ins or scheduled tasks
# Easy lookup and organization
component = TextContent(
    content="Data",
    key="user_prefs",
    tags=["user", "important", "settings"]
)
Don’t manually track position changes - ODI handles depth shifting automatically:
# Good: Reference by relative coordinates
metadata = context["d0, 1, 0"]

# Bad: Trying to manually track depth changes
# depth = calculate_current_depth()  # Don't do this!
Keep message content at position 0, components at position 1+:
# Messages at position 0
context.pact_insert("d0, 0, 0", message)

# Components at position 1+
context.pact_insert("d0, 1, 0", metadata)
context.pact_insert("d0, 2, 0", notes)

Common Patterns

User Preferences Storage

# Permanent metadata attached to current context
preferences = TextContent(
    content="User prefers: formal tone, concise answers",
    key="user_preferences",
    tags=["user", "preferences"]
)
context.pact_insert("d0, 1, 0", preferences)

# Automatically follows history via ODI

Temporary Task Tracking

# Task expires after 5 turns
task = TextContent(
    content="TODO: Follow up on research question",
    ttl=5,
    key="pending_task"
)
context.pact_insert("d0, 1, 0", task)

Persistent Status Indicator

# Always visible at same position
status = TextContent(
    content="Current Mode: Research",
    ttl=1,
    cadence=1,
    key="current_mode"
)
context.pact_insert("d0, 1, 0", status)

# Update status each turn by key
new_status = TextContent(
    content="Current Mode: Writing",
    ttl=1,
    cadence=1,
    key="current_mode"
)
context.pact_update("d0, 1, 0", new_status, mode="replace")

Scheduled Reminders

# Check in every 10 turns
reminder = TextContent(
    content="Check if user needs help",
    ttl=2,
    cadence=10
)
context.pact_insert("d0, 1, 0", reminder)

Debugging Context

Use ContextExplorer for debugging:
from egregore.analytics.context_explorer import ContextExplorer

explorer = ContextExplorer(context)

# Visualize current state
explorer.print()

# Advance episodes for TTL testing
explorer.step("render")
explorer.print()

Learn More

Complete guide to debugging context with ContextExplorer

What’s Next?

Message Scheduler

Understand episode management and TTL processing

Context History

Learn about snapshots and historical access

Context API Reference

Complete API documentation for context operations

Render Lifecycle

Advanced component positioning patterns