ADR 006: Superpowers Platform (User-Created Apps)¶
Date: 2025-12-12 Updated: 2026-01-07 Status: Implementing Deciders: Engineering Team Related: ADR 004: MiniApp Framework, ADR 001: Event Sourcing
Terminology¶
| Term | Definition |
|---|---|
| Superpower | A user-created custom app built via conversation with Sage |
| Workflow | An automated background task (Travel Brain, Gmail, Calendar, etc.) in app/workflows/ |
| MiniApp | A built-in app like Trip Planner or Bill Split |
Context¶
We need to evolve from developer-built MiniApps to user-created apps ("superpowers") that can be:
- Created via conversation - Users "vibe-code" by chatting with Sage
- Shared privately - Via unique links
- Published to marketplace - For community discovery
Use Case Example¶
A user describes: "Create a superpower that analyzes receipt photos, tracks who owes what, and checks my Gmail for Zelle/Venmo payment confirmations to mark splits as paid."
This should generate a functional superpower without the user writing any code.
Existing Infrastructure¶
ADR 004 established a mature MiniApp framework:
- Event sourcing with EventStore and StateCoordinator
- Handler Registry pattern for loading handlers
- Default reducer that merges event_data into state
- UI Block system for rendering
- SmartMessageRouter for intent-based routing
- Database tables: mini_apps, room_capabilities, user_miniapp_settings
Requirements¶
- No Code Required - Users define config, not Python
- Full Integration Support - Gmail, Calendar, Vision API via OAuth
- Secure Isolation - Superpowers can't access other users' data
- Seamless Routing - SmartMessageRouter routes to superpowers via
create_superpowerworkflow - Marketplace Ready - Foundation for future public sharing
Decision¶
We will implement a Config-Driven Superpowers Platform with:
- JSON Definition Schema - Pydantic-validated structure in
mini_apps.definition - GenericConfigHandler - Interprets JSON definitions at runtime
- Database-Driven Triggers - Move patterns from Python to
miniapp_trigger_patternstable - Scoped OAuth Grants - Per-app OAuth permissions in
custom_miniapp_oauth_grants - Template Interpolation - Dynamic content via
{{$state.field}}syntax
Conceptual Model¶
Users define three things (no code):
| What Users Define | What It Becomes |
|---|---|
| Objects/State | initial_state JSON with fields |
| Prompts for Processing | actions with operations (LLM, Vision, Gmail) |
| Output Format | response templates and ui_blocks |
Architecture¶
User (via Sage)
→ Describes superpower requirements
→ SmartRouter routes to `create_superpower` workflow
→ AppCreationHandler manages multi-turn conversation
→ GPT-5 generates CustomAppDefinition JSON
→ Validated against Pydantic schema
→ Stored in mini_apps.definition
User Message (using superpower)
→ SmartMessageRouter (dynamic superpower list)
→ MiniAppDetector (database-driven patterns)
→ MiniAppRegistry.get_handler()
→ Returns GenericConfigHandler for superpowers
→ GenericConfigHandler.handle()
→ Match action via triggers
→ Check OAuth grants
→ Execute operations sequentially
→ Emit events to EventStore
→ Interpolate response template
→ User sees response + UI
Alternatives Considered¶
Option 1: Sandboxed Python Execution¶
Approach: Users write Python code executed in sandbox
Pros: - Maximum flexibility - Full programming power
Cons: - ❌ Security nightmare (code injection, resource abuse) - ❌ Complex sandboxing infrastructure - ❌ Users must know Python - ❌ Versioning and dependency hell
Verdict: ❌ Too risky, too complex for MVP
Option 2: Visual Builder UI¶
Approach: Drag-and-drop workflow builder (like Zapier)
Pros: - Intuitive for non-technical users - Visual feedback
Cons: - ❌ Requires significant frontend investment - ❌ Limited flexibility - ❌ Doesn't leverage our conversational strength
Verdict: ⏳ Phase 3 enhancement, not foundation
Option 3: Config-Driven with GenericHandler (CHOSEN)¶
Approach: JSON definitions interpreted by generic handler
Pros: - ✅ No code security risks - ✅ Validates via Pydantic - ✅ Leverages existing EventStore, StateCoordinator, UIBuilder - ✅ Natural fit with conversational creation - ✅ Portable and versionable - ✅ Clear upgrade path to marketplace
Cons: - ⚠️ Limited to predefined operation types - ⚠️ Template syntax learning curve
Mitigation: - Rich operation library (vision, gmail, calendar, web) - AI-assisted template generation - Expandable operation types over time
Verdict: ✅ Best balance of safety, flexibility, and fit
Implementation Details¶
1. Database Schema Extensions¶
-- Extend mini_apps table
ALTER TABLE mini_apps ADD COLUMN creator_user_id UUID REFERENCES users(id);
ALTER TABLE mini_apps ADD COLUMN status VARCHAR(20) DEFAULT 'active';
ALTER TABLE mini_apps ADD COLUMN share_token VARCHAR(64) UNIQUE;
ALTER TABLE mini_apps ADD COLUMN required_scopes JSONB DEFAULT '[]';
ALTER TABLE mini_apps ADD COLUMN definition JSONB;
-- New: Trigger patterns (replaces hard-coded Python)
CREATE TABLE miniapp_trigger_patterns (
id UUID PRIMARY KEY,
miniapp_id VARCHAR(100) REFERENCES mini_apps(id),
keywords JSONB DEFAULT '[]',
phrases JSONB DEFAULT '[]',
regex_patterns JSONB DEFAULT '[]',
priority INTEGER DEFAULT 5,
enabled BOOLEAN DEFAULT TRUE
);
-- New: OAuth grants per custom app
CREATE TABLE custom_miniapp_oauth_grants (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
miniapp_id VARCHAR(100) REFERENCES mini_apps(id),
scope VARCHAR(50) NOT NULL,
granted_at TIMESTAMPTZ DEFAULT NOW()
);
2. CustomAppDefinition Schema¶
class CustomAppDefinition(BaseModel):
version: str = "1.0"
metadata: AppMetadata # name, description, category, icon
initial_state: Dict[str, Any] # Starting state for sessions
event_types: List[EventType] # Events this app can emit
trigger_patterns: TriggerDef # Auto-detection patterns
actions: List[ActionDefinition] # Behavior definitions
ui_blocks: Dict[str, UIBlock] # Named UI views
3. GenericConfigHandler¶
class GenericConfigHandler(BaseMiniAppHandler):
"""Interprets JSON app definitions at runtime."""
def __init__(self, definition: CustomAppDefinition, miniapp_id: str):
self.definition = definition
self.app_id = miniapp_id
self.template_engine = TemplateEngine()
self.operation_executors = {
"emit_event": EmitEventExecutor(),
"vision_analysis": VisionAnalysisExecutor(),
"gmail_search": GmailSearchExecutor(),
"llm_extract": LLMExtractExecutor(),
}
async def handle(self, context: MiniAppContext) -> MiniAppResponse:
# 1. Match action via triggers
action = self._match_action(context)
# 2. Check OAuth requirements
if action.oauth_required:
missing = await self._check_grants(context.user_id, action.oauth_required)
if missing:
return self._request_oauth(missing)
# 3. Execute operations, emit events
op_results = {}
for op in action.operations:
result = await self._execute_operation(op, context, op_results)
if op.output:
op_results[op.output] = result
# 4. Interpolate response
message = self.template_engine.interpolate(
action.response.template,
{"state": context.state, "op": op_results}
)
return MiniAppResponse(message=message)
4. Template Interpolation¶
{{$state.items | length}} → len(state["items"])
{{$state.total | currency}} → "$123.45"
{{$op.receipt_data.merchant}} → Previous op result
{{$context.user_name}} → Execution context
{{$state.items | map:"name"}} → Extract field from list
{{value | default:"none"}} → Default if falsy
5. SmartMessageRouter Integration¶
# Dynamic MiniApp list in system prompt
def _build_miniapp_list(self, user_id: str) -> str:
# Built-in apps
apps = ["trip_planner: Plan trips...", "bill_split: Split bills..."]
# User's custom apps
custom = self.db.query(MiniApp).filter(
MiniApp.creator_user_id == user_id,
MiniApp.status == "active"
).all()
for app in custom:
apps.append(f"{app.id}: {app.description}")
return "\n".join(apps)
Security Model¶
OAuth Scope Isolation¶
- Custom apps request scopes via
required_scopes - Users explicitly grant scopes via
custom_miniapp_oauth_grants - Grants are per-user-per-app (not global)
- Users can revoke at any time
State Isolation¶
- Custom apps use existing EventStore with session_id scoping
- No cross-session or cross-user data access
- Event namespace:
custom:{miniapp_id}:{session_id}
Template Safety¶
- Templates only access explicit context
- No code execution in templates
- Pydantic validation prevents injection
Rate Limits¶
- Operation execution rate limited per user
- LLM operations count against user's quota
- Integration calls (Gmail, etc.) throttled
Phases¶
Phase 1: Foundation (Current)¶
- Database migration ✅
- SQLAlchemy models ✅
- Pydantic schema ✅
- Template engine
- GenericConfigHandler
- Operation executors
- Database-driven triggers
- Registry integration
Phase 2: Creation Flow¶
- Sage conversation → Definition generation
- Preview and validation UI
- Edit via conversation
Phase 3: Private Sharing¶
- Share tokens and install flow
- Permission prompts
- Usage analytics
Phase 4: Public Marketplace¶
- Discovery and search
- Ratings and reviews
- Featured apps
Trade-offs¶
Pros¶
- Safe by Design - No arbitrary code execution
- Leverages Existing Infra - EventStore, StateCoordinator, UIBuilder
- Natural Fit - Conversational creation matches product DNA
- Extensible - New operation types can be added
- Portable - JSON definitions are versionable and shareable
Cons¶
- Limited Flexibility - Can't do arbitrary computation
- Learning Curve - Template syntax and operation model
- Complexity - GenericHandler more complex than dedicated handlers
Mitigations¶
- Rich operation library covers most use cases
- AI assistance for template generation
- Comprehensive documentation and examples
- Gradual rollout with power users first
Success Criteria¶
Technical¶
- Custom app handles 95% of use cases without code
- GenericHandler performance <200ms overhead
- OAuth grants work correctly
- Template interpolation handles edge cases
- Database triggers load <50ms with caching
Product¶
- User can create simple app in <5 minutes via chat
- Share flow works without friction
- Installed apps appear in SmartMessageRouter
References¶
- ADR 004: MiniApp Framework
- ADR 001: Event Sourcing
- Feature Spec: Custom MiniApps
- Supabase JSONB: https://supabase.com/docs/guides/database/json
Revision History¶
- 2025-12-12: Initial implementation - database, models, schema complete