An intelligent automation platform that converts natural language commands into scheduled, multi-step agent workflows. Users describe business processes in plain English — e.g., "read my daily emails, find receipts, and upload them to Google Drive" — and the platform orchestrates execution across integrated services.
NestJS microservices (TypeScript) and Python services communicate via NATS JetStream, backed by PostgreSQL (Prisma ORM) and deployed via Docker Compose (Kubernetes-ready with namespace-per-customer isolation).
TypeScript Services: api-gateway (REST entry point), websocket-service (real-time event streaming), connection-registry (Nango OAuth), scheduler-service (K8s CronJob management), onboarding-service (org lifecycle).
Python Services: agent-builder-py (LLM planner via Claude/GPT + LangGraph), agent-runtime-py (step execution with OAuth pause/resume).
Libraries (libs/): shared-types, nats-client, prisma-client, auth, observability (OpenTelemetry + pino), integration-provider (proxy action registry + declarative config interpreter + template loader).
Shared Python (services/shared-py/): SQLAlchemy models, NATS event helpers, Nango client with declarative config interpreter.
Frontend: Next.js admin dashboard at frontend/web/. Example chat app at examples/chat-app/ demonstrating real-time agentic workflows with OAuth cards and step-by-step feedback.
- Events flow through NATS subjects defined in
libs/shared-types/src/events/subjects.ts - Proxy actions map integration API calls fully declaratively via database-driven configs — no hardcoded transformer functions
- Connection metadata (displayName, logoUrl) comes from
AvailableIntegrationrecords — never hardcode provider names providerConfigKeyis the standardized field name across the entire platform for identifying integration provider configurations (e.g.,google-drive-4,google-mail). All models use this name:AvailableIntegration.providerConfigKey,ConnectionRef.providerConfigKey,ToolRegistryEntry.providerConfigKey,ProxyActionDefinition.providerConfigKey
All third-party API interactions are configured declaratively through ProxyActionDefinition rows in the database. No transformer functions or hardcoded API attribute names — everything is driven by JSON config.
inputSchema— JSON Schema defining accepted input parameters. Extended properties (mapTo,aliases,default,wrapArray) auto-generate param/body mappings. Fields referenced byqueryBuilder.whenorbodyConfig.templateare auto-enriched into the schema so the LLM planner discovers them.outputSchema— JSON Schema defining the output format. Acts as a filter: only fields listed inoutputSchema.propertiesare returned. Also drivesfieldsParamto request only needed fields from the API.paramsConfig— Query parameter configuration:defaults: static params always sentmappings: auto-generated frominputSchemaextended fieldsqueryBuilder: builds composite query strings (e.g.,q=name contains 'X' and trashed=false)fieldsParam: auto-derives API field selection fromoutputSchema(e.g.,fields=files(id,name,mimeType))
bodyConfig— Request body configuration:defaults,template,mappings(withwrapArraysupport)mimeEncode: true— builds RFC2822 MIME messages for email sending
headersConfig— Static HTTP headers (e.g.,Notion-Version)responseConfig— Response mapping:rootPath: extract from nested response (e.g.,"files","messages")pick,flatten: standard list filteringextractHeaders: extract from key-value arrays (e.g., Gmail'spayload.headers)mergeFields: copy top-level fieldsbodyExtract: extract body from nested MIME parts with fallbackattachmentExtract: extract attachment metadata from parts
postProcessConfig— Post-processing with enrichment:enrichment.endpoint: fetch additional data per item (with{{field}}substitution)enrichment.merge: fields to copy from enrichment responseenrichment.extractHeaders: extract from key-value arrays in enrichment response
Proxy action templates live as JSON files in templates/proxy-actions/ (one file per integration type, e.g., google-drive.json, slack.json). Templates are not auto-imported during tool sync. Instead, admins explicitly import templates via the "Import Proxy Tools" flow on the Tools page, which:
- Shows available templates from the JSON files
- Lets the admin review all actions before importing
- Requires selecting an
AvailableIntegrationto assign theproviderConfigKey - Creates
ProxyActionDefinitionrows on approval
The TemplateLoaderService (libs/integration-provider/src/proxy/template-loader.service.ts) reads template files at runtime. Template directory is configurable via TEMPLATE_DIR env var (defaults to templates/proxy-actions/ relative to process.cwd()).
Two parallel interpreters convert DB rows into executable configs:
- TypeScript:
libs/integration-provider/src/proxy/declarative-config-interpreter.ts(used by api-gateway) - Python:
services/shared-py/shared/nango_client.py(used by agent-runtime-py, agent-builder-py)
Both must be kept in sync when adding new declarative config features.
- Never hardcode API attribute names (e.g.,
threadId,subject,snippet). All field mapping is declarative viaextractHeaders,mergeFields,bodyExtract,attachmentExtract. - No transformer functions — the
transformerNameDB column exists but is unused. All logic is expressed through declarative config. outputSchemafilters results — editingoutputSchemato remove a field means that field is no longer returned.inputSchemais the single source of truth for parameter definitions. UsemapTo,aliases,default,wrapArrayin schema properties instead of separateparamsConfig.mappingsorbodyConfig.mappings.- Do not auto-deploy — provide the build/deploy command for the user to run manually.
docker compose build <services> && docker compose up -dServices to rebuild depend on what changed:
shared-py+agent-runtime+agent-builder— Python services or shared-py changesapi-gateway— TypeScript interpreter, proxy-actions service, or integration-provider lib changesfrontend— UI changeswebsocket-service— WebSocket handler changes- Prisma migrations run from
libs/prisma-clientdirectory npm installfrom root for monorepo deps