Create AI agents, configure how they behave (personality, tools, memory, schedules, limits), wire them into collaborative multi-agent workflows on a visual canvas, and run them on a real actor/message-bus runtime. Agents execute real tools, hand off to each other asynchronously, and one agent (the Concierge) is reachable from Slack so a human can drive everything conversationally. Everything runs fully local with one command.
Domain framing: payments — the demo agents do real analytical work over a seeded payments dataset with a planted anomaly, so every run discovers a genuine story.
Deeper docs:
ARCHITECTURE.md(decisions + diagram) · This README is the entry point: what it is, how to run it.
- Configurable agents — 11 real dimensions per agent (prompt/personality, model, tools, skills, memory, interaction rules, guardrails, channels, schedules). See below.
- Visual workflow builder — a React Flow canvas to create/edit/delete workflows: drag agent nodes, connect them, add conditional branches and feedback loops.
- Real runtime — a custom actor/message-bus on Redis + BullMQ. Agents run real tool-calling loops; a conductor walks the workflow graph; a scheduler fires cron workflows.
- Real tools — read-only SQL over the payments DB, web search (Exa), Slack posting, sub-workflow launching, memory updates, and more.
- Slack integration — the Concierge agent is reachable via Slack (Socket Mode, no public URL), converses, remembers your preferences, and launches workflows for you.
- Live monitoring — an SSE-fed dashboard of the event stream, inter-agent messages, and per-run token/cost, plus full persisted history.
- One-command local —
docker compose upbrings up the whole stack and seeds a demo-ready dataset (7 agents, 2 workflows, a schedule, 5,502 transactions).
| Layer | Choice |
|---|---|
| Language | TypeScript end-to-end, pnpm monorepo |
| Runtime | Custom actor/message-bus on Redis + BullMQ (also drives schedules) |
| API | Fastify — REST for control, SSE for live monitoring |
| Persistence | Postgres + Prisma |
| LLM access | Vercel AI SDK via OpenRouter (one key → Claude + OpenAI + more) |
| Messaging channel | Slack @slack/bolt Socket Mode (behind a Channel interface) |
| Web | Vite + React + Tailwind + shadcn/ui + React Flow |
| Observability | OpenTelemetry traces → Jaeger |
See ARCHITECTURE.md for the full ADR table and rationale.
- Docker + Docker Compose
- An OpenRouter API key (one key serves every model). Optional: an Exa key for web search, and Slack tokens for the live chat demo.
cp .env.example .envEdit .env and set at least:
OPENROUTER_API_KEY=sk-or-... # required — serves Claude + OpenAI + more
EXA_API_KEY=... # optional — enables the web_search tool
SLACK_BOT_TOKEN=xoxb-... # optional — enables the Slack demo (see below)
SLACK_APP_TOKEN=xapp-... # optionalPrefer calling providers directly? Leave
OPENROUTER_API_KEYblank and setANTHROPIC_API_KEY/OPENAI_API_KEYinstead.
docker compose upA one-shot migrate service applies migrations + seeds the demo data, then gates the app
services. When it's up:
| Service | URL |
|---|---|
| Web UI | http://localhost:5173 |
| API (REST + SSE) | http://localhost:4000 |
| Worker health | http://localhost:4100/health |
| Jaeger (traces) | http://localhost:16686 |
Open the web UI, go to Workflows → Payments Analytics Assistant → Run, and ask "Why did Stripe's authorization rate drop recently?" — then watch it on the Monitor tab.
The Concierge uses Socket Mode, so there's no public URL / ngrok — your machine opens an outbound WebSocket to Slack. At https://api.slack.com/apps:
- Create New App → From scratch, pick your workspace.
- Socket Mode → Enable → generate an App-Level Token with scope
connections:write. Thatxapp-…value isSLACK_APP_TOKEN. - OAuth & Permissions → Bot Token Scopes: add
chat:write,im:history,channels:history. - Event Subscriptions → Enable, then Subscribe to bot events:
message.im,message.channels. (No Request URL needed — Socket Mode handles it.) - Install App → Install to Workspace → copy the Bot User OAuth Token (
xoxb-…) asSLACK_BOT_TOKEN. - Put both tokens in
.env, restart the worker (docker compose restart workerafter the firstup, or justdocker compose up), then DM the bot.
On success the worker logs Slack channel started (Socket Mode).
Each agent exposes 11 dimensions (editable in the UI):
| Dimension | Notes |
|---|---|
name, role |
identity descriptors |
systemPrompt (SOUL) |
personality / instructions |
model |
provider + model id (via OpenRouter) |
tools |
subset of the tool registry composed into the LLM call |
channels |
only the Concierge is bound to slack |
schedules |
cron specs → BullMQ repeatable jobs |
memory |
{ enabled, doc (markdown), windowSize } — agent- and human-writable |
skills |
markdown modules, always injected |
interactionRules |
{ allowedRecipients[], maxTurns, canInitiate } — allowlist enforced in builder + bus |
guardrails |
{ maxIterations, maxCostUSD, timeoutMs } — runtime-enforced; trips surface in monitoring |
sql_query (read-only, schema-scoped) · web_search (Exa) · post_to_slack · run_workflow
(launch a sub-workflow) · update_memory · calculator · current_time.
- Payments Analytics Assistant —
Analyst → Investigator → Reporter → Reviewerwith a Reviewer quality-gate feedback loop. The deliverable is the Reporter's brief. - Scheduled Payments Health Monitor —
Monitorchecks metrics; branches toTriage(Slack alert) on an anomaly, or ends quietly. Also wired to a daily cron schedule.
The whole stack runs in Docker, but you can also run apps directly:
pnpm install
docker compose up -d postgres redis # infra only
pnpm dev # run apps in dev (hot reload)LLM calls are mocked (deterministic, free) via an injectable model seam — tests never hit a real provider.
pnpm test # unit (fast, no infra)
pnpm test:integration # needs Postgres + Redis (uses a dedicated yuno_test DB)
pnpm test:all # bothpnpm db:migrate # apply migrations
pnpm db:seed # (re)apply the demo seed — idempotent
pnpm db:generate # regenerate the Prisma clientapps/web Vite/React SPA — agent CRUD, React Flow builder, live monitoring
apps/server Fastify — REST control + SSE monitoring stream
apps/worker the runtime — BullMQ workers, conductor, scheduler, Slack
packages/shared TS types + Prisma client + demo seed (imported by server & worker)
packages/observability OpenTelemetry tracing + structured logging
packages/testkit test DB provisioning + reset helpers
Strict 3-layer separation: UI ⟂ runtime ⟂ persistence — no layer reaches across another's boundary.