| name | hybrid-architecture |
| description | Deep knowledge of Hybrid's agent runtime architecture, including agent server, memory system, scheduler, and channel adapters. Use when working on core agent infrastructure, understanding data flow, or debugging system-level issues. |
Hybrid Architecture
Hybrid is a TypeScript agent runtime with 100% OpenClaw feature parity plus PARA memory, multi-user ACL, and channel adapters.
System Overview
HTTP • Scheduler callbacks
│
▼
┌─────────────────────────────────┐
│ Channel Adapters │
│ @hybrd/channels │
│ HTTP IPC │
└─────────────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ Agent Server │
│ hybrid/agent (port 8454) │
│ │
│ SOUL.md + AGENTS.md │
│ Memory search (vector + BM25) │
│ MCP: memory tools │
│ MCP: scheduler tools │
│ Claude Code SDK → SSE stream │
└─────────────────────────────────┘
│ │
┌───────────────┘ └───────────────┐
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────────┐
│ @hybrd/memory │ │ @hybrd/scheduler │
│ │ │ │
│ Layer 1: PARA graph │ │ cron / interval / one-time │
│ projects / areas / │ │ Precise timer, no polling │
│ resources / archives │ │ Exponential error backoff │
│ │ │ Delivers via channel adapter │
│ Layer 2: Daily log │ └──────────────────────────────┘
│ logs/YYYY-MM-DD.md │
│ │
│ Layer 3: Auto memory │
│ MEMORY.md │
│ │
│ SQLite + sqlite-vec │
│ Multi-user ACL │
└──────────────────────────┘
Packages
| Package | Purpose | Key Entry Points |
|---|
hybrid/agent | Agent runtime | src/server/index.ts |
@hybrd/memory | PARA memory | src/index.ts, src/para.ts |
@hybrd/scheduler | Time-based triggers | src/index.ts |
@hybrd/channels | Channel adapters | src/adapters/ |
@hybrd/types | Shared types | All type definitions |
@hybrd/cli | CLI commands | src/index.ts |
hybrid/gateway | CF Workers gateway | src/index.ts |
Agent Server (hybrid/agent)
HTTP API
Port 8454
POST /api/chat
Run the agent and stream a response:
interface ChatRequest {
messages: Array<{
id: string
role: "system" | "user" | "assistant"
content: string
}>
chatId: string
userId?: string
teamId?: string
systemPrompt?: string
}
Response: Server-Sent Events stream
data: {"type":"text","content":"I can help with..."}
data: {"type":"tool-call-start","toolCallId":"tc1","toolName":"memory_search"}
data: {"type":"tool-call-delta","toolCallId":"tc1","argsTextDelta":"\"query\":\""}
data: {"type":"tool-call-end","toolCallId":"tc1"}
data: {"type":"usage","inputTokens":450,"outputTokens":123,"totalCostUsd":0.0012}
data: [DONE]
GET /health
{ "status": "healthy" }
System Prompt Construction
Order of prompt assembly:
IDENTITY.md — Agent identity (name, emoji, avatar)
SOUL.md — Agent personality and core truths
- Custom system prompt (if provided in request)
AGENTS.md — Behavioral guidelines and workspace rules
TOOLS.md — Local tool and environment notes
USER.md — User profile (multi-tenant support)
- Current timestamp
- Conversation history as
<conversation_history> XML block
- Memory search results from
@hybrid/memory
MCP Tool Server
The agent runs a unified MCP server providing:
- Memory tools — Read/write PARA memory, daily logs, auto memory
- File tools — OpenClaw-compatible read/write/edit/apply_patch
File Operations Security
- All paths restricted to
./workspace/{userId}/
- Path traversal (
../) blocked
- Symlink escapes prevented
- Each user has isolated workspace
Memory System (@hybrd/memory)
3-Layer PARA Architecture
Layer 1: PARA Knowledge Graph
.hybrid/memory/life/
projects/ProjectName/
items.json ← All facts (including superseded)
summary.md ← Hot + warm facts only
areas/
resources/
archives/
Each fact has:
category: relationship | milestone | status | preference | user-signal
status: active | superseded
decayTier: hot | warm | cold
accessCount: Affects decay tier
lastAccessed: Timestamp
Decay tier computation:
accessCount >= 10 → always "warm"
accessCount >= 5 && < 14 days → "hot"
< 7 days → "hot"
< 30 days → "warm"
> 30 days → "cold"
Layer 2: Daily Log
.hybrid/memory/logs/2026-03-02.md
Append-only, timestamped entries: [FACT], [DECISION], [ACTION], [EVENT]
Layer 3: Auto Memory
MEMORY.md
## User Preferences
## Learnings
## Decisions
## Context
## Notes
Hybrid Search
Combines vector similarity (sqlite-vec) + BM25 keyword matching (FTS5):
const results = await manager.search("project deadline", {
maxResults: 10,
minScore: 0.5
})
User-Scoped Memory
.hybrid/memory/
├── MEMORY.md # Shared memory
└── users/
├── alice/
│ └── MEMORY.md # Alice's private memory
└── bob/
└── MEMORY.md # Bob's private memory
const manager = await MemoryIndexManager.get({ workspaceDir, userId })
Scheduler (@hybrd/scheduler)
Schedule Types
type CronSchedule =
| { kind: "at"; at: string }
| { kind: "every"; everyMs: number; anchorMs?: number }
| { kind: "cron"; expr: string; tz?: string; staggerMs?: number }
Precise Timer
Arms timer to exact next wake time (no polling):
private armTimer(): void {
const nextAt = this.nextWakeAtMs()
if (!nextAt) return
const delay = Math.max(0, nextAt - Date.now())
this.state.timer = setTimeout(() => this.onTimer(), delay)
}
Error Backoff
| Consecutive Failures | Delay Before Retry |
|---|
| 1 | 30 seconds |
| 2 | 60 seconds |
| 3 | 5 minutes |
| 4 | 15 minutes |
| 5+ | 1 hour |
Job Definition
interface CronJob {
id: string
name: string
enabled: boolean
deleteAfterRun?: boolean
schedule: CronSchedule
payload: CronPayload
delivery?: CronDelivery
state: CronJobState
}
type CronPayload =
| { kind: "systemEvent"; text: string }
| { kind: "agentTurn"; message: string; model?: string; ... }
MCP Tools
| Tool | Description |
|---|
schedule_task | Create scheduled task |
list_tasks | List with pagination/filtering |
cancel_task | Remove task |
get_task | Get task details |
run_task | Manually trigger |
Channel Adapters (@hybrd/channels)
Interface
interface ChannelAdapter {
channel: ChannelId
port: number
start(): Promise<void>
stop(): Promise<void>
trigger(req: TriggerRequest): Promise<TriggerResponse>
}
Default Ports
const DEFAULT_ADAPTER_PORTS = {
telegram: 8456
}
HTTP IPC
All communication uses http://127.0.0.1:{port}/api/trigger:
await dispatchToChannel({
channel: "telegram",
to: "user-123",
message: "Scheduled reminder"
})
Gateway (hybrid/gateway)
Cloudflare Workers Architecture
Cloudflare Worker (edge)
│
├── GET /health → container + server health check
└── POST /api/chat
│
▼
ensureAgentServer()
│
├── sandbox.listProcesses() (wait up to 30s)
├── Check for server
├── HTTP health check on port 8454
└── If unhealthy:
kill node processes
start server (wait for port 8454)
│
▼
sandbox.containerFetch() → port 8454
│
▼
SSE passthrough → caller
Durable Object
Each teamId gets its own Sandbox instance:
export { Sandbox } from "@cloudflare/sandbox"
Environment Variables
| Variable | Required | Description |
|---|
ANTHROPIC_API_KEY | Either | For Claude direct |
OPENROUTER_API_KEY | Either | Auto-configures Anthropic client |
Types (@hybrd/types)
All shared type definitions. Zero runtime except BehaviorRegistryImpl.
Key Types
interface Agent<TRuntimeExtension, TPluginContext> { ... }
interface AgentConfig<TRuntimeExtension> { ... }
interface Tool<TInput, TOutput, TRuntimeExtension> { ... }
interface Plugin<T> { name, description?, apply(app, context) }
interface BehaviorObject<TRuntimeExtension> {
id: string
before?(context): Promise<void>
after?(context): Promise<void>
}
interface AgentRuntime {
scheduler?: unknown
}
interface ChannelAdapter { ... }
interface TriggerRequest { to, message, metadata? }
interface TriggerResponse { delivered, messageId?, error? }
type CronSchedule = { kind: "at" | "every" | "cron", ... }
interface CronJob { ... }
CLI (@hybrd/cli)
Commands
hybrid init [name]
hybrid build [--target]
hybrid dev
hybrid deploy [platform]
hybrid register
hybrid install <source>
hybrid uninstall <name>
hybrid skills
Build Pipeline
hybrid build
│
├── pnpm --filter hybrid/agent build
├── Create .hybrid/
├── Copy dist/ → .hybrid/dist/
├── Copy SOUL.md, AGENTS.md, agent.ts
├── Copy core skills → .hybrid/skills/core/
├── Copy ./skills/ → .hybrid/skills/ext/
├── Write skills_lock.json
└── Generate: package.json, Dockerfile, fly.toml, start.sh
Deployment Targets
Sprites (default)
hybrid deploy sprites
E2B
hybrid deploy e2b
Northflank
hybrid deploy northflank
Node.js (self-hosted)
hybrid build
Key Patterns
Behavior Chain
Behaviors implement middleware pattern:
const myBehavior = (): BehaviorObject => ({
id: "my-behavior",
async before(context) {
},
async after(context) {
}
})
Plugin System
Plugins extend the Hono HTTP server:
interface Plugin<T> {
name: string
apply: (app: Hono, context: T) => void | Promise<void>
}
agent.use(myPlugin())
Conversation History
Channel adapters fetch recent messages and build conversation context before forwarding to the agent server.
Environment Variables
Agent Server
| Variable | Description |
|---|
ANTHROPIC_API_KEY | Anthropic API key |
ANTHROPIC_BASE_URL | Override base URL (auto-set for OpenRouter) |
OPENROUTER_API_KEY | OpenRouter key (auto-configures) |
PROJECT_ROOT | Override workspace root |
PORT | Agent server port (default: 8454) |
Memory
| Variable | Default | Description |
|---|
MEMORY_ENABLED | true | Enable memory |
MEMORY_PROVIDER | auto | Embedding provider |
MEMORY_MODEL | Provider default | Embedding model |
MEMORY_DB_PATH | ~/.hybrid/memory/{agentId}.sqlite | SQLite path |
Scheduler
| Variable | Default | Description |
|---|
SCHEDULER_ENABLED | true | Enable scheduler |
SCHEDULER_DB_PATH | ./data/scheduler.db | SQLite path |
SCHEDULER_TIMEZONE | System TZ | Default timezone |
Troubleshooting
Memory Search Not Working
- Check embedding provider is configured
- Run
manager.sync({ force: true }) to rebuild index
- Check
MEMORY_ENABLED=true
- Verify
MEMORY_DB_PATH is writable
Scheduler Jobs Not Running
- Check
SCHEDULER_ENABLED=true
- Verify SQLite database exists
- Check job
enabled: true
- Look for errors in job state
Build Failures
- Run
pnpm build:packages first
- Check TypeScript errors with
pnpm typecheck
- Verify all dependencies installed
- Check Biome lint with
pnpm lint