diff --git a/apps/backend/prisma/models/character.prisma b/apps/backend/prisma/models/character.prisma
new file mode 100644
index 0000000..68adf30
--- /dev/null
+++ b/apps/backend/prisma/models/character.prisma
@@ -0,0 +1,56 @@
+// Character model and character knowledge
+
+enum ImportSourceType {
+ file
+ url
+ manual
+}
+
+enum ImportStatus {
+ pending
+ processing
+ completed
+ failed
+}
+
+model Character {
+ id String @id @default(uuid())
+ name String
+ avatarUrl String?
+ personalityPrompt String
+ attributes Json @default("{}")
+ config Json @default("{}")
+ isPublic Boolean @default(false)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ userId String
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+ conversations Conversation[]
+ knowledgeSources CharacterKnowledge[]
+ vectorMemories VectorMemory[]
+
+ @@index([userId])
+ @@index([name])
+}
+
+model CharacterKnowledge {
+ id String @id @default(uuid())
+ name String
+ sourceType ImportSourceType
+ sourceName String
+ mimeType String?
+ fileSize BigInt?
+ rawContent String?
+ status ImportStatus @default(pending)
+ processingInfo Json?
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ characterId String
+ character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
+ vectorMemories VectorMemory[]
+
+ @@index([characterId])
+ @@index([status])
+}
diff --git a/apps/backend/prisma/models/conversation.prisma b/apps/backend/prisma/models/conversation.prisma
new file mode 100644
index 0000000..1d63197
--- /dev/null
+++ b/apps/backend/prisma/models/conversation.prisma
@@ -0,0 +1,38 @@
+// Conversation and participant models
+
+model Conversation {
+ id String @id @default(uuid())
+ title String?
+ messageCount Int @default(0)
+ totalTokens Int @default(0)
+ settings Json @default("{}")
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ userId String
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+ characterId String
+ character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
+ messages Message[]
+ vectorMemories VectorMemory[]
+ storyBranches StoryBranch[]
+ participants ConversationParticipant[]
+
+ @@index([userId])
+ @@index([characterId])
+ @@index([createdAt])
+}
+
+model ConversationParticipant {
+ id String @id @default(uuid())
+ isActive Boolean @default(true)
+ autoRespond Boolean @default(true)
+ createdAt DateTime @default(now())
+
+ conversationId String
+ conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
+ characterId String
+
+ @@unique([conversationId, characterId])
+ @@index([conversationId])
+}
diff --git a/apps/backend/prisma/models/importDocument.prisma b/apps/backend/prisma/models/importDocument.prisma
new file mode 100644
index 0000000..71f4670
--- /dev/null
+++ b/apps/backend/prisma/models/importDocument.prisma
@@ -0,0 +1,21 @@
+// General import documents (not linked to characters)
+
+model ImportDocument {
+ id String @id @default(uuid())
+ sourceType ImportSourceType
+ sourceName String
+ mimeType String?
+ fileSize BigInt?
+ content String?
+ status ImportStatus @default(pending)
+ errorMessage String?
+ metadata Json?
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ userId String
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+ @@index([userId])
+ @@index([status])
+}
diff --git a/apps/backend/prisma/models/message.prisma b/apps/backend/prisma/models/message.prisma
new file mode 100644
index 0000000..d6295c1
--- /dev/null
+++ b/apps/backend/prisma/models/message.prisma
@@ -0,0 +1,24 @@
+// Message model
+
+enum MessageRole {
+ user
+ assistant
+ system
+}
+
+model Message {
+ id String @id @default(uuid())
+ role MessageRole
+ content String
+ tokensUsed Int?
+ model String?
+ metadata Json?
+ createdAt DateTime @default(now())
+
+ conversationId String
+ conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
+
+ @@index([conversationId])
+ @@index([createdAt])
+ @@index([conversationId, createdAt])
+}
diff --git a/apps/backend/prisma/models/storyBranch.prisma b/apps/backend/prisma/models/storyBranch.prisma
new file mode 100644
index 0000000..fd152e5
--- /dev/null
+++ b/apps/backend/prisma/models/storyBranch.prisma
@@ -0,0 +1,21 @@
+// Story branching for narrative generation (Phase 2)
+
+model StoryBranch {
+ id String @id @default(uuid())
+ title String?
+ content String
+ userDirection String
+ generationParams Json?
+ depth Int @default(0)
+ branchOrder Int @default(0)
+ createdAt DateTime @default(now())
+
+ conversationId String
+ conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
+ parentId String?
+ parent StoryBranch? @relation("BranchTree", fields: [parentId], references: [id], onDelete: Cascade)
+ children StoryBranch[] @relation("BranchTree")
+
+ @@index([conversationId])
+ @@index([parentId])
+}
diff --git a/apps/backend/prisma/models/user.prisma b/apps/backend/prisma/models/user.prisma
new file mode 100644
index 0000000..38ea73d
--- /dev/null
+++ b/apps/backend/prisma/models/user.prisma
@@ -0,0 +1,25 @@
+// User model and related enums
+
+enum UserRole {
+ USER
+ ADMIN
+}
+
+model User {
+ id String @id @default(uuid())
+ email String @unique
+ username String @unique
+ passwordHash String?
+ keycloakSub String? @unique
+ role UserRole @default(USER)
+ isActive Boolean @default(true)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ characters Character[]
+ conversations Conversation[]
+ importDocs ImportDocument[]
+
+ @@index([email])
+ @@index([keycloakSub])
+}
diff --git a/apps/backend/prisma/models/vectorMemory.prisma b/apps/backend/prisma/models/vectorMemory.prisma
new file mode 100644
index 0000000..094ad1e
--- /dev/null
+++ b/apps/backend/prisma/models/vectorMemory.prisma
@@ -0,0 +1,29 @@
+// Vector memory for embeddings (conversation and character knowledge)
+
+enum MemoryType {
+ conversation
+ character
+}
+
+model VectorMemory {
+ id String @id @default(uuid())
+ content String
+ embedding Unsupported("vector")?
+ memoryType MemoryType @default(conversation)
+ metadata Json?
+ createdAt DateTime @default(now())
+
+ conversationId String?
+ conversation Conversation? @relation(fields: [conversationId], references: [id], onDelete: Cascade)
+
+ characterId String?
+ character Character? @relation(fields: [characterId], references: [id], onDelete: Cascade)
+
+ knowledgeId String?
+ knowledge CharacterKnowledge? @relation(fields: [knowledgeId], references: [id], onDelete: Cascade)
+
+ @@index([conversationId])
+ @@index([characterId])
+ @@index([knowledgeId])
+ @@index([memoryType])
+}
diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts
new file mode 100644
index 0000000..ee5f2c9
--- /dev/null
+++ b/apps/backend/src/app.module.ts
@@ -0,0 +1,8 @@
+import { Module } from '@nestjs/common';
+
+@Module({
+ imports: [],
+ controllers: [],
+ providers: [],
+})
+export class AppModule {}
diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts
new file mode 100644
index 0000000..8bdaff5
--- /dev/null
+++ b/apps/backend/src/main.ts
@@ -0,0 +1,20 @@
+import { NestFactory } from '@nestjs/core';
+import { AppModule } from './app.module';
+
+async function bootstrap() {
+ const app = await NestFactory.create(AppModule);
+
+ app.enableCors({
+ origin: ['http://localhost:5173'],
+ credentials: true,
+ });
+
+ app.setGlobalPrefix('api');
+
+ const port = process.env.PORT || 3000;
+ await app.listen(port);
+
+ console.log(`🚀 Backend running on: http://localhost:${port}/api`);
+}
+
+bootstrap();
diff --git a/apps/frontend/index.html b/apps/frontend/index.html
new file mode 100644
index 0000000..8356551
--- /dev/null
+++ b/apps/frontend/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ DreamChat
+
+
+
+
+
+
diff --git a/apps/frontend/postcss.config.js b/apps/frontend/postcss.config.js
new file mode 100644
index 0000000..2aa7205
--- /dev/null
+++ b/apps/frontend/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/apps/frontend/public/vite.svg b/apps/frontend/public/vite.svg
new file mode 100644
index 0000000..e69de29
diff --git a/apps/frontend/src/App.tsx b/apps/frontend/src/App.tsx
new file mode 100644
index 0000000..f07479c
--- /dev/null
+++ b/apps/frontend/src/App.tsx
@@ -0,0 +1,14 @@
+function App() {
+ return (
+
+
+
DreamChat
+
+ Character simulation and interactive storytelling platform
+
+
+
+ );
+}
+
+export default App;
diff --git a/apps/frontend/src/main.tsx b/apps/frontend/src/main.tsx
new file mode 100644
index 0000000..1f860e3
--- /dev/null
+++ b/apps/frontend/src/main.tsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import App from './App';
+import './styles/globals.css';
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+);
diff --git a/apps/frontend/src/styles/globals.css b/apps/frontend/src/styles/globals.css
new file mode 100644
index 0000000..10c2d37
--- /dev/null
+++ b/apps/frontend/src/styles/globals.css
@@ -0,0 +1,59 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 84% 4.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 84% 4.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 84% 4.9%;
+ --primary: 222.2 47.4% 11.2%;
+ --primary-foreground: 210 40% 98%;
+ --secondary: 210 40% 96.1%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+ --muted: 210 40% 96.1%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+ --accent: 210 40% 96.1%;
+ --accent-foreground: 222.2 47.4% 11.2%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 214.3 31.8% 91.4%;
+ --input: 214.3 31.8% 91.4%;
+ --ring: 222.2 84% 4.9%;
+ --radius: 0.5rem;
+ }
+
+ .dark {
+ --background: 222.2 84% 4.9%;
+ --foreground: 210 40% 98%;
+ --card: 222.2 84% 4.9%;
+ --card-foreground: 210 40% 98%;
+ --popover: 222.2 84% 4.9%;
+ --popover-foreground: 210 40% 98%;
+ --primary: 210 40% 98%;
+ --primary-foreground: 222.2 47.4% 11.2%;
+ --secondary: 217.2 32.6% 17.5%;
+ --secondary-foreground: 210 40% 98%;
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
+ --accent: 217.2 32.6% 17.5%;
+ --accent-foreground: 210 40% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 210 40% 98%;
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 212.7 26.8% 83.9%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
diff --git a/apps/frontend/tailwind.config.ts b/apps/frontend/tailwind.config.ts
new file mode 100644
index 0000000..5f3b447
--- /dev/null
+++ b/apps/frontend/tailwind.config.ts
@@ -0,0 +1,38 @@
+import type { Config } from 'tailwindcss';
+
+export default {
+ darkMode: ['class'],
+ content: ['./src/**/*.{js,ts,jsx,tsx}'],
+ theme: {
+ extend: {
+ colors: {
+ border: 'hsl(var(--border))',
+ input: 'hsl(var(--input))',
+ ring: 'hsl(var(--ring))',
+ background: 'hsl(var(--background))',
+ foreground: 'hsl(var(--foreground))',
+ primary: {
+ DEFAULT: 'hsl(var(--primary))',
+ foreground: 'hsl(var(--primary-foreground))',
+ },
+ secondary: {
+ DEFAULT: 'hsl(var(--secondary))',
+ foreground: 'hsl(var(--secondary-foreground))',
+ },
+ destructive: {
+ DEFAULT: 'hsl(var(--destructive))',
+ foreground: 'hsl(var(--destructive-foreground))',
+ },
+ muted: {
+ DEFAULT: 'hsl(var(--muted))',
+ foreground: 'hsl(var(--muted-foreground))',
+ },
+ accent: {
+ DEFAULT: 'hsl(var(--accent))',
+ foreground: 'hsl(var(--accent-foreground))',
+ },
+ },
+ },
+ },
+ plugins: [],
+} satisfies Config;
diff --git a/packages/shared/src/api/dto.ts b/packages/shared/src/api/dto.ts
new file mode 100644
index 0000000..c6b2cdb
--- /dev/null
+++ b/packages/shared/src/api/dto.ts
@@ -0,0 +1,86 @@
+/**
+ * Shared API DTOs
+ * Used by backend for validation and frontend for type safety
+ */
+
+// Character DTOs
+export interface CreateCharacterDto {
+ name: string;
+ personalityPrompt: string;
+ backstory?: string;
+ attributes?: Record;
+ avatarUrl?: string;
+ config?: Record;
+}
+
+export interface UpdateCharacterDto extends Partial {}
+
+export interface CharacterResponseDto {
+ id: string;
+ name: string;
+ avatarUrl?: string;
+ personalityPrompt: string;
+ backstory?: string;
+ attributes: Record;
+ config: Record;
+ isPublic: boolean;
+ createdAt: string;
+ updatedAt: string;
+}
+
+// Conversation DTOs
+export interface CreateConversationDto {
+ characterId: string;
+ title?: string;
+}
+
+export interface ConversationResponseDto {
+ id: string;
+ title?: string;
+ characterId: string;
+ messageCount: number;
+ totalTokens: number;
+ createdAt: string;
+ updatedAt: string;
+}
+
+// Message DTOs
+export interface CreateMessageDto {
+ content: string;
+}
+
+export interface MessageResponseDto {
+ id: string;
+ role: 'user' | 'assistant' | 'system';
+ content: string;
+ tokensUsed?: number;
+ model?: string;
+ metadata?: Record;
+ createdAt: string;
+}
+
+// Auth DTOs
+export interface LoginDto {
+ username: string;
+ password: string;
+}
+
+export interface RegisterDto {
+ email: string;
+ username: string;
+ password: string;
+}
+
+export interface AuthResponseDto {
+ accessToken: string;
+ refreshToken: string;
+ expiresIn: number;
+ user: UserResponseDto;
+}
+
+export interface UserResponseDto {
+ id: string;
+ email: string;
+ username: string;
+ role: 'USER' | 'ADMIN';
+}
diff --git a/packages/shared/src/api/index.ts b/packages/shared/src/api/index.ts
new file mode 100644
index 0000000..ab3e767
--- /dev/null
+++ b/packages/shared/src/api/index.ts
@@ -0,0 +1 @@
+export * from './dto.js';
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
new file mode 100644
index 0000000..aaddc73
--- /dev/null
+++ b/packages/shared/src/index.ts
@@ -0,0 +1,2 @@
+export * from './websocket/index.js';
+export * from './api/index.js';
diff --git a/packages/shared/src/websocket/events.ts b/packages/shared/src/websocket/events.ts
new file mode 100644
index 0000000..8d7f794
--- /dev/null
+++ b/packages/shared/src/websocket/events.ts
@@ -0,0 +1,84 @@
+/**
+ * WebSocket Event Types
+ * Shared between frontend and backend for type-safe communication
+ */
+
+export enum WebSocketEventType {
+ // Client -> Server
+ JOIN_CONVERSATION = 'JOIN_CONVERSATION',
+ LEAVE_CONVERSATION = 'LEAVE_CONVERSATION',
+ SEND_MESSAGE = 'SEND_MESSAGE',
+ STOP_GENERATION = 'STOP_GENERATION',
+
+ // Server -> Client
+ CONVERSATION_JOINED = 'CONVERSATION_JOINED',
+ MESSAGE_ACK = 'MESSAGE_ACK',
+ STREAM_CHUNK = 'STREAM_CHUNK',
+ STREAM_COMPLETE = 'STREAM_COMPLETE',
+ ERROR = 'ERROR',
+}
+
+// Base message interface
+export interface WebSocketMessage {
+ type: WebSocketEventType;
+ payload: T;
+ timestamp: string;
+ requestId?: string;
+}
+
+// Client -> Server payloads
+export interface JoinConversationPayload {
+ conversationId: string;
+}
+
+export interface LeaveConversationPayload {
+ conversationId: string;
+}
+
+export interface SendMessagePayload {
+ conversationId: string;
+ content: string;
+ streaming?: boolean;
+}
+
+export interface StopGenerationPayload {
+ conversationId: string;
+}
+
+// Server -> Client payloads
+export interface ConversationJoinedPayload {
+ conversationId: string;
+ history: MessageHistoryItem[];
+}
+
+export interface MessageHistoryItem {
+ id: string;
+ role: 'user' | 'assistant' | 'system';
+ content: string;
+ createdAt: string;
+}
+
+export interface MessageAckPayload {
+ messageId: string;
+ status: 'received' | 'processing' | 'error';
+}
+
+export interface StreamChunkPayload {
+ conversationId: string;
+ chunk: string;
+ isComplete: boolean;
+}
+
+export interface StreamCompletePayload {
+ conversationId: string;
+ messageId: string;
+ content: string;
+ tokensUsed?: number;
+ model?: string;
+}
+
+export interface ErrorPayload {
+ code: string;
+ message: string;
+ details?: Record;
+}
diff --git a/packages/shared/src/websocket/index.ts b/packages/shared/src/websocket/index.ts
new file mode 100644
index 0000000..4cc3078
--- /dev/null
+++ b/packages/shared/src/websocket/index.ts
@@ -0,0 +1 @@
+export * from './events.js';