The brain between your agent and your models.
Platform-agnostic context routing kernel for agent runtimes. Classifies inputs, routes to the right model, enforces policy gates, extracts memory candidates, and emits audit events ΓÇö all before a single token gets generated. Drop it into any agent system and stop hardcoding your routing logic.
npm install context-kernelimport { ContextKernel } from "context-kernel";
const kernel = new ContextKernel({
router: {
tokenCompressionThreshold: 10000,
allowPremiumEscalation: true,
routeMap: {
textDefault: "local_text",
multimodal: "local_vision",
urgent: "premium",
codeHighContext: "premium",
},
},
policy: { postOnlyMode: false },
});
const decision = await kernel.decide({
sessionId: "sess-001",
timestamp: new Date().toISOString(),
messages: [{ role: "user", content: "Help me refactor this repo" }],
estimatedTokens: 12000,
});
console.log(decision.route); // "premium" (code + high tokens)
console.log(decision.compress); // true (over threshold)
console.log(decision.taskType); // "code"const kernel = new ContextKernel({
router: { tokenCompressionThreshold: 8000, allowPremiumEscalation: false },
policy: {
postOnlyMode: false,
quietHours: { startHour: 23, endHour: 7 },
rules: [
{ id: "safe-actions", kind: "action_allowlist", actions: ["send", "post"] },
{ id: "no-keys", kind: "secret_regex", patterns: ["AKIA[0-9A-Z]{16}"], severity: "high" },
],
},
});import { fromOpenClawEnvelope } from "context-kernel/adapters/openclaw";
const kernelInput = fromOpenClawEnvelope({
sessionKey: "oc-session-42",
timestamp: new Date().toISOString(),
messages: [{ role: "user", content: "Summarize this image" }],
images: [{ name: "screenshot.png" }],
});
const decision = await kernel.decide(kernelInput);
// inputKind: "multimodal" → routes to vision modelimport { fromHttpEnvelope } from "context-kernel/adapters/http";
const kernelInput = fromHttpEnvelope({
id: "req-abc",
at: new Date().toISOString(),
payload: {
messages: [{ role: "user", content: "What's the weather?" }],
estimatedTokens: 500,
},
});npx context-kernel --config ./kernel.config.json --input ./input.jsonThe kernel classifies every input along two axes:
Input kind ΓÇö text or multimodal (detected from attachments)
Task type ΓÇö pattern-matched from message content:
| Task Type | Triggers |
|---|---|
urgent |
"urgent", "asap", "immediately" |
code |
"refactor", "typescript", "bug", "test", "build", "npm", "repo", "PR" |
memory |
"remember", "recall", "note this" |
admin |
"config", "policy", "settings", "permission" |
chat |
everything else |
Routes are resolved from your routeMap based on classification:
routeMap: {
textDefault: "local_text", // default for text chat
multimodal: "local_vision", // any input with image/audio attachments
urgent: "premium", // urgent requests escalate
codeHighContext: "premium", // code tasks over token threshold
premiumFallback: "premium", // fallback when escalation is allowed
}Premium escalation (allowPremiumEscalation) controls whether urgent and high-context code tasks can jump to premium models. Disable it to keep everything local.
router: {
tokenCompressionThreshold: 10000 // trigger compression above this
}When estimatedTokens exceeds the threshold, the decision returns compress: true with a compressionReason. Your downstream worker handles the actual compression ΓÇö the kernel just makes the call.
Three built-in guards run on every request, plus a rule DSL for custom policies:
- Post-only mode ΓÇö restricts actions to post/send when
postOnlyMode: true - Quiet hours — blocks during configured hours (supports overnight ranges like 23→7)
- Secret detection ΓÇö regex scan on message content (defaults:
api_key,password,token,secret,AKIA...)
rules: [
// Only allow specific actions
{ id: "safe-actions", kind: "action_allowlist", actions: ["send", "post", "tool_call"] },
// Block during overnight hours
{ id: "overnight", kind: "quiet_hours", startHour: 1, endHour: 6, severity: "low" },
// Catch leaked credentials
{ id: "no-aws", kind: "secret_regex", patterns: ["AKIA[0-9A-Z]{16}"], severity: "high" },
// PII detection ΓÇö block, warn, or redact
{ id: "no-pii", kind: "pii_guard", action: "block", types: ["email", "ssn"], severity: "high" },
]Every guard and rule produces a PolicyVerdict with allowed, reason, ruleId, and severity.
The kernel scans user messages for memory-worthy content and returns MemoryCandidate[]:
| Strategy | Trigger patterns | Priority | TTL |
|---|---|---|---|
preference |
"remember", "always", "never", "I prefer" | high | 365 days |
decision |
"decided", "we will", "approved" | high | 180 days |
summary |
long messages (>220 chars) | medium | 30 days |
Each candidate includes writebackHint with namespace, optional upsert key, and TTL ΓÇö ready for your memory store.
Hook into extraction:
const kernel = new ContextKernel(config, {
onMemoryCandidates: (candidates, input) => {
for (const c of candidates) {
console.log(`[${c.priority}] ${c.source.strategy}: ${c.summary}`);
}
},
});Every decision emits a lifecycle of events via the onEvent hook:
const kernel = new ContextKernel(config, {
onEvent: (event) => console.log(JSON.stringify(event)),
});Events: started → classified → guard_blocked? → compressed? → routed → completed | failed
Each event carries sessionId, timestamp, and a detail object for observability.
Scan messages for personally identifiable information with configurable actions:
import { scanForPII, scanMessages } from "context-kernel";
// Scan a single string
const result = scanForPII("Email me at john@example.com", { action: "redact" });
console.log(result.detected); // true
console.log(result.redactedText); // "Email me at [REDACTED]"
// Scan all messages
const msgResult = scanMessages(messages, { action: "warn", types: ["email", "ssn"] });Supported PII types: email, phone, ssn. Actions: redact (mask), warn (flag but allow), block (reject).
Integrates directly into the kernel as a pii_guard policy rule ΓÇö see Custom rules above.
Detect and merge near-duplicate context entries using cosine similarity on term-frequency vectors:
import { deduplicateEntries, findDuplicate } from "context-kernel";
const result = deduplicateEntries(entries, { similarityThreshold: 0.85 });
console.log(result.unique); // entries that survived
console.log(result.merged); // groups of merged duplicates
console.log(result.removedCount); // total entries removed
// Check a single entry against a corpus
const dup = findDuplicate(candidate, corpus, { similarityThreshold: 0.85 });
if (dup) console.log(`Duplicate of ${dup.match.id} (${dup.similarity})`);Rank context entries by relevance, recency, and usage frequency:
import { scoreEntries, topK } from "context-kernel";
const scored = scoreEntries(entries, "TypeScript generics", usageRecords, {
relevanceWeight: 0.5,
recencyWeight: 0.3,
frequencyWeight: 0.2,
recencyHalfLifeHours: 24,
});
// Or just get the top results
const top5 = topK(entries, "TypeScript generics", 5, usageRecords);Each ScoredEntry includes a breakdown with individual relevance, recency, and frequency scores.
Enforce bounded context stores with automatic eviction:
import { evict } from "context-kernel";
// LRU ΓÇö evict least recently used
const lruResult = evict("lru", entries, { maxEntries: 100 }, accessRecords);
// LFU ΓÇö evict least frequently used
const lfuResult = evict("lfu", entries, { maxEntries: 100 }, accessRecords);
// TTL ΓÇö evict entries older than 24 hours
const ttlResult = evict("ttl", entries, { ttlMs: 24 * 60 * 60 * 1000 });
console.log(ttlResult.retained); // entries that survived
console.log(ttlResult.evicted); // entries that were removedStructured audit logging with query API and JSONL export:
import { createAuditTrail, createAuditHook, queryAuditTrail, exportJSONL } from "context-kernel";
const trail = createAuditTrail();
const kernel = new ContextKernel(config, { onEvent: createAuditHook(trail) });
// After running decisions...
const result = queryAuditTrail(trail, {
sessionId: "sess-001",
eventTypes: ["completed", "guard_blocked"],
limit: 50,
});
// Export for log ingestion
const jsonl = exportJSONL(trail);Cross-session context sharing for multi-agent coordination:
import { createSharedMemoryRegistry, createPool, publish, readPool, subscribe } from "context-kernel";
const registry = createSharedMemoryRegistry();
createPool(registry, "workspace", 1000); // max 1000 entries
// Agent A publishes context
publish(registry, "workspace", "agent-a", { id: "e1", content: "...", timestamp: "..." });
// Agent B subscribes and reads
subscribe(registry, "workspace", "agent-b");
const entries = readPool(registry, "workspace", { excludeSessionId: "agent-b" });Save and restore full context state for session replay or branching:
import { createSnapshotStore, takeSnapshot, restoreSnapshot } from "context-kernel";
const store = createSnapshotStore();
takeSnapshot(store, {
id: "before-refactor",
sessionId: "sess-001",
entries: currentEntries,
usageRecords: currentUsage,
label: "pre-refactor checkpoint",
});
// Later, restore the snapshot
const { entries, usageRecords } = restoreSnapshot(store, "before-refactor");Efficient batch operations for context stores:
import { createContextStore, bulkInsert, bulkDelete, bulkQuery, bulkGet } from "context-kernel";
const store = createContextStore();
// Batch insert
const insertResult = bulkInsert(store, entries);
console.log(insertResult.inserted); // count of successfully inserted
console.log(insertResult.failed); // count of failures (duplicate ids, etc.)
// Batch delete
const deleteResult = bulkDelete(store, ["id-1", "id-2", "id-3"]);
// Batch get
const found = bulkGet(store, ["id-1", "id-2"]);
// Query with predicate and pagination
const queryResult = bulkQuery(store, (e) => e.content.includes("TypeScript"), {
limit: 10,
offset: 0,
});All inputs and configs are validated at runtime with Zod schemas. Import them directly:
import { kernelInputSchema, kernelConfigSchema } from "context-kernel/schemas";
const config = kernelConfigSchema.parse(rawConfig); // throws on invalid
const input = kernelInputSchema.parse(rawInput);| Option | Type | Default | Description |
|---|---|---|---|
router.tokenCompressionThreshold |
number |
10000 |
Token count that triggers compression |
router.allowPremiumEscalation |
boolean |
true |
Allow urgent/code tasks to escalate to premium models |
router.modelRegistry |
Record<string, {provider?, model}> |
ΓÇö | Named model targets (informational) |
router.routeMap |
object |
ΓÇö | Maps task classifications to model keys |
policy.postOnlyMode |
boolean |
false |
Restrict to post/send actions only |
policy.quietHours |
{startHour, endHour, timezone?} |
ΓÇö | Global quiet hours window |
policy.blockedSecretPatterns |
string[] |
common patterns | Regex patterns for secret detection |
policy.rules |
PolicyRule[] |
[] |
Custom policy rules (allowlist, quiet hours, secret regex, PII guard) |
- GitHub: github.com/darks0l/context-kernel
- Changelog:
CHANGELOG.md - Architecture:
ARCHITECTURE.md - Security:
SECURITY.md - License: MIT
Built with teeth. 🌑
