Core Concepts
Fragment layer, codec system, and engine orchestration
This page explains the foundational concepts behind @deepagents/context: fragments, codecs, and the ContextEngine.
Fragment Layer
Fragments are the building blocks of context. In normal usage, you create them through helpers such as:
role()hint()term()guardrail()user()assistantText()fragment()
Every fragment has a stable name, optional runtime metadata, and optional lifecycle fields such as type, persist, and codec.
Fragment Composition
Fragments can be nested to create structured context:
fragment(
'database',
hint('PostgreSQL 15'),
hint('Tables: users, orders'),
fragment(
'constraints',
hint('No DELETE without audit'),
),
);That lets you build hierarchical context without writing prompt strings by hand.
Type Guards
The package provides runtime guards for fragment inspection:
| Guard | Purpose |
|---|---|
isFragment(value) | Check if a value is a ContextFragment |
isFragmentObject(value) | Check if a value is a plain object (not array/fragment/primitive) |
isMessageFragment(fragment) | Check if fragment has type: 'message' |
Codec System
Codecs let fragments materialize or serialize alternate representations.
interface FragmentCodec {
decode(): unknown;
encode(): unknown;
}The important practical rule is:
- message fragments are codec-first
resolve()usescodec.encode()to produceUIMessage[]save()also persistscodec.encode()
Message Lifecycle
┌────────────────────────────────────────────────────────┐
│ Message Lifecycle │
├────────────────────────────────────────────────────────┤
│ │
│ user('Hello') │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Fragment with attached codec │ │
│ │ codec.encode() -> UIMessage │ │
│ │ codec.decode() -> materialized form │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ├──── resolve() ────▶ codec.encode() → messages[]│
│ │ │
│ └──── save() ───────▶ codec.encode() → store │
│ │
└────────────────────────────────────────────────────────┘Built-in Message Helpers
Built-in message helpers attach codecs automatically:
const userMessage = user('Hello');
const assistantMessage = assistantText('Hi there');If you already have an AI SDK UIMessage, use assistant(...) or message(...).
Engine Orchestration
The ContextEngine maintains two internal lanes:
- regular fragments for the system prompt
- pending message fragments for conversation history
set() Routing
set() routes fragments by type:
public set(...fragments: ContextFragment[]) {
for (const fragment of fragments) {
if (isMessageFragment(fragment)) {
this.#pendingMessages.push(fragment);
} else {
this.#fragments.push(fragment);
}
}
return this;
}That separation is why resolve() can render system context and conversation history differently.
Resolve Flow
resolve() produces AI SDK-ready output in this order:
- Initialize chat and active branch.
- Render regular fragments into
systemPrompt. - Load persisted message history from the graph.
- Encode pending message fragments that have not been saved yet.
- Validate the final message list with
validateUIMessages(...).
ResolveResult
interface ResolveResult {
systemPrompt: string;
messages: UIMessage[];
}This is the key contract of the package:
systemPromptis renderer output from regular fragmentsmessagesis a validated AI SDKUIMessage[]
Why This Design
This split keeps the system clean:
- renderers only care about regular context structure
- conversation history stays in graph storage
- message serialization is centralized in codecs
- validation happens at the
resolve()boundary, where@deepagents/contextpromises AI SDK-compatible output
Related Pages
- Resolve And Render - Full
resolve()pipeline - State Management - DAG model, branches, and checkpoints
- Renderers - Renderer architecture and output strategies
- Persistence - Store interface and adapters