Sprinter Platform

Entity System

Schema/behavior/UI split, three authoring modes, field builders, and the entity type lifecycle.

Overview

Entities are the fundamental data unit in the Sprinter Platform. Every record -- a deal, a company, a task, a document -- is an entity with a type. Entity types are defined by three layers:

  1. Schema -- JSON Schema defining the fields and their types
  2. Behavior -- Field configs controlling extraction, dependencies, and agent assignment
  3. UI -- Optional custom components for rendering (falls back to auto-rendering)

Entity types live in the database, not in code. The defineEntityType builder is used for seeding and module definitions. At runtime, the platform reads schemas from the database and renders generically.

Three Authoring Modes

1. Code-first (modules and seeds)

Use defineEntityType and field builders in TypeScript:

import { defineEntityType, field } from "@sprinterai/core";

const company = defineEntityType({
  slug: "company",
  name: "Company",
  schema: {
    type: "object",
    properties: {
      name: field.text({ title: "Company Name" }),
      sector: field.select({
        title: "Sector",
        options: ["SaaS", "Fintech", "Healthcare"],
      }),
      revenue: field.currency({ title: "Revenue" }),
      headcount: field.integer({ title: "Headcount", minimum: 1 }),
    },
    required: ["name"],
  },
});

2. Admin UI

Create and edit entity types through the Admin panel. The schema editor provides a visual interface for adding fields, configuring extraction, and setting up scoring criteria.

3. Agent-driven

AI agents with admin permissions can create and modify entity types using the createEntityType, updateEntityTypeSchema, and updateFieldConfig tools.

Entity Type Record

The full entity type record stored in the database:

interface EntityTypeRecord {
  id: string;
  tenant_id: string;
  slug: string;
  name: string;
  name_singular: string | null;
  description: string | null;
  icon: string | null;
  color: string | null;
  json_schema: EntityJsonSchema;
  behavior_json: EntityTypeBehaviorJson | null;
  ui_json: EntityTypeUiJson | null;
  config: EntityTypeConfig;
  is_visible: boolean;
  sort_order: number;
  created_at: string;
  updated_at: string;
}

Entity Record

interface EntityRecord {
  id: string;
  tenant_id: string;
  entity_type_id: string;
  entity_type_slug: string;  // denormalized for filtering
  name: string;
  metadata: EntityMetadata;
  tags: string[];
  score: number | null;
  status: string | null;
  created_by: string | null;
  created_at: string;
  updated_at: string;
}

The metadata JSONB field stores the entity's field values, keyed by the property names from the JSON Schema.

Field Configuration

Beyond the JSON Schema property definition, each field can have extraction configuration:

interface FieldConfig {
  key: string;
  extraction?: {
    agentSlug: string;     // which agent populates this field
    prompt?: string;       // extraction instructions
    source?: ExtractionSource;
    dependsOn?: string[];  // field dependencies (DAG edges)
  };
  scoring?: {
    criteriaSetSlug: string;
    weight: number;
  };
}

This is what powers the core loop: fields declare which agent should populate them, what dependencies exist, and how they contribute to scoring.

Entity Store Interface

interface EntityStore {
  getEntityType(slugOrId: string): Promise<EntityTypeRecord | null>;
  listEntityTypes(): Promise<EntityTypeRecord[]>;
  getEntity(id: string): Promise<EntityRecord | null>;
  searchEntities(query: string, options?: {
    typeSlug?: string;
    tag?: string;
    limit?: number;
  }): Promise<EntityRecord[]>;
  createEntity(input: CreateEntityInput): Promise<EntityRecord>;
  updateEntity(id: string, input: UpdateEntityInput): Promise<EntityRecord>;
  deleteEntity(id: string): Promise<void>;
  createRelation(fromId: string, toId: string, type: string): Promise<void>;
  getEntityCounts(tenantId: string): Promise<Record<string, number>>;
}

Relations

Entities can be related to each other through typed relations:

import { relation } from "@sprinterai/core";

// In entity type config
const config = {
  relations: [
    relation("portfolio-company", {
      targetTypeSlug: "company",
      label: "Portfolio Company",
      inverse: "owned-by",
    }),
  ],
};

Value Locking

Entity fields can be locked to prevent extraction from overwriting manual edits:

// entity.metadata.lockedFields = ["revenue", "sector"]
// Locked fields are skipped during extraction workflow runs

Entity Visibility and Sharing

import { ENTITY_VISIBILITY, ENTITY_SHARE_ROLES } from "@sprinterai/core";

// Visibility: "private" | "team" | "public"
// Share roles: "viewer" | "editor" | "admin"

AI Tools for Entities

The platform provides 8 built-in entity tools:

ToolPermissionDescription
searchEntitiesentities.team.readSearch across all entity types
getEntityentities.team.readGet a single entity by ID
createEntityentities.team.createCreate a new entity
updateEntityentities.team.updateUpdate entity fields
deleteEntityentities.team.deleteDelete an entity
createRelationentities.team.updateCreate a relation between entities
listEntityTypesentities.team.readList all entity types (supports detailed: true)
getEntityStatsentities.team.readGet entity counts per type