logicspike/docs

AI Brain

Phase 3: Domain Model — AI Brain

Last Updated: 2026-04-03 Status: Draft


1. Core Entities & Aggregates

1.1 Agent Domain

1.1.1 AgentConfig

Defines a specialist agent's identity and behavior. One per domain per tenant (blog specialist, content specialist, etc.), plus a shared router config.

Field Type Required Description
id UUID Primary key
tenant_id UUID Owning tenant
agent_type Enum router, blog, content, analytics, chat
display_name String Human-readable name (e.g., "Blog Assistant")
system_prompt Text Domain-specific system prompt
model_tier Enum haiku, sonnet, opus — default model for this agent
temperature Float Creativity (0.0–1.0), default 0.3 for most, 0.7 for content generation
max_tokens Integer Max response tokens, default 1000
tools Text[] List of tool names this agent can call
enabled Boolean Whether this specialist is active
created_at Timestamp Record creation
updated_at Timestamp Last modification

1.1.2 ToolDefinition

A registered tool that agents can call. Maps to a service action.

Field Type Required Description
name String Unique identifier (e.g., blog.create_draft)
service Enum blog, content, chat, media, analytics, billing
description Text What this tool does — used by the LLM to decide when to call it
parameters JSONB JSON Schema of accepted parameters
requires_confirmation Boolean If true, user must approve before execution (destructive actions)
required_permission String PBAC permission needed (e.g., blog.write)
rate_limit Integer Max calls per minute per tenant (null = unlimited)

1.2 Conversation Domain

1.2.1 Conversation

A conversation thread between a user and the AI copilot.

Field Type Required Description
id UUID Primary key
tenant_id UUID Owning tenant
user_id UUID The user who started this conversation
title String Auto-generated summary of the conversation topic
status Enum active, idle, closed
context_snapshot JSONB Frozen context at conversation start (tenant profile, plan)
total_tokens Integer Cumulative tokens used across all messages
total_cost_usd Float Cumulative cost in USD
message_count Integer Total messages in this conversation
started_at Timestamp When conversation began
last_message_at Timestamp Last activity timestamp
closed_at Timestamp When conversation was closed

1.2.2 Message

An individual message within a conversation.

Field Type Required Description
id UUID Primary key
conversation_id UUID FK → Conversation
role Enum user, assistant, tool_call, tool_result, system
content Text Message text (or tool call/result JSON)
agent_type Enum Which specialist generated this (null for user messages)
model String Model used (e.g., claude-haiku-4-5-20251001)
input_tokens Integer Tokens consumed for input
output_tokens Integer Tokens generated for output
tool_calls JSONB Array of tool calls made in this turn
latency_ms Integer Time to first token in milliseconds
created_at Timestamp When this message was created

1.3 Memory Domain

1.3.1 Memory

A stored piece of knowledge about a tenant, linked to entities.

Field Type Required Description
id UUID Primary key
tenant_id UUID Owning tenant — NEVER cross-tenant
memory_type Enum fact, preference, episode, pattern, entity
content Text The memory itself (natural language)
embedding vector(1536) Semantic embedding for similarity search
importance Float 0.0–1.0, boosted by outcomes, decayed by time
access_count Integer How many times this memory has been retrieved
decay_rate Float How fast importance decays (default 0.01 per day)
entity_ids Text[] Linked entities (e.g., ["blog", "traffic", "recipe"])
source_type Enum conversation, observation, consolidation, insight
source_id UUID FK to conversation or insight that created this
expires_at Timestamp Auto-pruned after this date (null = permanent)
accessed_at Timestamp Last time this memory was retrieved
created_at Timestamp Record creation

1.3.2 MemoryEntity

An entity node in the memory graph. Represents a concept the AI tracks.

Field Type Required Description
id String Entity identifier (e.g., blog, traffic, priya, diwali-post)
tenant_id UUID Owning tenant
entity_type Enum service, topic, person, content, campaign, metric
display_name String Human-readable name
metadata JSONB Arbitrary structured data about this entity
memory_count Integer Number of memories linked to this entity
created_at Timestamp When this entity was first recognized
updated_at Timestamp Last modification

