Agentic Application Framework

Build AI-powered applications with model-agnostic architecture, event-driven messaging, and zero vendor lock.

Quick Start

npm install @happyvertical/smrt-core

import { SmrtObject, smrt } from '@happyvertical/smrt-core';

@smrt()
class Task extends SmrtObject {
  title: string = '';
  completed: boolean = false;
}

const tasks = await TaskCollection.create({ db: 'tasks.db' });
const task = await tasks.create({ title: 'Learn SMRT' });

Core Framework

Everything you need to build, deploy, and scale business logic with AI.

Simple

Extend SmrtObject. That's it.

class Product extends SmrtObject {
  name: string = '';
  price: number = 0.0;    // DECIMAL (inferred from decimal)
  quantity: number = 0;   // INTEGER (inferred from integer)
  categoryId = foreignKey(Category);
}

// That's it. You get:
// - Database schema with migrations
// - REST API endpoints
// - MCP tools for AI agents
// - CLI: smrt product create --name "Widget" --price 9.99
// - AI-powered methods: is(), do(), describe()

Advanced

Configure API, MCP, and CLI generation per class.

@smrt({
  api: { include: ['list', 'get', 'create'] },
  mcp: true,
  cli: true,
  tableStrategy: 'sti',
  conflictColumns: ['invoiceNumber'],
  hooks: {
    beforeSave: async (obj) => { /* validation */ },
    afterSave: async (obj) => { /* side effects */ }
  }
})
class Invoice extends SmrtObject {
  invoiceNumber: string = '';
  amount: number = 0.0;       // DECIMAL (from 0.0)
  status: string = 'draft';
  customerId = foreignKey(Customer);

  // AI-powered operations
  // await invoice.is("Is this invoice overdue?")
  // await invoice.do("Generate a payment reminder email")
}

Objects

AI-powered data models with built-in persistence, validation, and intelligence.

AI-Powered Methods

Validate, transform, and describe content with AI.

// Validate content with AI
const isValid = await article.is("Is this article well-structured?");

// Transform content with AI
const summary = await article.do("Summarize in 3 bullet points");

// Generate descriptions automatically
const description = await product.describe();

Vector Embeddings

Semantic representations for similarity search.

// Generate embeddings for configured fields
await article.generateEmbeddings();

// Check if content changed since last embedding
if (await article.hasStaleEmbeddings()) {
  await article.generateEmbeddings();
}

// Get embedding vector for a field
const vector = await article.getEmbedding('content');

Object Memory

Learn patterns, recall them across sessions.

// Remember learned patterns with confidence scores
await scraper.remember({
  scope: 'parser/example.com',
  key: 'content-selector',
  value: '.article-body',
  confidence: 0.95
});

// Recall with hierarchical fallback
const selector = await scraper.recall({
  scope: 'parser/example.com',
  key: 'content-selector',
  includeAncestors: true
});

Collections

Query, search, and manage objects with powerful collection operations.

Semantic Search

Find by meaning, not keywords.

// Search by text similarity
const results = await articles.semanticSearch('machine learning basics', {
  field: 'content',
  limit: 10,
  minSimilarity: 0.7
});

// Find similar to an existing object
const related = await articles.findSimilar(article, { limit: 5 });

Batch Operations

Efficient multi-record operations.

// Batch fetch by IDs (single query)
const items = await collection.listByIds(['id1', 'id2', 'id3']);

// Find or create with defaults
const user = await users.getOrUpsert(
  { email: 'user@example.com' },
  { name: 'New User', role: 'member' }
);

Agents

Autonomous actors with full object capabilities and inter-agent communication.

Inter-Agent Dispatch

Async, persistent messaging between agents.

// One agent notifies others
await dispatch.emit('task.completed', {
  taskId: 'task-123',
  result: { items: 42 }
});

// Another agent subscribes with wildcards
await dispatch.subscribe({
  signalType: 'task.*',
  subscriber: 'Roboto'
});

// Process incoming messages (survives restarts)
await dispatch.process('Roboto', async (payload, meta) => {
  await this.handleResult(payload.taskId, payload.result);
});