Appearance
Stage 4b: Pure Core + Plugin Bridges 🔌
The core is pure. External channels become plugins. Humans become passive agents. Identity lives in agent.profile; channel coordinates live in plugins.
Status: ✅ Complete Tests: 74/74 passing Philosophy: The core doesn't know the outside world exists.
The Paradigm
In a pure, first-principles agentic system:
"Anything with identity + inbox + behavior is an agent. Anything else is data."
A human being:
- Has identity ✓
- Can receive messages (via WhatsApp, email, SMS) ✓
- Has behavior (they respond) ✓
Therefore a human is an agent. One human = one agent folder. Every real person in the organism has a passive agent representing them.
But the agent itself doesn't know its own phone number. It just knows its own name and language. Channel coordinates belong to plugins, not the core.
What Changed
Removed
- ❌
contactsMongoDB collection - ❌
OrgManager(src/bone/org.ts) - ❌
add_contactandquery_orgskills - ❌
ChannelRegistry,MockChannel,ChannelStore(old in-core channels) - ❌
send_to_persontool - ❌ Channel coordinates (phone, whatsapp, email) from agent profiles
Added
- ✅
add_personskill (identity-only profile) - ✅
list_peopleskill (scan agents with profiles) - ✅
Passive agentconcept (no LLM, plugins handle their inbox) - ✅ Plugin API (
src/plugins/) - ✅ Plugin Host (loads & dispatches)
- ✅
plugins/directory (tenant-owned, plugin files) - ✅ Mock channel plugin (
plugins/mock-channel.plugin.ts)
The New Flow
Naveen types: "Tell Radha to clean office"
↓
Axiom processes: delegates, puts message in radha's inbox
↓
Heartbeat picks up: radha has 1 inbox message
↓
Core checks: radha is passive → no LLM call
↓
Plugin dispatch: all plugins watching "radha" receive the message
↓
mock-channel plugin: formats & logs ("📡 [mock] → ...")
(or whatsapp plugin): sends real WhatsApp via Meta API
↓
Real Radha replies: plugin's webhook receives it
↓
Plugin writes back: inbox.send(from: "channels", to: "radha", ...)
↓
Core: next heartbeat picks it up → delegates reply back to NaveenAgent Profile (Identity Only)
typescript
interface AgentProfile {
displayName?: string; // "Radha"
role?: string; // "Housekeeping"
language?: string; // "te"
reportsTo?: string | null; // "naveen"
isLeader?: boolean;
// NO phone, whatsapp, email — those are plugin concerns
}Plugin API
A plugin is a file exporting createPlugin(ctx): Plugin:
typescript
export const createPlugin: PluginFactory = (ctx) => ({
name: "my-channel",
description: "What I do",
watches: ["radha", "agil"], // or "*" for all
async start() { /* connect to external service */ },
async onOutgoing(message) {
// message = InboxMessage to one of my watched agents
// format it for my channel, send externally
},
async stop() { /* cleanup */ },
});Plugins get:
inbox— read/write agent inboxesloader— discover agents & profilesstate— access state managerlog— structured logging
Plugins maintain their own contact book. The mock-channel plugin has its own internal mapping radha → mock:radha. A real WhatsApp plugin would have radha → +91....
Architectural Purity
┌─────────────────────────────────────────────────┐
│ CORE (pure) │
│ Agents. Inboxes. Skills. Tasks. Data. │
│ Zero knowledge of phones, emails, WhatsApp. │
└────────────────────┬────────────────────────────┘
│ plugins subscribe to inboxes
│ plugins write to inboxes
▼
┌─────────────────────────────────────────────────┐
│ PLUGINS (outside) │
│ mock-channel, whatsapp, email, telegram... │
│ Each maintains its own contact book. │
│ Each formats messages for its channel. │
└────────────────────┬────────────────────────────┘
│ external APIs
▼
Real WorldKey Files
| File | Purpose |
|---|---|
src/plugins/plugin-api.ts | Plugin interface + context types |
src/plugins/plugin-host.ts | Loads & dispatches to plugins |
plugins/mock-channel.plugin.ts | Demo plugin — logs outgoing |
src/genesis/agent-loader.ts | AgentProfile = identity only now |
src/index.ts | Dispatches inbox messages to plugins |
Try It
bash
npm start
# You should see at startup:
# 📡 mock-channel ready (watching all agent inboxes)
# 🔌 Plugin loaded: mock-channel (watches: *)
# In /talk, tell Axiom:
# "Add Radha, passive agent, Telugu speaker, role housekeeping"
# "Send a message to radha's inbox asking to clean the office"
# Console shows:
# 📡 [mock] → mock:radha (te): clean the officeWhy This Matters
1. The core is simple. It's agents + inboxes + skills. Forever.
2. Real channels are swappable. Add a WhatsApp plugin → organism talks on WhatsApp. Remove it → it doesn't. Core unchanged.
3. Formatting is localized. WhatsApp prefers certain markdown; Telegram another. Each plugin handles its own formatting.
4. Testing is easy. No plugins = fully testable internal mesh. Add mock plugin = local testing. Add real plugin = production.
5. Privacy and compliance. Contact coordinates (phone, email) are stored by plugins that own those concerns. The core doesn't leak them.