Skip to content

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

  • contacts MongoDB collection
  • OrgManager (src/bone/org.ts)
  • add_contact and query_org skills
  • ChannelRegistry, MockChannel, ChannelStore (old in-core channels)
  • send_to_person tool
  • ❌ Channel coordinates (phone, whatsapp, email) from agent profiles

Added

  • add_person skill (identity-only profile)
  • list_people skill (scan agents with profiles)
  • Passive agent concept (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 Naveen

Agent 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 inboxes
  • loader — discover agents & profiles
  • state — access state manager
  • log — 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 World

Key Files

FilePurpose
src/plugins/plugin-api.tsPlugin interface + context types
src/plugins/plugin-host.tsLoads & dispatches to plugins
plugins/mock-channel.plugin.tsDemo plugin — logs outgoing
src/genesis/agent-loader.tsAgentProfile = identity only now
src/index.tsDispatches 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 office

Why 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.

Orbita — We don't build software. We grow organisms.