# Build-knowledge — the Leviathan programmability layer

- A Rule (db('rules')) wires { trigger: { type, props }, actions: [{ type, props }] } with a runtime: server rules arm in the API on boot (re-arm: POST /api/automation/rearm); client rules arm in the...


> How Triggers · Actions · Chatbots · Functioncalls · Toolbanks · Providers · the Capability SDK fit
> together, and how an agent extends each one. This is the build-knowledge entry for the layer that
> makes Leviathan programmable from the frontend and Studio.

## The mental model

```
TRIGGER (fires) ──rule──-> ACTION(s) (do) ← @leumas/triggers + @leumas/actions (+ rule engine)
CHATBOT = persona + provider + voice + TOOLBANKS of FUNCTIONCALLS   ← @leumas/chatbots
PROVIDER = the LLM brain (ollama/openai/claude/gemini/realtime/…)   ← @leumas/providers
CAPABILITY = a live functioncall a PAGE registers at runtime         ← @leumas/capability-sdk
```

- A **Rule** (`db('rules')`) wires `{ trigger: { type, props }, actions: [{ type, props }] }` with a
  `runtime`: `server` rules arm in the API on boot (re-arm: `POST /api/automation/rearm`); `client`
  rules arm in the browser (capability-sdk `TriggerRuntime`). Server rules reach the browser through
  the SSE stream `GET /api/automation/stream` as `directive` events.
- A **Chatbot** (`db('chatbots')`) resolves its `provider` ('auto' → first available) and its tools
  (toolbanks ∪ functioncalls ∪ the live client capabilities sent with the request) and runs the
  tool loop (`@leumas/chatbots` `runProgrammedTurn`): server tools execute in the API; client tools
  pause the turn (`clientToolCalls` + `turnId`) and resume with `toolResults`.
- A **Functioncall** (`db('functioncalls')`) is a named tool `{ parameters: JSON-Schema, executor }`;
  `executor.kind` = `adapter` ("system.fn") · `http` · `grid-cell` · `action` · `client`.
- A **Toolbank** (`db('toolbanks')`) is a named group of functioncall names assigned to bots.

## Where everything lives

| Piece | Path | Contract |
|---|---|---|
| Doc schemas + validators | `shared/packages/schemas/src/programmability.js` | zod; `defineFunctioncall/…` + `validate*` |
| Trigger engine | `shared/engines/triggers` | `defineTrigger({ id, metadata, arm(onFire, props) → disarm })`; seeds in `seeds/` |
| Action engine + rules | `shared/engines/actions` | `defineAction({ id, metadata, inputs, run(props) })`; `createRuleEngine({ triggers, actions, onEvent })` |
| Chatbots engine | `shared/engines/chatbots` | `runProgrammedTurn`, `bindExecutor`, `createTurnStore`; seeds (bots/banks/functioncalls) in `seeds/` |
| Providers | `shared/engines/adapters/providers` | `defineProvider({ id, capabilities, available, adapter | complete, realtimeSession })`; registry `resolve(bot)` |
| Capability SDK (frontend) | `shared/packages/capability-sdk` | `CapabilityProvider` + `useRegisterCapability(caps, { scopeId })` + `TriggerRuntime` + `createClientActionRegistry` + `useRealtimeVoice` + shared `bus` |
| Leviathan widget | `shared/packages/leviathan` | picks up capabilities, executes `clientToolCalls`, voice modes |
| Leviathan runtime | `shared/engines/leviathan` | `/leviathan/chat` (tool loop), `/leviathan/realtime/session` (SDP, key stays server-side) |
| Studio admin | `products/leumas-studio/src/admin/{Chatbots,Triggers,ActionsManager,ToolbanksManager,FunctionCallsManager}.jsx` | FunctionCallsManager anatomy; demos in `admin/demos/` |
| MCP exposure | `shared/engines/mcp/mcp-servers/leumas-automation` | thin HTTP adapters over `/api/automation/*` |
| EXE distribution | `ops/tools/exe-builder` | builds Studio dist + packages the API |

## How to add one of each (the agent recipes)

- **New server trigger**: add `shared/engines/triggers/src/adapters/<id>.js` exporting a
  `defineTrigger` factory with `arm(onFire, props)`, register it in `src/index.js`. It appears in
  `GET /api/automation/triggers` automatically.
- **New client trigger**: implement `{ id, arm(onFire, props) → disarm }` in
  `capability-sdk/src/triggers/adapters.js` (`CLIENT_TRIGGER_ADAPTERS`), and add the matching manifest
  to `shared/engines/triggers/src/manifests.js` so Studio lists it.
- **New action**: same split — server: `actions/src/adapters/*` + register; client:
  `capability-sdk/src/actions/registry.js` + `actions/src/manifests.js`.
- **New provider**: folder under `shared/engines/adapters/providers/src/<id>/` exporting
  `provider = defineProvider({...})` (lazy SDK imports, cheap `available()`), then add it to the
  barrel `src/index.js`. `GET /api/providers` and Studio's Chatbots picker list it automatically.
- **New page capability**: inside any React page under the `CapabilityProvider`:
  `useRegisterCapability([{ name, description, parameters, call }], { scopeId, pageTitle })` —
  registers on mount, unregisters on unmount. `simulateHear('<phrase>')` tests wakeword rules mic-free.
- **New seeds**: add records to the owning engine's `seeds/` (data-only ESM) — hydration runs on API
  boot via `hydrateCollection` (idempotent; `userModified: true` docs are never clobbered).

## Catalogs shipped (hydration)

`triggers_catalog` + `actions_catalog` hold the not-yet-implemented manifests (`implemented: false`,
greyed out in Studio) so the CRM shows the full roadmap surface. Live counts: see
`GET /api/automation/triggers|actions`, `GET /api/providers`, and `/index/capabilities|chatbots|automations`
(the registry entries that make functioncalls/toolbanks/bots/rules searchable and priceable in the CRM).


---
Source: shared/services/knowledge/build-knowledge/programmability.md
Canonical: https://docs.leumas.tech/p/how-to/programmability
