Context¶
Context provides reactive state management with hierarchical inheritance for AI agents. It combines metadata, rules, tools, and messages with automatic propagation from parent contexts.
Overview¶
Context is a container class that manages configuration and state for agents through:
- Reactive Metadata: Key-value store with automatic inheritance
- Rules: Behavioral guidelines that cascade from parent to child
- Tools: Function collections with automatic deduplication
- Messages: Conversation history with complete lineage
Key Features¶
- ✅ Hierarchical inheritance (parent → child)
- ✅ Automatic metadata propagation using broker pattern
- ✅ Tool deduplication by name (child overrides parent)
- ✅ Message history composition (parent + child)
- ✅ Rule aggregation (combined, not replaced)
- ✅ Type-safe with full TypeScript support
Constructor¶
ContextOptions¶
interface ContextOptions {
/** Parent context(s) for hierarchical inheritance */
context?: Context | Context[];
/** Metadata instance(s) or plain Record<string, string> objects */
metadata?: Metadata | Metadata[] | Record<string, string>;
/** Rule instance(s) or plain strings (auto-converted to new Rule(s)) */
rules?: Rule | Rule[] | string | string[];
/** Tool instance(s) for available functions */
tools?: Tool | Tool[];
/** Message instance(s) or plain MessageOptions objects (auto-converted) */
messages?: Message | Message[] | MessageOptions | MessageOptions[];
}
All properties are optional. The constructor accepts plain objects in addition to class instances: - metadata accepts Record<string, string> — each entry is loaded into a new Metadata node. - rules accepts string or string[] — each string is wrapped in new Rule(s). - messages accepts MessageOptions or MessageOptions[] — each plain object is wrapped in new Message(...).
All properties are optional, allowing you to create empty contexts or fully configured ones.
Examples¶
Empty Context:
Basic Context:
import { Context, Metadata, Rule } from '@arcaelas/agent';
// Using class instances
const ctx = new Context({
metadata: new Metadata().set("app", "MyApp").set("version", "1.0"),
rules: [new Rule("Maintain professional tone")]
});
// Using plain objects (also valid)
const ctx2 = new Context({
metadata: { app: "MyApp", version: "1.0" },
rules: ["Maintain professional tone"],
messages: [{ role: "user", content: "Hello" }]
});
console.log(ctx.metadata.get("app")); // "MyApp"
console.log(ctx.rules.length); // 1
Context with Inheritance:
const parent_ctx = new Context({
metadata: new Metadata().set("company", "Acme Corp"),
rules: [new Rule("Be helpful and courteous")]
});
const child_ctx = new Context({
context: parent_ctx, // Inherits everything from parent
metadata: new Metadata().set("department", "Sales"),
rules: [new Rule("Focus on customer needs")]
});
console.log(child_ctx.metadata.get("company")); // "Acme Corp" (inherited)
console.log(child_ctx.metadata.get("department")); // "Sales" (local)
console.log(child_ctx.rules.length); // 2 (parent + local)
Multiple Parent Contexts:
const auth_ctx = new Context({
metadata: new Metadata().set("auth_enabled", "true")
});
const logging_ctx = new Context({
metadata: new Metadata().set("log_level", "info")
});
const app_ctx = new Context({
context: [auth_ctx, logging_ctx], // Inherits from both
metadata: new Metadata().set("app_name", "Dashboard")
});
console.log(app_ctx.metadata.get("auth_enabled")); // true
console.log(app_ctx.metadata.get("log_level")); // "info"
console.log(app_ctx.metadata.get("app_name")); // "Dashboard"
Properties¶
metadata¶
Reactive metadata store with automatic inheritance from parent contexts.
Behavior: - Inherits all metadata from parent context(s) - Local metadata can override inherited values - Changes propagate using broker pattern
Example:
const parent = new Context({
metadata: new Metadata()
.set("theme", "light")
.set("lang", "en")
});
const child = new Context({
context: parent,
metadata: new Metadata().set("theme", "dark") // Override
});
console.log(child.metadata.get("theme")); // "dark" (overridden)
console.log(child.metadata.get("lang")); // "en" (inherited)
rules¶
Combined rules from parent context(s) and local rules.
Behavior: - Returns parent rules first, then local rules - Setter is idempotent: filters out inherited rules by reference equality before storing locals. Rules aggregate (no deduplication by content).
Example:
const global_ctx = new Context({
rules: [new Rule("Always be polite")]
});
const sales_ctx = new Context({
context: global_ctx,
rules: [new Rule("Focus on benefits")]
});
console.log(sales_ctx.rules.length); // 2
// Replace local rules only
sales_ctx.rules = [
new Rule("Focus on ROI"),
new Rule("Use customer testimonials")
];
console.log(sales_ctx.rules.length); // 3 (1 parent + 2 new local)
tools¶
Combined tools from parent context(s) and local tools with automatic deduplication by name.
Behavior: - Local tools override parent tools with same name (deduplication by name) - Returns unique tools only (no duplicates) - Setter is idempotent: filters out inherited tools by reference equality before storing locals
Example:
import { Tool } from '@arcaelas/agent';
const base_ctx = new Context({
tools: [
new Tool("search", async (agent) => "base search"),
new Tool("analyze", async (agent) => "base analyze")
]
});
const enhanced_ctx = new Context({
context: base_ctx,
tools: [
new Tool("search", async (agent) => "enhanced search"), // Overrides parent
new Tool("translate", async (agent) => "translation") // New tool
]
});
const all_tools = enhanced_ctx.tools;
console.log(all_tools.map(t => t.name)); // ["analyze", "translate", "search"]
// "search" uses enhanced version, not base version
messages¶
Combined message history from parent context(s) and local messages.
Behavior: - Returns parent messages first, then local messages - Maintains chronological order (parent → child) - Setter is idempotent: filters out inherited messages by reference equality before storing locals. This means ctx.messages = ctx.messages.concat(x) is safe — it won't duplicate inherited messages.
Example:
import { Message } from '@arcaelas/agent';
const parent_ctx = new Context({
messages: [
new Message({ role: "system", content: "You are a helpful assistant" })
]
});
const child_ctx = new Context({
context: parent_ctx,
messages: [
new Message({ role: "user", content: "Hello!" })
]
});
console.log(child_ctx.messages.length); // 2
// Idempotent setter — safe to concat without duplicating inherited messages
child_ctx.messages = child_ctx.messages.concat(
new Message({ role: "assistant", content: "Hi there!" })
);
console.log(child_ctx.messages.length); // 3
Removed methods:
appendMessages()andspliceAt()no longer exist. Use the idempotent setter with array spread/concat instead.
Inheritance Rules¶
Understanding how properties are inherited:
| Property | Inheritance Behavior | Override Behavior | Deduplication |
|---|---|---|---|
| metadata | Resolved via broker (parent + child) | Child overrides parent keys (last writer wins) | None — last value at read time |
| rules | Concatenated (parent + child) | No override, aggregates | None |
| tools | Merged (parent + child) | Child overrides by name | By name |
| messages | Concatenated (parent + child) | No override, aggregates | None |
Visualization¶
const parent = new Context({
metadata: new Metadata().set("a", 1).set("b", 2),
rules: [new Rule("R1")],
tools: [new Tool("T1", async (agent) => "parent")],
messages: [new Message({ role: "system", content: "M1" })]
});
const child = new Context({
context: parent,
metadata: new Metadata().set("b", 3).set("c", 4), // 'b' overrides
rules: [new Rule("R2")], // Adds to R1
tools: [new Tool("T1", async (agent) => "child")], // Replaces T1
messages: [new Message({ role: "user", content: "M2" })] // Adds to M1
});
// Results:
// metadata.all() = { a: 1, b: 3, c: 4 }
// rules.length = 2 (R1, R2)
// tools.length = 1 (T1 from child)
// messages.length = 2 (M1, M2)
Common Patterns¶
Pattern: Configuration Layers¶
Create hierarchical configuration with global → department → team layers:
// Global company context
const company_ctx = new Context({
metadata: new Metadata()
.set("company", "Acme Corp")
.set("compliance", "GDPR"),
rules: [new Rule("Protect customer privacy")]
});
// Department context
const sales_ctx = new Context({
context: company_ctx,
metadata: new Metadata().set("department", "Sales"),
rules: [new Rule("Focus on customer value")],
tools: [crm_tool, quote_tool]
});
// Team context
const team_a_ctx = new Context({
context: sales_ctx,
metadata: new Metadata().set("team", "Team A").set("region", "EMEA"),
rules: [new Rule("Respond in customer's timezone")]
});
Pattern: Feature Flags¶
Use metadata for feature toggles:
const base_ctx = new Context({
metadata: new Metadata()
.set("feature_analytics", true)
.set("feature_experimental", false)
});
const beta_ctx = new Context({
context: base_ctx,
metadata: new Metadata().set("feature_experimental", true) // Enable for beta
});
// Check feature flags
if (beta_ctx.metadata.get("feature_experimental")) {
// Use experimental features
}
Pattern: Tool Composition¶
Build specialized tool sets through inheritance:
const basic_tools_ctx = new Context({
tools: [time_tool, weather_tool]
});
const advanced_tools_ctx = new Context({
context: basic_tools_ctx,
tools: [database_tool, api_tool] // Adds to basic tools
});
const specialized_ctx = new Context({
context: advanced_tools_ctx,
tools: [
new Tool("weather", custom_weather_impl) // Override weather
]
});
Pattern: Conversation Templates¶
Reuse conversation starters:
const support_template = new Context({
messages: [
new Message({
role: "system",
content: "You are a customer support specialist. Be helpful and empathetic."
}),
new Message({
role: "assistant",
content: "Hello! How can I help you today?"
})
]
});
// Create new conversation from template
const customer_session = new Context({
context: support_template,
metadata: new Metadata()
.set("customer_id", "12345")
.set("priority", "high")
});
Best Practices¶
1. Keep Contexts Focused¶
Each context should represent a single concern:
// ✅ Good: Focused contexts
const auth_ctx = new Context({
metadata: new Metadata().set("auth_enabled", "true")
});
const logging_ctx = new Context({
metadata: new Metadata().set("log_level", "info")
});
// ❌ Bad: Too many concerns in one context
const everything_ctx = new Context({
metadata: new Metadata()
.set("auth_enabled", true)
.set("log_level", "info")
.set("theme", "dark")
.set("cache_ttl", 3600)
// ... many more unrelated settings
});
2. Use Clear Naming¶
Name contexts based on their scope:
const global_ctx = new Context({ /* ... */ });
const company_ctx = new Context({ context: global_ctx });
const department_ctx = new Context({ context: company_ctx });
const team_ctx = new Context({ context: department_ctx });
const user_ctx = new Context({ context: team_ctx });
3. Minimize Context Depth¶
Avoid deeply nested hierarchies (max 3-4 levels):
// ✅ Good: Reasonable depth
app → department → team
// ❌ Bad: Too deep
global → org → region → country → state → city → office → department → team
4. Document Override Behavior¶
When overriding tools or metadata, document why:
const custom_ctx = new Context({
context: base_ctx,
tools: [
// Override default search with custom implementation
// for better performance with our database
new Tool("search", custom_search_implementation)
]
});
Type Safety¶
Context is fully typed with TypeScript:
import { Context, ContextOptions, Metadata, Rule, Tool, Message } from '@arcaelas/agent';
// All types are inferred
const ctx = new Context({
metadata: new Metadata(), // Type: Metadata
rules: [new Rule("...")], // Type: Rule[]
tools: [new Tool("...", async (agent) => "")], // Type: Tool[]
messages: [new Message({ role: "user", content: "..." })] // Type: Message[]
});
// Property types are enforced
const metadata: Metadata = ctx.metadata; // ✅
const rules: Rule[] = ctx.rules; // ✅
const tools: Tool[] = ctx.tools; // ✅
const messages: Message[] = ctx.messages; // ✅
Related¶
- Metadata - Key-value store used by Context
- Rule - Behavioral guidelines managed by Context
- Tool - Functions orchestrated by Context
- Message - Conversation history in Context
- Agent - Uses Context for configuration
- Context Inheritance Example - Practical patterns
Next: Learn about Metadata →