Core Concepts
Fragment layer, codec system, lazy fragments, and engine orchestration
This page explains the foundational concepts behind @deepagents/context: fragments, codecs, lazy fragments, 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' |
isLazyFragment(fragment) | Check if fragment needs lazy ID resolution |
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(...).
Lazy Fragments
Lazy fragments defer ID resolution until save() time. This is useful when you want to update the most recent assistant message without knowing its ID ahead of time.
lastAssistantMessage()
The main lazy helper is lastAssistantMessage(...).
Use it for retry and correction flows where the replacement assistant response should overwrite the latest assistant node on the current branch.
context.set(lastAssistantMessage(correctedContent));
await context.save();During save(), the engine resolves that lazy fragment into a concrete assistant message using the latest assistant ID if one exists.
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.
- Resolve any lazy pending fragments.
- Collect message payloads from persisted history and pending message codecs.
- 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