1.4 Decision Domain

1.4.1 Decision

A logged AI decision — what the agent did, why, and what happened.

Field Type Required Description
id UUID Primary key
tenant_id UUID Owning tenant
conversation_id UUID FK → Conversation (null for proactive insights)
agent_type Enum Which specialist made this decision
action String What the agent decided to do (e.g., diagnosed_traffic_drop)
reasoning Text Chain of thought — why this action
tool_calls JSONB Tools called as part of this decision
outcome JSONB Tracked result (populated later by outcome tracker)
feedback_score Float -1.0 to 1.0, computed from outcome
outcome_tracked_at Timestamp When outcome was measured
created_at Timestamp When the decision was made

1.5 Insight Domain

1.5.1 Insight

A proactive insight generated by the insight engine.

Field Type Required Description
id UUID Primary key
tenant_id UUID Owning tenant
insight_type Enum opportunity, anomaly, win, suggestion, reminder
priority Enum high, medium, low
title String Short headline (shown in dashboard card)
body Text Detailed explanation
suggested_action String Tool name to execute if user clicks CTA
action_params JSONB Parameters for the suggested action
seen_at Timestamp When user viewed this insight (null = unseen)
acted_on Boolean Whether user clicked the CTA
dismissed_at Timestamp When user dismissed this insight
expires_at Timestamp Insights are time-sensitive — auto-hide after this date
created_at Timestamp When the insight was generated

1.6 Usage Domain

1.6.1 UsageRecord

Per-interaction cost tracking for the cost governor.

Field Type Required Description
id UUID Primary key
tenant_id UUID Owning tenant
conversation_id UUID FK → Conversation
message_id UUID FK → Message
model String Model used (e.g., claude-sonnet-4-6)
input_tokens Integer Tokens sent to LLM
output_tokens Integer Tokens received from LLM
cost_usd Float Computed cost for this call
billing_period String YYYY-MM format for monthly aggregation
created_at Timestamp When the LLM call was made

2. Entity Relationships


3. State Machines in Detail

3.1 Conversation.status Lifecycle

3.2 Memory.importance Lifecycle

3.3 Insight.status Lifecycle


4. Key Domain Rules & Invariants

  1. Tenant isolation is non-negotiable. Every entity has a tenant_id. Every query filters by it. Row-Level Security is the safety net, not the primary mechanism — application code MUST scope all queries. A bug that leaks cross-tenant data is a P0 security incident.

  2. Memories are never deleted by agents. Only the periodic consolidation cron can prune memories (when importance drops below 0.1). Agents can create memories and boost importance, but never delete. This prevents an AI hallucination from destroying learned knowledge.

  3. Destructive tool calls require user confirmation. If a ToolDefinition has requires_confirmation: true, the agent MUST pause execution and ask the user to approve before calling the tool. The UI shows a confirmation dialog. No implicit destructive actions.

  4. Usage is checked before LLM calls, not after. The cost governor checks usage_records for the current billing period before sending any request to an LLM provider. If the tenant has exceeded their plan limit, the request is rejected with a graceful message — no tokens are consumed.

  5. Conversations auto-close after 24 hours of inactivity. When closed, the system extracts memories from the conversation (immediate consolidation), logs final usage, and destroys the Durable Object. The conversation history remains in PostgreSQL permanently for audit and training.

  6. One active conversation per user per tenant. A user cannot have two simultaneous copilot sessions. Opening a new tab resumes the existing conversation. This is enforced by the Durable Object (keyed by tenant_id:user_id).

  7. Insights expire. Every insight has an expires_at timestamp. Stale insights (e.g., "Your traffic dropped last week" when it's now recovered) are hidden after expiry. The insight engine generates fresh insights every 6 hours.

  8. Memory importance is bounded [0.0, 1.0]. Boosting and decaying never push importance outside this range. A memory at 1.0 importance still decays — nothing is immune to time.

AI Brain