Context Injection¶
How the DM agent receives campaign state before each message.
Overview¶
The DM agent doesn't have persistent memory. Before each message, we inject relevant campaign state from the vault files. This keeps the agent grounded in canon.
Context Types¶
Full Context (~2-3KB)¶
Injected on session start or when user sends a resume command (e.g., "continue", "resume", "start session").
## SESSION CONTEXT: Seagate
**CITY: SEAGATE** (This is the setting - do not substitute other location names)
**Day 5** - morning
### Player Character
**Jake** (Wizard)
- HP: 13/13
- Location: the-salty-sigil
- Gold: 15 gp
### Current Location
**The Salty Sigil**
A curio shop in the Dredgeworks dealing in rare magical components...
### NPCs Present
- **Marlena** [active] - intimate-ally
*Voice: slow measured drawl, drops when emotional; Shows vulnerability with Jake*
- **Gareth** [rescued] - ally
### Active Storylines
- **[URGENT]** The Fading Muffle
Token scrying anchor muffled ~36 hours, fading...
→ Next: Execute infiltration before it fails
- **[URGENT]** Silent Circle Salon (TONIGHT)
Infiltrate as Courier Valen...
### Recent Events
- Jake rescued Gareth from Sunfall Hills gully
- Token muffling performed by Marlena
---
Light Context (~60-80 chars)¶
Injected on follow-up messages within an active conversation.
This is enough for the agent to maintain continuity without bloating the context window. The full message history provides additional grounding.
Context Sources¶
| Context Element | Source File | Notes |
|---|---|---|
| Campaign name | Directory name | seagate/ → "Seagate" |
| Day/time | canon/temporal-index.json |
{"current_day": 5, "current_time": "morning"} |
| PC state | canon/pcs/*.md frontmatter |
HP, location, conditions, gold, inventory |
| Current location | canon/locations/*.md |
Matched by PC's location field |
| NPCs present | canon/npcs/*.md |
Filtered by NPC's location field |
| Active threads | canon/open-threads.md |
Parsed by priority level |
| Recent events | canon/timeline.md |
Last 5 events from current day |
Context Builder¶
Located at api/context/builder.py.
from api.context.builder import ContextBuilder
from lib.vault import Vault
vault = Vault("/campaigns/seagate")
builder = ContextBuilder(vault)
# Build context from vault files
ctx = builder.build()
# Format for injection
full_context = builder.format_full(ctx) # Session start
light_context = builder.format_light(ctx) # Follow-up
Key Classes¶
@dataclass
class SessionContext:
campaign_name: str
pc_name: str
pc_class: str
hp_current: int
hp_max: int
location: str
conditions: list[str]
gold: int
current_day: int
current_time: str
current_location: LocationContext | None
npcs_present: list[NPCContext]
threads_high: list[ThreadContext]
threads_medium: list[ThreadContext]
recent_events: list[str]
@dataclass
class NPCContext:
id: str
name: str
role: str
status: str
disposition: str
location: str
personality: str # Extracted from ## Personality section
voice: str # Extracted from voice/speech patterns
Resume Command Detection¶
The system distinguishes between: - Resume commands: User starting/continuing a session → Full context - Normal messages: Mid-conversation actions → Light context
def is_resume_command(message: str) -> bool:
resume_commands = {
'continue', 'resume', 'go', 'start', 'begin', 'proceed',
'what now', 'whats next', "what's next", 'next',
'start session', 'resume session', 'continue session',
}
normalized = message.lower().strip().rstrip('?!.')
return normalized in resume_commands
Short messages like "1", "yes", "attack" use light context and rely on message history.
NPC Personality Extraction¶
For NPCs at the current location, the builder extracts personality and voice info:
## Personality
- Speaks in a slow, measured drawl (drops when emotional)
- Views herself as a "gardener" - supplies ingredients, doesn't judge
Becomes:
- **Marlena** [active] - intimate-ally
*Voice: slow measured drawl, drops when emotional; Views herself as a "gardener"*
This helps the DM maintain consistent NPC voices.
Debugging Context¶
The /campaigns/{id}/context endpoint returns the full context for debugging:
Context is also stored with each message in the injected_context column of rpg.chat_messages.
Known Limitations¶
-
Location changes: Context isn't re-injected when player moves mid-conversation. The system prompt instructs the DM to handle location changes, but NPC lists may be stale.
-
NPC limits: Only 5 NPCs are included to avoid context bloat. Crowded locations may miss some characters.
-
Thread parsing: Markdown parsing is regex-based and may miss unusual formatting.
-
No RAG yet: Entity mentions in player messages don't trigger retrieval. Planned for Phase 3.
Related¶
- Vault Conventions - File structure and frontmatter
- Architecture - Overall system design
- DM Quality Analysis - Known issues and remediation plan