vibesdk Documentation

An open-source vibe coding platform that helps you build your own vibe-coding platform, built entirely on Cloudflare stack

5,016 stars TypeScript TypeScript 1 file ยท ~50,285 tokens #ai#cloudflare-workers#coding-agent#durable-objects#text-to-app#vibe-coding
RAW Doc

1. Project Overview & Quickstart (cloudflare/vibesdk)

File: README.md

Cloudflare VibeSDK

An open source, agentic platform for building and deploying full-stack applications on Cloudflare.

What is VibeSDK?

VibeSDK lets people build full-stack applications by working with an AI coding agent. Describe what you want, answer clarifying questions, and follow the agent as it plans, edits files, deploys previews, inspects errors, and iterates with you in the loop.

The platform runs the complete workflow on Cloudflare. Think drives the agentic model-and-tool loop, a Durable Object provides an isolated workspace for each project, Dynamic Workers serve generated application previews, Durable Object Facets provide per-app SQLite storage, and Cloudflare Artifacts stores durable git history and restore points.

Capabilities

  • Agentic code generation: Build iteratively through a model-and-tool loop instead of a fixed sequence of generation phases.
  • Human-in-the-loop clarification: Let the agent ask structured questions when a request is underspecified.
  • Live workspace: Follow generated files and edits in an integrated code editor.
  • Dynamic previews: Bundle generated code and load each preview as a Dynamic Worker without a long-running development server.
  • Browser verification: Inspect browser console output, repair runtime errors, and redeploy in the same loop.
  • Application data: Give each generated app isolated SQLite-backed storage through a Durable Object Facet, with database inspection and reset controls.
  • Version history: Save agent-created restore points in Cloudflare Artifacts and roll back without rewriting existing history.
  • Model flexibility: Route configured model providers through AI Gateway with centralized observability and caching.
  • Realtime progress: Stream agent output, tool activity, file changes, and deployment status to the UI.
  • Project export: Export generated projects when you want to continue development outside VibeSDK.

Architecture

Component Role
ThinkAgent Agent powered by Cloudflare Think and backed by a Durable Object that runs the model-and-tool loop
SpaceDO Durable Object that provides each project's isolated workspace and files
Cloudflare Artifacts Binding that stores commits, branches, history, and restore points
Worker Loader Binding that loads bundled code as a Dynamic Worker preview
Generated App Durable Object Facet with isolated SQLite storage
AI Gateway Routes configured model providers with observability and caching
mermaid
flowchart LR
    U[User] <--> T[ThinkAgent]
    T <--> M[Models via AI Gateway]
    T <--> S[SpaceDO workspace]
    S <--> A[Cloudflare Artifacts]
    S --> L[Worker Loader]
    L --> W[Dynamic Worker preview]
    W <--> F[App Facet + SQLite]

Agent and tools

Think manages conversation history, streaming, skills, and the iterative model-and-tool loop. Its explicit tools read and edit the SpaceDO workspace, create restore points, deploy previews, inspect browser logs, and ask clarifying questions. Bash access is disabled.

Previews and application data

VibeSDK bundles each committed deployment with @cloudflare/worker-bundler and loads it through the Worker Loader binding. Static assets are served by SpaceDO, while backend requests and WebSockets are forwarded to the generated App Durable Object Facet. Each Facet has isolated SQLite storage that users can inspect and reset.

Files and version history

SpaceDO is the workspace and file layer. Cloudflare Artifacts is the git and version-history layer. Rollback restores a selected commit into the current branch, creates a new commit, and redeploys without rewriting history.

Agent workflow

  1. Understand: Read the request and ask structured questions when important details are missing.
  2. Build: Create and edit files in the SpaceDO workspace.
  3. Save: Record coherent restore points in Cloudflare Artifacts.
  4. Deploy: Commit and bundle the current branch into a Dynamic Worker preview.
  5. Verify: Inspect the live preview and browser console output.
  6. Repair: Fix build or runtime errors and repeat the deploy-and-verify loop.
  7. Stream: Continuously surface output, tool calls, files, and preview status in the UI.

Deploy your own VibeSDK

The fastest way to deploy is the Deploy to Cloudflare flow:

You will need:

  • A Cloudflare account with the required Workers features enabled.
  • A Workers Paid plan and Workers for Platforms access for production app previews.
  • A Cloudflare API token with access to the resources created by setup.
  • Credentials for at least one supported model provider, unless you use provider keys stored in AI Gateway.
  • A custom domain for production, including wildcard DNS and Advanced Certificate Manager when required by your subdomain layout.

Authentication providers, model providers, and export integrations are optional and configuration-dependent. See the complete setup guide for current permissions, DNS, AI Gateway, provider, OAuth, and production configuration.

Local development

Prerequisites

  • Node.js 18 or later
  • Bun
  • A Cloudflare account and API token

Quick start

bash
git clone https://github.com/cloudflare/vibesdk.git
cd vibesdk
bun install
bun run setup
bun run dev

Open http://localhost:5173. The setup script configures local and production environments, Cloudflare resources, AI Gateway and model providers, authentication, and database migrations.

For all setup options and troubleshooting, read docs/setup.md.

Feature toggles

Feature settings are dashboard-managed rather than committed in wrangler.jsonc, so production values survive deploys through keep_vars: true. Set them in the Cloudflare dashboard for each deployed environment, or in .dev.vars for local development. Unset values default to off unless noted otherwise: ENABLE_ARTIFACTS, ENABLE_READ_REPLICAS, ENABLE_CLOUDFLARE_LIMITS, ENABLE_USER_ACCOUNT_DEPLOY, ALLOWED_EMAIL, USE_CLOUDFLARE_IMAGES, and USE_TUNNEL_FOR_PREVIEW; ENABLE_EMAIL_AUTH defaults to on. ALLOCATION_STRATEGY uses its normal default when unset. See the feature-toggle setup reference for dependencies and behavior.

Development commands

Command Purpose
bun run dev Start the frontend development server
bun run dev:browser Start the optional local browser sidecar used for console-log inspection
bun run build Build SpaceDO and the Vite application
bun run typecheck Run TypeScript type checking
bun run lint Run ESLint
bun run test Run the test suite
bun run test:watch Run tests in watch mode
bun run deploy Build, migrate, and deploy using .prod.vars

Security and isolation

  • Project isolation: Each app maps to its own ThinkAgent Durable Object, SpaceDO workspace, Artifacts repository, and generated application Facet.
  • Tool boundaries: Think uses explicit workspace and product tools; workspace bash access is disabled.
  • Application data isolation: Each generated application stores its data in its own SQLite-backed Durable Object Facet.
  • Preview authorization: Preview URLs use signed, branch-scoped access.
  • Secret handling: Provider and platform credentials flow through configured Cloudflare services and bindings.
  • Reversible changes: Artifacts-backed restore points make deployed changes recoverable without deleting history.

Troubleshooting

  • Preview does not resolve: Confirm the wildcard DNS record is proxied, allow time for DNS propagation, and verify any required Advanced Certificate Manager configuration.
  • AI Gateway authentication fails: Confirm the gateway and token are configured, the token has Run permission, and the selected provider has valid credentials or stored keys.
  • Database migrations fail: Check D1 access and API-token permissions, then retry after newly provisioned resources become available.
  • Required variables are missing: Verify JWT_SECRET, Cloudflare account credentials, AI Gateway settings, and credentials for every enabled provider or integration.

More diagnostics are available in the setup guide. For help, use GitHub issues, GitHub discussions, or the Cloudflare Developers Discord.

Contributing

  1. Fork and clone the repository.
  2. Run bun install and bun run setup.
  3. Make focused changes that follow AGENTS.md.
  4. Run bun run typecheck, bun run lint, and bun run test.
  5. Open a pull request describing the change and how you validated it.

Resources

License

VibeSDK is available under the MIT License.


File: docs/architecture-diagrams.md

VibeSDK Architecture Diagrams

Current architecture

mermaid
flowchart LR
    U[User] <--> UI[VibeSDK UI on Workers]
    UI <--> T[ThinkAgentCloudflare Think + Durable Object]
    T <--> G[Models through AI Gateway]
    T <--> S[SpaceDOworkspace and files]
    S <--> A[Cloudflare Artifactscommits and history]
    S --> B[worker-bundler]
    B --> L[Worker Loader]
    L --> P[Dynamic Worker preview]
    P <--> F[App Facetisolated SQLite]

Think runs the iterative model-and-tool loop. Its explicit tools edit files in SpaceDO, create Artifacts-backed restore points, deploy Dynamic Worker previews, inspect browser logs, and repair errors. SpaceDO is the workspace and file layer; Cloudflare Artifacts is the durable git and version-history layer.

Rollback applies a selected commit tree to the current branch, creates a new commit, and redeploys without rewriting history. Generated applications export an App Durable Object class that SpaceDO hosts as a Facet with isolated SQLite storage.

Legacy architecture reference

The diagrams below document the retired phase-based sandbox architecture. They are retained only as historical context and must not be used to describe the current Think, SpaceDO, Artifacts, Worker Loader, and App Facet implementation.

Presentation-Ready Architecture Diagram

Copy-paste this beautiful diagram directly into your slides

text
/* Detailed source-code truncated for AI context efficiency. */

Detailed System Diagrams

1. Overall System Architecture

text
/* Detailed source-code truncated for AI context efficiency. */

2. Hybrid Agent System Architecture

text
/* Detailed source-code truncated for AI context efficiency. */

3. Authentication & User Management Flow

mermaid
sequenceDiagram
    participant User
    participant Frontend
    participant AuthController
    participant OAuth
    participant D1
    participant JWT
    
    User->>Frontend: Login Request
    Frontend->>AuthController: POST /api/auth/providers
    AuthController->>Frontend: Available auth methods
    
    alt OAuth Flow
        Frontend->>AuthController: POST /api/auth/oauth/github
        AuthController->>OAuth: Redirect to GitHub
        OAuth->>User: Authorization page
        User->>OAuth: Grant permission
        OAuth->>AuthController: Callback with code
        AuthController->>OAuth: Exchange code for tokens
        OAuth->>AuthController: User profile + tokens
        AuthController->>D1: Store/update user
        AuthController->>JWT: Generate tokens
        JWT->>AuthController: Access/Refresh tokens
        AuthController->>Frontend: Set HTTP-only cookies
    else Email/Password Flow
        Frontend->>AuthController: POST /api/auth/login
        AuthController->>D1: Verify credentials
        D1->>AuthController: User data
        AuthController->>JWT: Generate tokens
        JWT->>AuthController: Access/Refresh tokens
        AuthController->>Frontend: Set HTTP-only cookies
    end
    
    Frontend->>User: Login success
    
    Note over AuthController,D1: Session stored in D1JWT tokens in HTTP-only cookiesAutomatic token refresh

4. Sandbox System & Deployment Pipeline

text
/* Detailed source-code truncated for AI context efficiency. */

5. Database Schema & Relationships

text
/* Detailed source-code truncated for AI context efficiency. */
```mermaid
journey
    title User Creates and Deploys an App
    section Getting Started
      Visit Homepage: 5: User
      Enter App Description: 4: User
      Start Generation: 5: User
    section Code Generation
      AI Creates Blueprint: 3: System
      Phase-wise Implementation: 4: System
      Real-time Code Review: 4: System
      Error Detection & Fixing: 3: System
    section Live Preview
      Sandbox Container Starts: 5: System
      Live Preview Available: 5: User
      Runtime Error Detection: 4: System
      Iterative Improvements: 4: User, System
    section Quality Assurance
      Static Analysis: 4: System
      Code Review Cycle: 3: System
      Auto-fix Critical Issues: 4: System
      User Feedback Integration: 5: User
    section Deployment
      Resource Provisioning: 3: System
      Template Parsing: 3: System
      Cloudflare Workers Deploy: 5: System
      Live App URL Generated: 5: User
    section Post-Deployment
      Save to Dashboard: 4: User
      Share with Community: 3: User
      GitHub Export: 4: User

7. Real-time Communication Flow

mermaid
sequenceDiagram
    participant User
    participant Frontend
    participant Agent
    participant AIGateway
    participant Sandbox
    participant WebSocket
    
    User->>Frontend: Send message
    Frontend->>Agent: WebSocket message
    Agent->>WebSocket: Broadcast generation_started
    WebSocket->>Frontend: Real-time update
    
    loop Phase Generation
        Agent->>AIGateway: Phase planning request
        AIGateway->>Agent: Streaming response
        Agent->>WebSocket: Broadcast phase_update
        WebSocket->>Frontend: Live phase progress
    end
    
    loop Code Implementation
        Agent->>AIGateway: Code generation request
        AIGateway->>Agent: SCOF streaming format
        Agent->>WebSocket: Broadcast file_generated
        WebSocket->>Frontend: Live file updates
    end
    
    Agent->>Sandbox: Deploy to container
    Sandbox->>Agent: Preview URL ready
    Agent->>WebSocket: Broadcast preview_ready
    WebSocket->>Frontend: Preview available
    
    loop Quality Assurance
        Sandbox->>Agent: Runtime errors detected
        Agent->>AIGateway: Fix generation request
        AIGateway->>Agent: Fixed code
        Agent->>Sandbox: Update files
        Agent->>WebSocket: Broadcast fixes_applied
        WebSocket->>Frontend: Updated preview
    end
    
    User->>Frontend: Deploy to Cloudflare
    Frontend->>Agent: Deploy request
    Agent->>Sandbox: Trigger deployment
    Sandbox->>Agent: Deployment complete
    Agent->>WebSocket: Broadcast deployment_complete
    WebSocket->>Frontend: Live app URL

8. AI Operations Pipeline

mermaid
flowchart TD
    %% User Input
    Input["๐Ÿ—ฃ๏ธ User PromptNatural Language Request"]
    
    %% Planning Phase
    subgraph "๐Ÿ“‹ Planning Phase"
        TemplateSelection["๐Ÿท๏ธ Template SelectionCloudflare Stack Templates"]
        BlueprintGen["๐Ÿ  Blueprint GenerationPRD + Architecture Design"]
    end
    
    %% State Machine Controller
    subgraph "๐Ÿค– Deterministic State Machine"
        StateMachine["โš™๏ธ Agent State ControllergenerateAllFiles()"]
        
        subgraph "๐Ÿ”„ State Transitions"
            PhaseGenerating["๐Ÿ“ˆ PHASE_GENERATING"]
            PhaseImplementing["๐Ÿš€ PHASE_IMPLEMENTING"]
            Reviewing["๐Ÿ” REVIEWING"]
            Finalizing["โœจ FINALIZING"]
            Idle["โœ… IDLE"]
        end
    end
    
    %% Operations Layer
    subgraph "๐Ÿง  AI Operations"
        PhaseGenOp["๐Ÿ“ˆ Phase GenerationDevelopment Phases"]
        PhaseImplOp["๐Ÿš€ Phase ImplementationFile Generation + SCOF"]
        CodeReviewOp["๐Ÿ” Code ReviewIssue Detection"]
        FastCodeFixerOp["โšก Fast Code FixerQuick Issue Fixes"]
        FileRegenOp["๐Ÿ› ๏ธ File RegenerationSurgical Code Repairs"]
        ScreenshotAnalysisOp["๐Ÿ“ท Screenshot AnalysisVisual Validation"]
        UserConvOp["๐Ÿ’ฌ User ConversationFeedback Processing"]
    end
    
    %% Quality Assurance
    subgraph "๐Ÿ” Quality Assurance"
        ReviewCycles["๐Ÿ”„ Review CyclesUp to 5 iterations"]
        IssueCheck{{"โš ๏ธ Issues Found?"}}
        StaticAnalysis["๐Ÿ“Š Static AnalysisCode Validation"]
    end
    
    %% External Integration
    subgraph "๐Ÿค– AI Gateway"
        AIGateway["๐Ÿšช Cloudflare AI GatewayMulti-Provider Router"]
        
        subgraph "๐ŸŒ AI Providers"
            Gemini["๐Ÿ’Ž Gemini (Primary)"]
            GPT["๐Ÿง  GPT-4"]
            Claude["๐ŸŽญ Claude"]
            Cerebras["๐Ÿงช Cerebras"]
        end
    end
    
    %% Main Flow
    Input --> TemplateSelection
    TemplateSelection --> BlueprintGen
    BlueprintGen --> StateMachine
    
    %% State Machine Flow
    StateMachine --> PhaseGenerating
    PhaseGenerating --> PhaseImplementing
    PhaseImplementing --> Reviewing
    Reviewing --> Finalizing
    Finalizing --> Idle
    
    %% Operations Execution
    PhaseGenerating --> PhaseGenOp
    PhaseImplementing --> PhaseImplOp
    Reviewing --> CodeReviewOp
    
    %% Quality Control Loop
    CodeReviewOp --> ReviewCycles
    ReviewCycles --> IssueCheck
    IssueCheck -->|"โœ… Yes"| FastCodeFixerOp
    IssueCheck -->|"โœ… Yes"| FileRegenOp
    IssueCheck -->|"โŒ No"| StaticAnalysis
    FastCodeFixerOp --> PhaseImplementing
    FileRegenOp --> PhaseImplementing
    
    %% User Interaction
    UserConvOp --> PhaseGenerating
    ScreenshotAnalysisOp --> CodeReviewOp
    
    %% AI Integration
    TemplateSelection --> AIGateway
    BlueprintGen --> AIGateway
    PhaseGenOp --> AIGateway
    PhaseImplOp --> AIGateway
    CodeReviewOp --> AIGateway
    FastCodeFixerOp --> AIGateway
    FileRegenOp --> AIGateway
    ScreenshotAnalysisOp --> AIGateway
    UserConvOp --> AIGateway
    
    AIGateway --> Gemini
    AIGateway --> GPT
    AIGateway --> Claude
    AIGateway --> Cerebras
    
    %% Enhanced Styling
    classDef planning fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#000
    classDef statemachine fill:#fff3e0,stroke:#f57c00,stroke-width:3px,color:#000
    classDef states fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#000
    classDef operations fill:#e8f5e8,stroke:#388e3c,stroke-width:2px,color:#000
    classDef quality fill:#ffebee,stroke:#d32f2f,stroke-width:2px,color:#000
    classDef ai fill:#ff9900,stroke:#cc7a00,stroke-width:3px,color:#fff
    classDef decision fill:#fff8e1,stroke:#f57f17,stroke-width:2px,color:#000
    classDef success fill:#66ff66,stroke:#2e7d32,stroke-width:3px,color:#000
    classDef providers fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:#000
    
    class Input,TemplateSelection,BlueprintGen planning
    class StateMachine statemachine
    class PhaseGenerating,PhaseImplementing,Reviewing,Finalizing states
    class Idle success
    class PhaseGenOp,PhaseImplOp,CodeReviewOp,FastCodeFixerOp,FileRegenOp,ScreenshotAnalysisOp,UserConvOp operations
    class ReviewCycles,StaticAnalysis quality
    class IssueCheck decision
    class AIGateway ai
    class Gemini,GPT,Claude,Cerebras providers

9. Technology Stack Overview

mermaid
graph TB
    subgraph "Frontend Layer"
        React[React 18]
        Vite[Vite Build Tool]
        TailwindCSS[Tailwind CSS]
        ShadcnUI[shadcn/ui Components]
        ReactRouter[React Router]
    end
    
    subgraph "Backend Layer"
        CFWorkers[Cloudflare Workers]
        AgentsSDK[Cloudflare Agents SDK]
        TypeScript[TypeScript]
        HonoRouter[Hono Router]
        AuthMiddleware[Auth Middleware]
        BaseController[Base Controller]
    end
    
    subgraph "Data Layer"
        D1[Cloudflare D1 SQLite]
        Drizzle[Drizzle ORM]
        KV[Cloudflare KV]
        R2[Cloudflare R2]
        DatabaseService[Database Service]
        UserService[User Service]
        AppService[App Service]
        AuthService[Auth Service]
    end
    
    subgraph "AI & External Services"
        AIGateway[Cloudflare AI Gateway]
        OpenAI[OpenAI GPT-4]
        GitHubAPI[GitHub API]
        OAuth[OAuth Providers]
    end
    
    subgraph "Infrastructure"
        CFContainers[Cloudflare Containers]
        SandboxSDK[Cloudflare Sandbox SDK]
        WebSockets[WebSocket API]
        WorkersAnalytics[Workers Analytics]
    end
    
    React --> CFWorkers
    Vite --> React
    TailwindCSS --> React
    ShadcnUI --> React
    ReactRouter --> React
    
    CFWorkers --> AgentsSDK
    TypeScript --> CFWorkers
    HonoRouter --> CFWorkers
    AuthMiddleware --> CFWorkers
    BaseController --> CFWorkers
    
    CFWorkers --> DatabaseService
    DatabaseService --> Drizzle
    Drizzle --> D1
    DatabaseService --> UserService
    DatabaseService --> AppService
    DatabaseService --> AuthService
    CFWorkers --> KV
    CFWorkers --> R2
    
    CFWorkers --> AIGateway
    AIGateway --> OpenAI
    CFWorkers --> GitHubAPI
    CFWorkers --> OAuth
    
    CFWorkers --> CFContainers
    CFContainers --> SandboxSDK
    CFWorkers --> WebSockets
    CFWorkers --> WorkersAnalytics
    
    style CFWorkers fill:#ff9900
    style AgentsSDK fill:#ff9900
    style D1 fill:#ff9900
    style AIGateway fill:#ff9900
    style CFContainers fill:#ff9900

File: docs/llm.md

VibeSDK Developer Guide

Follow AGENTS.md for current commands, code style, error handling, and required implementation patterns. The current architecture in this section supersedes the legacy reference later in this document.

Current architecture

  • ThinkAgent is an Agent powered by Cloudflare Think and backed by a Durable Object. It owns conversation history, context selection, skills, streaming, tools, and step limits.
  • SpaceDO is a Durable Object that provides each project's isolated workspace and files. Think's explicit workspace tools call it through Durable Object RPC; workspace bash is disabled.
  • When ENABLE_ARTIFACTS="true" is selected for a SpaceDO, Cloudflare Artifacts is its durable git and version-history layer for commits, branches, history, and restore points; otherwise the SQLite workspace filesystem stores its local git history.
  • @cloudflare/worker-bundler builds committed project files, and a Worker Loader binding loads them as a Dynamic Worker preview.
  • Generated apps export an App Durable Object class that SpaceDO hosts as a Facet with isolated SQLite storage.
  • AI Gateway routes configured model providers and provides centralized observability and caching.

SpaceDO is the workspace and file layer. For Artifacts-backed spaces, Cloudflare Artifacts is the git and history layer; SQL-backed spaces retain local git history in SQLite. Do not describe SpaceDO itself as git-backed.

Current coding loop

  1. The host behavior configures ThinkAgent with model coordinates, project context, and a signed preview URL.
  2. Think calls models and tools iteratively within the turn's step budget.
  3. Workspace tools read and edit files in the companion SpaceDO.
  4. commit creates a meaningful restore point without deploying; deploy_space commits and rebuilds the branch preview.
  5. SpaceDO bundles files and loads the result through the Worker Loader binding.
  6. Browser console diagnostics return to Think for repair and redeployment.
  7. Rollback applies a selected tree, creates a new commit on the current branch, and redeploys without rewriting history.

Authoritative paths

Area Path
Think and tool registration worker/agents/think/ThinkAgent.ts
Host orchestration worker/agents/core/behaviors/think.ts
Workspace adapter worker/agents/think/space-workspace-ops.ts
Think prompts and skills worker/agents/think/prompts/, worker/agents/think/skills/
SpaceDO space/src/space/durable-object.ts
Preview build pipeline space/src/space/deploy-engine.ts
Artifacts synchronization space/src/space/artifacts-sync.ts
Frontend API types and client src/api-types.ts, src/lib/api-client.ts
API routes and controllers worker/api/routes/, worker/api/controllers/
Setup and deployment scripts/setup.ts, scripts/deploy.ts

Development commands

bash
bun install
bun run setup
bun run dev
bun run typecheck
bun run lint
bun run test
bun run build

Use bun run dev:browser for local browser-console inspection and bun run deploy with .prod.vars for production deployment.

Legacy reference

Everything below this point documents the retired phase-based sandbox architecture. It is retained for historical context only. Do not follow its paths, commands, state machines, sandbox guidance, or git ownership model without verifying them against the current code.


๐Ÿ“ Recent Changes (Updated Nov 2024)

**Git System Refactor - CLI Semantics Alignment**

Changes Made:

  1. GitVersionControl Class (/worker/agents/git/git.ts):

    • Added reset(ref, options) - Aligns with git reset --hard (moves HEAD, no new commit)
    • Removed revert() - Was creating incorrect "revert commits"
    • Removed restoreCommit() - Functionality merged into internal helpers
    • Removed inferPurposeFromPath() - No longer needed
    • Added setOnFilesChangedCallback() - Callback mechanism for FileManager sync
    • Added getAllFilesFromHead() - Simple HEAD file read for syncing
  2. FileManager Auto-Sync (/worker/agents/services/implementations/FileManager.ts):

    • Self-contained synchronization via callback registration
    • Auto-registers with GitVersionControl during construction
    • Syncs generatedFilesMap from git HEAD after operations
    • Preserves file purposes across syncs
    • No external changes required - fully transparent
  3. Git Tool with Access Control (/worker/agents/tools/toolkit/git.ts):

    • Parameterized tool creation: createGitTool(agent, logger, options?)
    • Dynamic command filtering via excludeCommands parameter
    • User conversations: Get safe version (commit, log, show only)
    • Deep debugger: Gets full version (includes reset with warnings)
    • Type-safe enum generation based on context
  4. Deep Debugger Prompt Updates (/worker/agents/assistants/codeDebugger.ts):

    • Updated git command documentation
    • Added strong warnings about reset being UNTESTED and DESTRUCTIVE
    • Clear guidance: only use reset when absolutely necessary
    • Documented proper git semantics (reset vs checkout)
  5. User Conversation Updates (/worker/agents/operations/UserConversationProcessor.ts):

    • Added git tool to help documentation
    • Noted reset unavailable for safety
  6. Commit Message Enhancement (/worker/agents/core/simpleGeneratorAgent.ts):

    • Phase commits now include description in body
    • Format: feat: Phase Name\n\nPhase description
    • Provides better context in git history

Benefits:

  • โœ… Git commands now match actual git CLI behavior
  • โœ… FileManager stays in sync automatically via callbacks
  • โœ… Context-aware tool access (safe for users, full for debugger)
  • โœ… Type-safe, DRY, flexible architecture
  • โœ… Self-contained FileManager (no external changes needed)

See detailed documentation: Section "GitVersionControl Class & Git Tool" (line 901)


๐Ÿ› ๏ธ Quick Start for Developers

**Running Locally**

Prerequisites:

  • Node.js 22+
  • Cloudflare account (for D1, Durable Objects)
  • API keys: OpenAI, Anthropic, Google AI Studio

Environment Variables:
Create .dev.vars in project root:

bash
# LLM Providers
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_AI_STUDIO_API_KEY=...

# Authentication
JWT_SECRET=your-secret-key
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...

# Cloudflare
CLOUDFLARE_ACCOUNT_ID=...
CLOUDFLARE_API_TOKEN=...

# Sandbox Service
SANDBOX_SERVICE_URL=https://sandbox.example.com
SANDBOX_SERVICE_TOKEN=...

Setup:

bash
# Install dependencies
npm install

# Setup local D1 database
npm run db:migrate:local

# Start dev servers
npm run dev        # Frontend (Vite)
npm run dev:worker # Backend (Wrangler)

**Common Development Tasks**

Task: Change LLM model for an operation

File: /worker/agents/inferutils/config.ts

typescript
export const AGENT_CONFIG = {
  blueprint: {
    name: GEMINI_2_5_PRO,  // Change this
    reasoning_effort: 'medium',
    max_tokens: 64000,
    temperature: 0.7
  },
  // ... other operations
};

Task: Modify system prompt for conversation agent

File: /worker/agents/operations/UserConversationProcessor.ts

  • Line ~50: System prompt starts
  • Defines Orange AI personality, tool usage rules, behavior

Task: Add new WebSocket message

See "Getting Started - Common Tasks" section below (line 1605)

Task: Debug Durable Object state

In code:

typescript
// In simpleGeneratorAgent.ts
this.logger().info('Current state', { 
  devState: this.state.currentDevState,
  filesCount: Object.keys(this.state.generatedFilesMap).length,
  currentPhase: this.state.currentPhase
});

Via Cloudflare dashboard:

  1. Go to Workers & Pages โ†’ Durable Objects
  2. Find your DO instance
  3. View SQLite database directly

๐ŸŽฏ Core Principles & Non-Negotiable Rules

**1. Strict Type Safety**

  • โŒ NEVER use any type - find or create proper types
  • โœ… All frontend types imported from @/api-types (which re-exports from worker) or shared/types/
  • โœ… Search codebase for existing types before creating new ones
  • โœ… Extend/compose existing types rather than duplicating

Type Import Pattern:

typescript
// โœ… CORRECT - Single source of truth
import { BlueprintType, WebSocketMessage } from '@/api-types';

// โŒ WRONG - Direct worker imports in frontend
import { BlueprintType } from 'worker/agents/schemas';

**2. DRY Principle**

  • Search for similar functionality before implementing
  • Extract reusable utilities, hooks, and components
  • Never copy-paste code - refactor into shared functions

**3. Follow Existing Patterns**

  • Frontend APIs: All defined in /src/lib/api-client.ts
  • Backend Routes: Controllers in worker/api/controllers/, routes in worker/api/routes/
  • Database Services: In worker/database/services/
  • Types: Shared types in shared/types/, API types in src/api-types.ts

**4. File Naming Conventions**

  • React Components: PascalCase.tsx
  • Utilities/Hooks: kebab-case.ts
  • Backend Services: PascalCase.ts
  • Match the naming style of surrounding files

**5. Code Quality Standards**

  • โœ… Production-ready code only - no TODOs or placeholders
  • โœ… Proper TypeScript types with no implicit any
  • โœ… Clean, maintainable code
  • โŒ No hacky workarounds
  • โŒ No overly verbose AI-like comments
  • โŒ No emojis in code (only in markdown docs)

**6. Comments Style**

typescript
// โœ… GOOD - Explains code's purpose
// Calculate exponential backoff with max cap
const delay = Math.min(Math.pow(2, attempt) * 1000, 30000);

// โŒ BAD - Verbose AI narration
// Here we are calculating the delay using exponential backoff...

๐Ÿ—๏ธ Project Architecture

**Tech Stack**

  • Frontend: React 18, TypeScript, Vite, TailwindCSS, React Router v7
  • Backend: Cloudflare Workers, Durable Objects, D1 (SQLite)
  • AI/LLM: OpenAI, Anthropic, Google AI Studio (Gemini)
  • WebSocket: PartySocket for real-time communication
  • Sandbox: Custom container service with CLI tools
  • Templates: Project scaffolding system with template catalog

**Complete Directory Structure**

text
/* Detailed source-code truncated for AI context efficiency. */

๐Ÿ’ฌ Chat View Architecture

**Core Component:** `/src/routes/chat/chat.tsx`

Layout:

  • Left Panel (40%): Chat messages, phase timeline, deployment controls, chat input
  • Right Panel (60%): Editor view, Preview iframe, or Blueprint markdown

**State Management:** `/src/routes/chat/hooks/use-chat.ts`

This hook manages all chat state:

typescript
{
  files: FileType[]              // Generated files
  phaseTimeline: PhaseTimelineItem[] // Phase progress
  messages: ChatMessage[]        // Chat history
  websocket: WebSocket           // Real-time connection
  isGenerating: boolean          // Generation state
  previewUrl: string             // Preview deployment URL
  // ... deployment, blueprint, bootstrap state
}

**WebSocket Message Handler**

Location: /src/routes/chat/utils/handle-websocket-message.ts

Critical Messages:

  • agent_connected - Restore full state on connect
  • conversation_state - Load chat history with deduplication
  • file_generating / file_generated - File generation progress
  • phase_implementing / phase_implemented - Phase progress
  • deployment_completed - Preview URL ready
  • conversation_response - AI message (streaming or complete)
  • generation_stopped - User cancelled, mark phases as "cancelled"

**Phase Timeline**

Component: /src/routes/chat/components/phase-timeline.tsx

Status States:

  • generating - Active (orange spinner)
  • validating - Code review (blue spinner)
  • completed - Success (green checkmark)
  • cancelled - Interrupted (orange X)
  • error - Failed (red alert)

**Message Deduplication**

Problem: Tool execution causes duplicate AI messages

Solution: Multi-layer approach

  1. Backend skips redundant LLM calls (empty tool results)
  2. Frontend utilities (deduplicate-messages.ts) for live and restored messages
  3. System prompt teaches LLM not to repeat

๐Ÿ”ง Backend Architecture

**Durable Objects Pattern**

Each chat session is a Durable Object instance:

typescript
class SimpleCodeGeneratorAgent implements DurableObject {
  // Persisted in SQLite
  private state: CodeGenState;
  
  // In-memory only (ephemeral)
  private currentAbortController?: AbortController;
  private deepDebugPromise: Promise<any> | null = null;
}

Key Concepts:

  • Persistent State: Stored in SQLite (blueprint, files, history)
  • Ephemeral State: In-memory (abort controllers, active promises)
  • Lifecycle: Created on-demand, evicted after inactivity
  • Concurrency: Single-threaded per DO instance

**CodeGenState - Agent Persistent State**

Location: /worker/agents/core/state.ts

Stored in Durable Object SQLite, survives page refreshes.

Key groups:

  1. Project Identity: blueprint (full PRD), projectName, original query, templateName
  2. File Management: generatedFilesMap (tracks all files with hash, modified time, uncommitted changes)
  3. Phase Tracking: generatedPhases (completed), currentPhase (active), phasesCounter
  4. State Machine: currentDevState (IDLE/PHASE_GENERATING/PHASE_IMPLEMENTING/REVIEWING/FINALIZING), shouldBeGenerating flag, mvpGenerated, reviewingInitiated
  5. Sandbox: sandboxInstanceId, commandsHistory, lastPackageJson
  6. Configuration: agentMode (deterministic/smart), sessionId, hostname
  7. Conversation: conversationMessages (chat history), pendingUserInputs, projectUpdatesAccumulator
  8. Debug: lastDeepDebugTranscript (for context in next debug session)

๐Ÿ“‹ Blueprint - Project Requirements Document

Location: /worker/agents/schemas.ts

The blueprint is the complete PRD generated from user's prompt. Contains:

  1. Identity: title, projectName (kebab-case), description
  2. Visual Design: colorPalette (RGB codes), views (screens/pages)
  3. User Experience: uiLayout, uiDesign, userJourney
  4. Technical Architecture: dataFlow, component structure
  5. Features: List of capabilities
  6. Phases: Ordered implementation steps with file paths and purposes
  7. Development Guide: pitfalls (common bugs to avoid), frameworks (allowed dependencies)
  8. Implementation Roadmap: High-level phases
  9. Initial Phase: First phase to implement with file list

Generation process:

  1. User submits query
  2. Template selection (LLM picks React/Vue/Next/etc.)
  3. Blueprint generation (large LLM call with full context)
  4. Bootstrap template files
  5. Initial phase implementation
  6. User can iterate or approve

Key principles:

  • Blueprint is source of truth for entire project
  • Modified via alter_blueprint tool
  • Pitfalls guide prevent common mistakes
  • Framework constraints limit dependencies

๐Ÿ“ฆ ADDITIONAL DIRECTORIES

container/

Purpose: Sandbox container tooling for local development and debugging

Location: /container/

Key files:

  1. cli-tools.ts - Command-line interface for container operations:

    • File synchronization
    • Command execution in sandbox
    • Log retrieval
    • Static analysis
    • Runtime error monitoring
  2. storage.ts - Persistent storage management:

    • Key-value store for container state
    • File system operations
    • Cache management
  3. process-monitor.ts - Process lifecycle monitoring:

    • Dev server health checks
    • Process restart on crashes
    • Resource usage tracking
    • Error collection
  4. types.ts - TypeScript types for container APIs

Usage:
These tools are used internally by the sandbox service and for local debugging. Not typically modified unless adding new sandbox features.


templates/

Purpose: Project template system for generating new apps

Location: /templates/

Key files:

  1. template_catalog.json - Master catalog of all available templates:

    • React (Vite, CRA)
    • Next.js
    • Vue
    • Svelte
    • Vanilla JS
      Each with metadata: name, description, files, dependencies
  2. definitions/ - Template definition files (not in repo, generated)

  3. zips/ - Compressed template archives for deployment

  4. generate_template_catalog.py - Builds catalog from template sources

  5. deploy_templates.sh - Deploys templates to production

  6. reference/ - Reference implementations

How templates work:

  1. Agent selects template based on user's requirements (React, Vue, etc.)
  2. Template provides base files (package.json, tsconfig, etc.)
  3. Agent generates additional files on top of template
  4. All files deployed to sandbox for preview

Adding new templates:

  1. Create template definition in definitions/
  2. Run python generate_template_catalog.py
  3. Test locally with agent
  4. Deploy: bash deploy_templates.sh

๐ŸŒณ GIT SYSTEM

Overview

Location: /worker/agents/git/

Vibesdk uses isomorphic-git to manage version control entirely in the browser/Worker environment - no git binary required.

Key features:

  • Git operations in SQLite (no filesystem needed)
  • Full commit history tracking
  • Git clone protocol support (clone generated repos)
  • Template rebasing for clean history

Git Architecture

text
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                   Agent (Durable Object)                    โ”‚
โ”‚  - Generates files                                          โ”‚
โ”‚  - Tracks changes                                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ”‚
                 โ†“ Calls
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      GitService                             โ”‚
โ”‚  - commitFiles()                                            โ”‚
โ”‚  - getCommitHistory()                                       โ”‚
โ”‚  - buildCloneRepository()                                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ”‚
                 โ†“ Uses
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    isomorphic-git                           โ”‚
โ”‚  - commit(), log(), readCommit()                            โ”‚
โ”‚  - Works with SQLite filesystem                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ”‚
                 โ†“ Stores in
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  SQLite Filesystem                          โ”‚
โ”‚  - fs-adapter.ts                                            โ”‚
โ”‚  - Git objects stored as blobs                              โ”‚
โ”‚  - Read/write via SQL queries                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Core Operations

**1. File Commits**

When: After each phase implementation, after fixes

Flow:

  1. Agent tracks file changes (new/modified)
  2. Calls GitService.commitFiles(files, message)
  3. Git service stages all files
  4. Creates commit with metadata:
    • Author: "vibesdk AI Agent"
    • Committer: same
    • Message: "Phase X: Feature Y" or "Fix: Bug Z"
    • Timestamp: current time
  5. Stores commit in SQLite
  6. Returns commit SHA

Example messages:

  • "Phase 1: Initial project setup"
  • "Phase 2: Add user authentication"
  • "Fix: Resolve type errors in UserStore"
  • "Update: Improve button styling"

**2. Commit History**

Used by: Git clone service, UI display

Flow:

  1. Query git log via isomorphic-git
  2. Returns commits with:
    • SHA, author, message, timestamp
    • Parent commits
    • Tree (file snapshot)
  3. Ordered newest first

**3. Git Clone Service**

Purpose: Allow users to clone their generated repos locally

Location: /worker/agents/git/git-clone-service.ts

Problem: Agent commits are separate from template commits. Users cloning would get disconnected history.

Solution: Rebase agent commits on top of template

Process:

  1. Create fresh repo in memory (MemFS)
  2. Template base commit:
    • Write all template files
    • Commit as "Initial template setup"
    • This becomes commit #0
  3. Import agent's git objects (skip refs)
  4. Replay each agent commit:
    • Read commit from agent's repo
    • Extract files from commit tree
    • Write files to memory repo (template + agent files)
    • Stage ALL files
    • Create new commit with:
      • Same message as original
      • Same author/committer
      • Same timestamps
      • Parent = previous rebased commit
  5. Generate packfile:
    • Collect ALL git objects
    • Create git packfile (binary format)
    • Return via HTTP (git clone protocol)

Result: Clean linear history starting from template

**4. Git Clone Protocol**

Endpoints:

  • GET /git/{agentId}/info/refs?service=git-upload-pack - Returns refs (branches/tags)
  • POST /git/{agentId}/git-upload-pack - Returns packfile for clone

Usage:

bash
git clone https://vibesdk.com/git/{agentId}

User gets complete repository with:
โœ… All commits with original messages
โœ… Full file history
โœ… Template base included
โœ… Can push to GitHub, modify locally, etc.


SQLite Filesystem Adapter

Location: /worker/agents/git/fs-adapter.ts

Purpose: Make SQLite look like a filesystem to isomorphic-git

Implementation:

  • Implements Node.js fs API (readFile, writeFile, readdir, stat, etc.)
  • Stores files as blobs in SQLite
  • Handles directory structures
  • Supports git object storage format

Why SQLite?

  • Durable Objects have SQLite built-in
  • Persistent across DO hibernation
  • Fast for git operations
  • No external filesystem needed

Memory Filesystem (MemFS)

Location: /worker/agents/git/MemFS.ts

Purpose: Ephemeral in-memory filesystem for clone service

Usage:

  • Git clone service builds repo in memory
  • Fast (no disk I/O)
  • Garbage collected after request completes
  • Full async API for isomorphic-git

Commit Preservation

When cloning, the following are preserved:

โœ… Commit messages - Exact text
โœ… Author info - Name, email
โœ… Timestamps - Original commit times
โœ… Committer info - Who made the commit
โœ… File states - Exact file content at each commit
โœ… Template files - Base template in all commits

โŒ NOT preserved:

  • Original commit SHAs (rebased, so new SHAs)
  • Agent's internal refs (branches reset)

Testing

Coverage: 140 tests passing

Test areas:

  • Basic git workflow (init, add, commit, log)
  • Template rebasing with multiple commits
  • Packfile generation
  • Large-scale operations (100+ commits)
  • File content integrity
  • Commit metadata preservation

GitVersionControl Class & Git Tool

**Overview**

Location: /worker/agents/git/git.ts

The GitVersionControl class wraps isomorphic-git with Git CLI-aligned semantics and provides callback support for FileManager synchronization.

**Key Methods**

**1. commit(files, message)**

Standard git commit - stages and commits files.

typescript
await git.commit([], 'feat: Add authentication');

**2. reset(ref, options?)**

Aligns with: git reset --hard <commit>

typescript
await git.reset('abc123', { hard: true });
// Moves HEAD to commit, updates working directory
// No new commit created (destructive)

Behavior:

  • Moves HEAD to specified commit
  • Updates working directory (hard: true by default)
  • Does NOT create a new commit
  • Triggers onFilesChangedCallback

**3. log(limit?)**

Query commit history - standard git log.

**4. show(oid)**

Show commit details - files changed in commit.

**5. setOnFilesChangedCallback(callback)**

Register callback to be notified after git operations that change files.

typescript
git.setOnFilesChangedCallback(() => {
  fileManager.syncGeneratedFilesMapFromGit();
});

**6. getAllFilesFromHead()**

Get all files from HEAD commit for syncing.

typescript
const files = await git.getAllFilesFromHead();
// Returns: [{ filePath: string, fileContents: string }]

**FileManager Sync Pattern**

File: /worker/agents/services/implementations/FileManager.ts

FileManager is self-contained - it registers with GitVersionControl during construction and auto-syncs after git operations.

typescript
constructor(stateManager, getTemplateDetailsFunc, git) {
  // Auto-register callback with git
  this.git.setOnFilesChangedCallback(() => {
    this.syncGeneratedFilesMapFromGit();
  });
}

private async syncGeneratedFilesMapFromGit(): Promise<void> {
  // Get all files from HEAD commit
  const gitFiles = await this.git.getAllFilesFromHead();
  
  // Preserve existing file purposes
  const oldMap = this.stateManager.getState().generatedFilesMap;
  
  // Build new map
  const newMap = {};
  for (const file of gitFiles) {
    newMap[file.filePath] = {
      ...file,
      filePurpose: oldMap[file.filePath]?.filePurpose || 'Generated file',
      lastDiff: ''
    };
  }
  
  // Update state
  this.stateManager.setState({ generatedFilesMap: newMap });
}

Flow:

  1. FileManager constructed โ†’ Registers callback with git
  2. User performs operations โ†’ Dual-write continues (map + git)
  3. User calls git reset/checkout โ†’ Git modifies files
  4. Git calls callback โ†’ FileManager.syncGeneratedFilesMapFromGit()
  5. Sync reads from HEAD โ†’ Updates generatedFilesMap
  6. State synchronized โœ…

**Git Tool - Access Control**

Location: /worker/agents/tools/toolkit/git.ts

The git tool has parameterized access control - different commands available in different contexts.

**Tool Creation**

typescript
export function createGitTool(
  agent: CodingAgentInterface,
  logger: StructuredLogger,
  options?: { excludeCommands?: GitCommand[] }
): ToolDefinition<...> {
  const allowedCommands = options?.excludeCommands
    ? allCommands.filter(cmd => !options.excludeCommands!.includes(cmd))
    : allCommands;
  
  // Dynamic enum and description based on allowed commands
  return {
    function: {
      enum: allowedCommands,
        ? "... WARNING: reset is destructive!"
        : "...",
    }
  };
}

**Access by Context**

Context Available Commands File Notes
User Conversations commit, log, show /worker/agents/tools/customTools.ts (line 56) โœ… Safe - no destructive ops
Deep Debugger commit, log, show, reset /worker/agents/tools/customTools.ts (line 71) โš ๏ธ Full access with warnings

User Conversations:

typescript
// Safe version - no reset
createGitTool(agent, logger, { excludeCommands: ['reset'] })

Deep Debugger:

typescript
// Full access - includes reset
createGitTool(session.agent, logger) // No restrictions

**Reset Command - Safety**

Deep debugger prompt warnings:

  • Marked as UNTESTED and DESTRUCTIVE
  • Only use when:
    • User explicitly requests it
    • Tried everything else
    • Absolutely certain it's necessary
  • Must warn user before using
  • Prefer alternatives: regenerate_file, generate_files

**Why This Architecture?**

โœ… Single implementation - DRY principle maintained
โœ… Type-safe - TypeScript enforces valid commands
โœ… Context-aware - Different access in different contexts
โœ… Flexible - Easy to add more restrictions
โœ… Safe default - Users can't accidentally reset commits
โœ… Git CLI semantics - Aligns with actual git behavior

**Removed Methods**

  • inferPurposeFromPath() - Removed as requested
  • revert() - Was creating incorrect "revert commits"
  • restoreCommit() - Renamed to internal helper readFilesFromCommit

**Why These Limits?**

MAX_PHASES = 12:

  • Prevents infinite generation loops
  • Forces focused, efficient implementation
  • Keeps projects manageable

MAX_TOOL_CALLING_DEPTH = 7:

  • Prevents infinite recursion
  • Typically need 2-3 levels max
  • Safety against runaway LLM behavior

MAX_IMAGES_PER_MESSAGE = 2:

  • Balance between utility and cost
  • Vision API costs are high
  • Usually 1-2 images sufficient for context

MAX_LLM_MESSAGES = 200:

  • Prevents conversation from growing unbounded
  • Compactification kicks in before this
  • Typical session has 20-50 messages

**State Machine - Detailed Flow**

States: Defined in CurrentDevState enum

text
/* Detailed source-code truncated for AI context efficiency. */

**State Transitions**

From To Trigger
IDLE PHASE_GENERATING User starts generation / Resume after queue
PHASE_GENERATING PHASE_IMPLEMENTING Phase plan ready
PHASE_IMPLEMENTING REVIEWING Files generated, deployed
REVIEWING IDLE Review complete (not looping back to PHASE_GENERATING)
FINALIZING REVIEWING After final fixes, review again
PHASE_IMPLEMENTING FINALIZING Last phase, no more phases needed
ANY IDLE User stops generation

**State Persistence**

  • State stored in CodeGenState.currentDevState
  • Survives page refreshes
  • Used to resume generation after reconnect
  • If shouldBeGenerating=true and state=IDLE โ†’ restart

๐Ÿง  Deep Debugger

**System Prompt & Configuration**

File: /worker/agents/assistants/codeDebugger.ts

  • System prompt defines behavior, diagnostic priorities, action-oriented instructions
  • Model: Gemini 2.5 Pro (reasoning_effort: high, 32k tokens, temperature: 0.2)
  • Max tool depth: 7 recursive calls

**Available Tools**

Tool Registry: /worker/agents/tools/customTools.ts โ†’ buildDebugTools() function (lines 59-87)

Tool Definitions Location: /worker/agents/tools/toolkit/

Each tool is a separate file:

  1. read-files.ts - Read source code
  2. run-analysis.ts - TypeScript static analysis (tsc --noEmit)
  3. get-runtime-errors.ts - Fetch runtime errors from sandbox
  4. get-logs.ts - Container logs (dev server, console)
  5. regenerate-file.ts - Fix single file with issues list
  6. generate-files.ts - Generate multiple files
  7. deploy-preview.ts - Deploy to sandbox
  8. exec-commands.ts - Run shell commands
  9. wait.ts - Wait N seconds (for user interaction)
  10. git.ts - Version control (commit, log, show, reset)

**How Tools Work**

Each tool file exports:

typescript
// Structure in /worker/agents/tools/toolkit/{tool-name}.ts
export function createToolName(agent: CodingAgentInterface, logger: StructuredLogger) {
  return {
    type: 'function',
    function: {
      name: 'tool_name',
      parameters: { /* JSON schema */ }
    },
    implementation: async (args) => {
      // Tool logic here
      return result;
    }
  };
}

**To Add a New Tool:**

  1. Create /worker/agents/tools/toolkit/my-tool.ts
  2. Export createMyTool(agent, logger) function
  3. Import in /worker/agents/tools/customTools.ts
  4. Add to either buildTools() (conversation) or buildDebugTools() (debugging)
  5. Tool automatically available to LLM

**Diagnostic Priority (in system prompt)**

  1. run_analysis first (fast, no user interaction needed)
  2. get_runtime_errors second (focused errors)
  3. get_logs last resort (verbose, cumulative)

Can fix multiple files in parallel - regenerate_file called simultaneously on different files

Concurrency: Cannot run while code generation active - checked via agent.isCodeGenerating()


๐Ÿ”Œ WebSocket Communication

**Connection Flow**

  1. User visits /chat/:chatId
  2. Frontend calls apiClient.connectToAgent(chatId)
  3. API returns websocketUrl
  4. Frontend connects via PartySocket
  5. Backend sends agent_connected with full state
  6. Frontend restores UI

**State Restoration**

typescript
case 'agent_connected': {
  // Backend sends snapshot
  setState(message.state);
  websocket.send({ type: 'get_conversation_state' });
}

case 'conversation_state': {
  // Restore with deduplication
  const deduplicated = deduplicateMessages(message.messages);
  setMessages(prev => [...prev, ...deduplicated]);
}

**Streaming Pattern**

typescript
// Backend sends chunks
ws.send({
  type: 'conversation_response',
  conversationId: 'abc',
  message: 'chunk',
  isStreaming: true,
});

// Frontend updates in place
setMessages(prev => updateOrAppendMessage(prev, id, content));

๐Ÿ› ๏ธ Implementation Patterns

**Adding New API Endpoint**

1. Define types (src/api-types.ts):

typescript
export interface GetFeatureRequest {
  id: string;
}

export interface GetFeatureResponse {
  feature: Feature;
}

2. Add to API client (src/lib/api-client.ts):

typescript
export const apiClient = {
  async getFeature(req: GetFeatureRequest): Promise<GetFeatureResponse> {
    const response = await fetch('/api/features', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(req),
    });
    if (!response.ok) throw new ApiError(response);
    return response.json();
  },
};

3. Create service (worker/database/services/FeatureService.ts):

typescript
export class FeatureService {
  constructor(private env: Env) {}
  
  async getFeature(id: string): Promise<Feature> {
    // Database logic
  }
}

4. Create controller (worker/api/controllers/feature/controller.ts):

typescript
export const featureController = {
  async getFeature(c: Context<AppEnv>) {
    const body = await c.req.json<GetFeatureRequest>();
    const service = new FeatureService(c.env);
    const feature = await service.getFeature(body.id);
    return c.json({ feature });
  },
};

5. Add route (worker/api/routes/feature-routes.ts):

typescript
export const featureRoutes = new Hono<AppEnv>();
featureRoutes.post('/', featureController.getFeature);

6. Register in main router (worker/api/routes/index.ts):

typescript
router.route('/api/features', featureRoutes);

**Creating Custom Hook**

Pattern: /src/hooks/use-{feature}.ts

typescript
export function useFeature(params: FeatureParams) {
  const [data, setData] = useState<FeatureData | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  
  useEffect(() => {
    async function fetch() {
      try {
        const result = await apiClient.getFeature(params);
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    }
    fetch();
  }, [params]);
  
  const refetch = useCallback(() => {
    setLoading(true);
    // ... refetch logic
  }, [params]);
  
  return { data, loading, error, refetch };
}

**Adding LLM Tool**

1. Create tool file (worker/agents/tools/toolkit/my-tool.ts):

typescript
export function createMyToolDefinition() {
  return {
    type: 'function' as const,
    function: {
      name: 'my_tool',
      parameters: {
        type: 'object',
        properties: {
          param: { type: 'string', description: 'Param description' },
        },
        required: ['param'],
      },
    },
  };
}

export async function myToolImplementation(
  args: { param: string },
  context: ToolContext,
  streamCb?: StreamCallback
): Promise<ToolResult> {
  // Check concurrency if needed
  if (context.agent.isCodeGenerating()) {
    return { error: 'GENERATION_IN_PROGRESS' };
  }
  
  // Implementation
  const result = await doWork(args.param);
  
  return { result };
}

2. Register tool (worker/agents/tools/customTools.ts):

typescript
import { createMyToolDefinition, myToolImplementation } from './toolkit/my-tool';

export function buildTools(agent: CodingAgentInterface) {
  return [
    // ... existing tools
    createTool(createMyToolDefinition(), myToolImplementation),
  ];
}

๐Ÿงช Testing Patterns

**Frontend Tests**

  • Component tests in __tests__/ directories
  • Integration tests for hooks
  • E2E tests with Playwright (if applicable)

**Backend Tests**

  • Unit tests for services
  • Integration tests for API endpoints
  • Tool execution tests

๐Ÿ“ Documentation Standards

**Code Comments**

  • Explain WHY, not WHAT (code should be self-documenting)
  • Keep comments brief and to the point
  • Update comments when code changes
  • No emojis in code comments

**Type Documentation**

typescript
// โœ… GOOD
/**
 * Represents a generated file in the chat interface.
 * Contains both metadata and content.
 */
export interface FileType {
  filePath: string;
  fileContents: string;
  isGenerating: boolean;
}

// โŒ BAD
// This is a file type that we use to represent files
export interface FileType { ... }

๐Ÿ” Debugging Guide

**Frontend Debugging**

  • Use React DevTools for component state
  • Check browser console for errors
  • Monitor WebSocket messages in Network tab
  • Use Debug Panel in chat interface

**Backend Debugging**

  • Check Cloudflare Workers logs
  • Use wrangler tail for live logs
  • Add strategic console.log with prefixes: [TOOL_CALL_DEBUG], [WS_DEBUG]
  • Check DO storage for persisted state

**Common Issues**

Empty Deep Debug Transcript:

  • Check max_tokens is sufficient (32000+)
  • Verify tool calls are completing
  • Check for abort signals

Duplicate Messages:

  • Verify deduplication utilities are used
  • Check backend history management
  • Ensure tool results aren't causing re-calls

WebSocket Disconnects:

  • Check retry logic in use-chat.ts
  • Verify DO isn't being evicted prematurely
  • Check for abort controller issues

๐Ÿ“ฆ Deployment

**Frontend**

  • Built with Vite
  • Deployed as static assets
  • Served by Cloudflare Pages or Workers

**Backend**

  • Deployed via Wrangler
  • Durable Objects for stateful agents
  • D1 for persistent database
  • KV for caching (if used)

**Database Migrations**

bash
# Generate migration
npm run db:generate

# Apply migrations (local)
npm run db:migrate:local

# Apply migrations (production)
npm run db:migrate:remote

๐Ÿ”„ Continuous Improvement

**Keep This Document Updated**

When you:

  • Add new features or components
  • Discover undocumented patterns
  • Find inaccuracies or outdated info
  • Learn domain-specific knowledge
  • Identify new best practices

Update sections:

  • Add to relevant section
  • Create new section if needed
  • Mark outdated info with โš ๏ธ and correction
  • Add examples for clarity
  • Keep it concise but complete

**Document Structure**

This guide is organized by:

  1. Core principles (rules that never change)
  2. Architecture (how things are structured)
  3. Patterns (how to implement features)
  4. Examples (concrete implementations)

Keep this structure when adding content.


๐Ÿ”Œ WebSocket Communication - Complete Reference

**WebSocket Message Types**

Location: /worker/agents/constants.ts

**Request Messages (Frontend โ†’ Backend):**

typescript
WebSocketMessageRequests:
- GENERATE_ALL: 'generate_all'           // Start code generation
- DEPLOY: 'deploy'                       // Deploy to Cloudflare Workers
- PREVIEW: 'preview'                     // Deploy to sandbox preview
- STOP_GENERATION: 'stop_generation'     // Cancel current operation
- RESUME_GENERATION: 'resume_generation' // Resume paused generation
- USER_SUGGESTION: 'user_suggestion'     // User message (conversational AI)
- CLEAR_CONVERSATION: 'clear_conversation' // Reset chat history
- GET_CONVERSATION_STATE: 'get_conversation_state' // Request history
- GET_MODEL_CONFIGS: 'get_model_configs' // Request model info
- CAPTURE_SCREENSHOT: 'capture_screenshot' // Capture preview screenshot
- GITHUB_EXPORT: 'github_export'         // DEPRECATED - use OAuth flow

**Response Messages (Backend โ†’ Frontend):**

text
/* Detailed source-code truncated for AI context efficiency. */

**WebSocket Message Flow Examples**

**1. Code Generation Flow:**

text
User clicks "Generate" button
  โ†“
Frontend: GENERATE_ALL
  โ†“
Backend: generation_started
  โ†“
[For each phase]
  Backend: phase_generating (LLM thinking)
  Backend: phase_generated (plan ready)
  Backend: phase_implementing (files starting)
  [For each file]
    Backend: file_generating
    Backend: file_chunk_generated (streaming)
    Backend: file_generated
  Backend: deployment_started
  Backend: deployment_completed (preview URL)
  Backend: code_reviewing (static analysis)
  Backend: code_reviewed (results)
  Backend: phase_implemented (phase done)
  โ†“
Backend: generation_complete

**2. User Conversation Flow:**

text
User types message โ†’ clicks send
  โ†“
Frontend: USER_SUGGESTION { message, images? }
  โ†“
Backend: conversation_response { isStreaming: true } (chunks)
  โ†“
[If tool calls]
  Backend: conversation_response { tool: { name, status: 'start' } }
  Backend: conversation_response { tool: { name, status: 'success', result } }
  โ†“
Backend: conversation_response { isStreaming: false } (final)

**3. Abort Generation Flow:**

text
User clicks abort button
  โ†“
Frontend: STOP_GENERATION
  โ†“
Backend: 
  - Calls agent.cancelCurrentInference()
  - Aborts active AbortController
  - Sets shouldBeGenerating = false
  โ†“
Backend: generation_stopped
  โ†“
Frontend:
  - Marks active phases as 'cancelled'
  - Shows orange X icon
  - Disables abort button

**Critical State Flags**

**`shouldBeGenerating` Flag**

Purpose: Persistent intent to generate code, survives page refreshes.

When set to true:

  • User clicks "Generate" button
  • User resumes generation

When set to false:

  • User clicks "Stop" button
  • Generation completes successfully
  • Generation fails permanently

Why it matters:

  • On page refresh, if shouldBeGenerating=true and no active generation โ†’ restart
  • Prevents abandoned generation sessions
  • Used by frontend to show "generating" vs "cancelled" phases

๐Ÿค– Conversational AI System ("Orange")

**Purpose**

Orange is the AI interface between users and the development agent. It handles:

  • User questions and discussions
  • Feature/bug requests (via queue_request tool)
  • Immediate debugging (via deep_debug tool)
  • Web searches for information

**System Prompt Philosophy**

CRITICAL: Orange speaks AS IF it's the developer:

  • โœ… "I'll add that feature"
  • โœ… "I'm fixing that bug"
  • โŒ NEVER: "The team will...", "The agent will..."

Two Options for User Requests:

  1. Immediate Action (deep_debug):

    • For active bugs needing instant fixes
    • Transfers control to autonomous debug agent
    • Returns transcript after completion
    • User sees real-time progress
  2. Queued Implementation (queue_request):

    • For features or non-urgent fixes
    • Relays to development agent
    • Implemented in next phase
    • Tell user: "I'll have that in the next phase or two"

**Available Tools**

Location: /worker/agents/tools/customTools.ts โ†’ buildTools()

typescript
1. queue_request: Queue modification requests
2. get_logs: Fetch sandbox logs (USE SPARINGLY)
3. deep_debug: Autonomous debugging (immediate fixes)
4. git: Version control (commit, log, show) - Safe version without reset
5. wait_for_generation: Wait for code generation
6. wait_for_debug: Wait for debug session
7. deploy_preview: Redeploy sandbox
8. clear_conversation: Clear chat history
9. rename_project: Rename the project
10. alter_blueprint: Modify blueprint fields
11. web_search: Search the web
12. feedback: Submit platform feedback

Note: User conversations get safe git tool (no reset command). Deep debugger gets full git tool (includes reset with warnings).

**Tool Call Rendering**

Pattern:

typescript
// Tool calls appear as expandable UI in chat messages
conversation_response {
  tool: {
    name: 'deep_debug',
    status: 'start' | 'success' | 'error',
    args: { issue: "..."},
    result: "transcript or error"
  }
}

Frontend displays:

  • Tool name with icon
  • Status indicator (spinner/check/alert)
  • Expandable arguments
  • Expandable result (for deep_debug, shows full transcript)

**Conversation History Management**

Two-Tier Storage:

  1. Running History (Compact):

    • Used for LLM context
    • Size-limited for token efficiency
    • Can be archived/summarized
    • Stored in compact_conversations table
  2. Full History:

    • Complete conversation log
    • Used for UI restoration
    • Never truncated
    • Stored in full_conversations table

Compactification:

typescript
COMPACTIFICATION_CONFIG:
- MAX_TURNS: 40 conversation turns
- MAX_ESTIMATED_TOKENS: 100,000 tokens
- PRESERVE_RECENT_MESSAGES: 10 messages always kept
- CHARS_PER_TOKEN: 4 (estimation)

Update Pattern:

typescript
addConversationMessage(message) {
  // Update or append to both histories
  if (exists) {
    // Update existing (for streaming)
    runningHistory[index] = message;
  } else {
    // Append new message
    runningHistory.push(message);
  }
  // Same for fullHistory
  save();
}

๐Ÿ› ๏ธ Tool System Architecture

**Tool Definition Pattern**

Location: /worker/agents/tools/toolkit/{tool-name}.ts

typescript
// 1. Define tool schema
export function createMyToolDefinition() {
  return {
    type: 'function' as const,
    function: {
      name: 'my_tool',
      parameters: {
        type: 'object',
        properties: {
          param: { 
            type: 'string', 
          }
        },
        required: ['param'],
      },
    },
  };
}

// 2. Implement tool logic
export async function myToolImplementation(
  args: { param: string },
  context: ToolContext,
  streamCb?: StreamCallback
): Promise<ToolResult> {
  // Validation
  if (!args.param) {
    return { error: 'Missing required parameter' };
  }
  
  // Concurrency checks (if needed)
  if (context.agent.isCodeGenerating()) {
    return { error: 'GENERATION_IN_PROGRESS' };
  }
  
  // Execute tool logic
  const result = await doWork(args.param);
  
  // Stream progress if callback provided
  streamCb?.('Processing...');
  
  return { result };
}

**Tool Registration**

For Conversation Tools:

typescript
// File: /worker/agents/tools/customTools.ts
import { createMyToolDefinition, myToolImplementation } from './toolkit/my-tool';

export function buildTools(
  agent: CodingAgentInterface,
  logger: StructuredLogger,
  toolRenderer: RenderToolCall,
  streamCb: (chunk: string) => void
): ToolDefinition[] {
  return [
    // ... existing tools
    createTool(createMyToolDefinition(), myToolImplementation),
  ];
}

For Debug Tools:

typescript
export function buildDebugTools(
  session: DebugSession,
  logger: StructuredLogger
): ToolDefinition[] {
  return [
    createReadFilesTool(session.agent, logger),
    createRunAnalysisTool(session.agent, logger),
    createRegenerateFileTool(session.agent, logger),
    // ... more debug-specific tools
  ];
}

**Tool Lifecycle Hooks**

typescript
const tool = {
  function: {...},
  implementation: async (args) => {...},
  
  // Optional hooks for UI feedback
  onStart: (args) => {
    toolRenderer({ 
      name: 'my_tool', 
      status: 'start', 
      args 
    });
  },
  
  onComplete: (args, result) => {
    toolRenderer({ 
      name: 'my_tool', 
      status: 'success', 
      args,
      result: JSON.stringify(result)
    });
  }
};

๐Ÿ—„๏ธ Database Schema Overview

**Core Tables**

Location: /worker/database/schema.ts

**1. Users Table**

typescript
users {
  id: text (PK)
  email: text (unique)
  username: text (unique, nullable)
  displayName: text
  avatarUrl: text
  provider: 'github' | 'google' | 'email'
  providerId: text
  passwordHash: text (for email provider)
  
  // Security
  emailVerified: boolean
  failedLoginAttempts: number
  lockedUntil: timestamp
  
  // Preferences
  theme: 'light' | 'dark' | 'system'
  timezone: text
  
  // Status
  isActive: boolean
  isSuspended: boolean
  
  // Timestamps
  createdAt, updatedAt, lastActiveAt, deletedAt
}

**2. Apps Table**

typescript
apps {
  id: text (PK)
  title: text
  iconUrl: text
  
  // Generation
  originalPrompt: text
  finalPrompt: text
  framework: text
  
  // Ownership
  userId: text (FK โ†’ users, nullable for anonymous)
  sessionToken: text (for anonymous)
  
  // Visibility
  visibility: 'private' | 'public'
  status: 'generating' | 'completed'
  
  // Deployment
  deploymentId: text
  githubRepositoryUrl: text
  
  // Metadata
  isArchived: boolean
  isFeatured: boolean
  version: number
  parentAppId: text (for forks)
  screenshotUrl: text
  
  // Timestamps
  createdAt, updatedAt, lastDeployedAt
}

**3. Sessions Table**

typescript
sessions {
  id: text (PK)
  userId: text (FK โ†’ users)
  
  // Session data
  deviceInfo: text
  userAgent: text
  ipAddress: text
  
  // Security
  isRevoked: boolean
  accessTokenHash: text
  refreshTokenHash: text
  
  // Timing
  expiresAt: timestamp
  createdAt: timestamp
  lastActivity: timestamp
}

**4. Stars & Favorites**

typescript
stars {
  id: text (PK)
  userId: text (FK โ†’ users)
  appId: text (FK โ†’ apps)
  starredAt: timestamp
  
  // Unique constraint on (userId, appId)
}

favorites {
  id: text (PK)
  userId: text (FK โ†’ users)
  appId: text (FK โ†’ apps)
  createdAt: timestamp
  
  // Unique constraint on (userId, appId)
}

**5. Analytics Tables**

typescript
appViews {
  id: text (PK)
  appId: text (FK โ†’ apps)
  userId: text (FK โ†’ users, nullable)
  sessionId: text
  viewedAt: timestamp
  
  // Indexes for fast counting
}

userModelConfigs {
  id: text (PK)
  userId: text (FK โ†’ users)
  agentActionName: text
  
  // Model overrides
  modelName: text
  maxTokens: number
  temperature: number
  reasoningEffort: 'low' | 'medium' | 'high'
  fallbackModel: text
  
  // Unique per user+action
}

**Database Service Pattern**

typescript
// File: /worker/database/services/DomainService.ts
export class DomainService {
  private db: D1Database;
  
  constructor(env: Env) {
    this.db = env.DB;
  }
  
  async getItem(id: string): Promise {
    // Use Drizzle ORM for type safety
    const result = await this.db
      .select()
      .from(itemsTable)
      .where(eq(itemsTable.id, id))
      .get();
    
    if (!result) throw new ApiError(404, 'Not found');
    return result;
  }
  
  async createItem(data: CreateInput): Promise {
    // Insert with validation
    const id = generateId();
    await this.db
      .insert(itemsTable)
      .values({ id, ...data });
    
    return this.getItem(id);
  }
}

๐Ÿ“ธ Image Attachment System

**Supported Formats**

Location: /worker/types/image-attachment.ts

typescript
SUPPORTED_IMAGE_MIME_TYPES = [
  'image/png',
  'image/jpeg',
  'image/webp',
]

MAX_IMAGE_SIZE_BYTES = 10 * 1024 * 1024; // 10MB
MAX_IMAGES_PER_MESSAGE = 2;

**Image Flow**

text
1. User uploads/drags image
   โ†“
2. Frontend: Validate size/type
   โ†“
3. Frontend: Convert to base64
   โ†“
4. Frontend: Show preview
   โ†“
5. User sends message
   โ†“
6. Frontend โ†’ Backend: USER_SUGGESTION { message, images: [...] }
   โ†“
7. Backend: Upload to R2 storage
   โ†“
8. Backend: Pass to LLM with vision model
   โ†“
9. LLM: Analyze image + generate response

**Image Types**

typescript
// Raw upload from user
interface ImageAttachment {
  id: string;
  filename: string;
  mimeType: SupportedImageMimeType;
  base64Data: string; // Without data URL prefix
  size: number;
  dimensions?: { width: number; height: number };
}

// After R2 upload
interface ProcessedImageAttachment {
  mimeType: SupportedImageMimeType;
  base64Data?: string; // Optional, may be cleared after upload
  r2Key: string; // R2 storage key
  publicUrl: string; // Public URL
  hash: string; // Content hash
}

**Frontend Validation**

Location: /src/hooks/use-image-upload.ts

typescript
Validation checks:
1. File type in SUPPORTED_IMAGE_MIME_TYPES
2. File size โ‰ค MAX_IMAGE_SIZE_BYTES
3. Total images โ‰ค MAX_IMAGES_PER_MESSAGE

Rejection behavior:
- Show error toast
- Don't add to preview
- Log validation failure

**Backend Validation**

Location: /worker/agents/core/websocket.ts

typescript
case USER_SUGGESTION:
  if (images && images.length > MAX_IMAGES_PER_MESSAGE) {
    sendError(`Maximum ${MAX_IMAGES_PER_MESSAGE} images allowed`);
    return;
  }
  
  for (const image of images) {
    if (image.size > MAX_IMAGE_SIZE_BYTES) {
      sendError(`Image exceeds ${MAX_IMAGE_SIZE_BYTES / 1024 / 1024}MB`);
      return;
    }
  }

๐Ÿ” Authentication Guards

Location: /src/hooks/useAuthGuard.ts and useActionGuard.ts

Purpose: Protect actions requiring authentication (star, fork, etc.)

Flow:

  1. User clicks protected action (not authenticated)
  2. Guard shows auth modal
  3. User logs in via GitHub/Google OAuth
  4. OAuth callback creates session
  5. Redirects back with ?action=star parameter
  6. Frontend detects parameter, executes pending action
  7. Clears action parameter

Options: requireFullAuth (reject anonymous), actionContext ("to star this app"), onSuccess callback


๐Ÿš€ GETTING STARTED - Common Tasks

**Adding a New LLM Tool**

Steps:

  1. Create tool file: /worker/agents/tools/toolkit/my-new-tool.ts
  2. Structure:
typescript
import { CodingAgentInterface } from 'worker/agents/services/implementations/CodingAgent';
import { StructuredLogger } from '../../../logger';

export function createMyNewTool(agent: CodingAgentInterface, logger: StructuredLogger) {
  return {
    type: 'function' as const,
    function: {
      name: 'my_new_tool',
      parameters: {
        type: 'object',
        properties: {
          input: {
            type: 'string',
          }
        },
        required: ['input']
      }
    },
    implementation: async (args: { input: string }) => {
      logger.info('Tool called', { args });
      // Your logic here
      return { result: 'success' };
    }
  };
}
  1. Register in /worker/agents/tools/customTools.ts:
    • Import: import { createMyNewTool } from './toolkit/my-new-tool';
    • Add to buildTools() array (line 44): createMyNewTool(agent, logger),
  2. Tool is now available to conversation agent (Orange AI)

**Modifying Deep Debugger Behavior**

File: /worker/agents/assistants/codeDebugger.ts

System prompt sections:

  • Identity (lines 11-25): Who the debugger is
  • Available Tools (lines 27-70): Tool descriptions (keep concise!)
  • Required Workflow (lines 72-80): Step-by-step process
  • Diagnostic Priority (lines 82-110): When to use which tool
  • Action-Oriented (lines 112-120): Prevents "explain instead of do"
  • Common Pitfalls (lines 122-140): What NOT to do

To change tool priority: Edit "Diagnostic Priority" section

To add tool to debugger:

  1. Create tool in /worker/agents/tools/toolkit/
  2. Import in customTools.ts
  3. Add to buildDebugTools() function (line 59)
  4. Update debugger system prompt's "Available Tools" section

**Adding a New WebSocket Message Type**

Backend:

  1. Add type to /worker/agents/constants.ts:
    • WebSocketMessageRequests (client โ†’ server)
    • WebSocketMessageResponses (server โ†’ client)
  2. Handle in /worker/agents/core/websocket.ts โ†’ handleWebSocketMessage()
  3. Send via sendToConnection(connection, messageType, data)

Frontend:

  1. Add type to /src/api-types.ts โ†’ WebSocketMessage union
  2. Handle in /src/routes/chat/utils/handle-websocket-message.ts
  3. Update state in handler

**Modifying Database Schema**

Steps:

  1. Edit /worker/database/schema.ts
  2. Generate migration: npm run db:generate
  3. Review SQL in /migrations/{number}_*.sql
  4. Apply locally: npm run db:migrate:local
  5. Test changes
  6. Apply to production: npm run db:migrate:remote

Never: Manually edit migration files

**Adding a New Database Service Method**

Example: Add method to UserService

File: /worker/database/services/UserService.ts

typescript
export class UserService extends BaseService {
  // Existing methods...
  
  async getNewMethod(userId: string): Promise<ResultType> {
    // Use 'fresh' for user's own data
    const readDb = this.getReadDb('fresh');
    
    const result = await readDb
      .select()
      .from(schema.users)
      .where(eq(schema.users.id, userId));
      
    if (!result) throw new Error('Not found');
    return result;
  }
}

Call from controller:

typescript
const userService = new UserService(c.env);
const data = await userService.getNewMethod(userId);

**Understanding Agent State Machine**

Current state: Check agent.state.currentDevState

States: Defined in /worker/agents/core/state.ts โ†’ CurrentDevState enum

  • IDLE (0): No generation
  • PHASE_GENERATING (1): Planning next phase
  • PHASE_IMPLEMENTING (2): Generating files
  • REVIEWING (3): Code review
  • FINALIZING (4): Final touches

State machine logic: /worker/agents/core/simpleGeneratorAgent.ts โ†’ launchStateMachine() (line 856)

Operations:

  • Phase Generation: /worker/agents/operations/PhaseGeneration.ts
  • Phase Implementation: /worker/agents/operations/PhaseImplementation.ts
  • Code Review: /worker/agents/operations/PostPhaseCodeFixer.ts
  • Conversation: /worker/agents/operations/UserConversationProcessor.ts

**Finding Where Something is Implemented**

"Where is X implemented?"

Feature Primary Location
Agent state machine /worker/agents/core/simpleGeneratorAgent.ts โ†’ launchStateMachine()
Blueprint generation /worker/agents/operations/PhaseGeneration.ts โ†’ system prompt
File generation /worker/agents/operations/PhaseImplementation.ts
Chat messages /worker/agents/operations/UserConversationProcessor.ts
Deep debugging /worker/agents/assistants/codeDebugger.ts
Sandbox deployment /worker/agents/services/implementations/DeploymentManager.ts
Git operations /worker/agents/services/implementations/GitService.ts
WebSocket handling /worker/agents/core/websocket.ts โ†’ handleWebSocketMessage()
Database queries /worker/database/services/{Domain}Service.ts
API routes /worker/api/routes/*.ts
API controllers /worker/api/controllers/{domain}/controller.ts
Frontend API calls /src/lib/api-client.ts (ALL calls in one place)
Chat UI state /src/routes/chat/hooks/use-chat.ts
Phase timeline UI /src/routes/chat/components/phase-timeline.tsx
Auth guards /src/hooks/useAuthGuard.ts

๐Ÿ› ๏ธ WORKER SERVICES

Rate Limiting Service

Location: /worker/services/rate-limit/

Purpose: Prevent API abuse with bucketed sliding window rate limiting

**Architecture**

Storage Options:

  1. Durable Objects (Primary) - rateLimitDO.ts
    • Bucketed sliding window algorithm
    • Better consistency than KV
    • Per-key isolated storage
  2. KV Store (Fallback) - rateLimitKV.ts
    • Global edge cache
    • Eventual consistency

**Identifier Strategy**

typescript
// User-based (authenticated)
user:abc123

// Token-based (JWT hash)
token:sha256_hash_16_chars

// IP-based (anonymous)
ip:192.168.1.1

**Rate Limit Types**

Location: /worker/services/rate-limit/config.ts

  1. API_ENDPOINT - HTTP endpoint rate limit
  2. LLM_REQUEST - LLM inference rate limit
  3. AUTH_ATTEMPT - Login/signup attempts
  4. APP_CREATION - New app creation
  5. GITHUB_EXPORT - GitHub push operations

**Configuration Structure**

typescript
interface DORateLimitConfig {
  limit: number;        // Max requests per period
  period: number;       // Time window in seconds
  burst?: number;       // Burst allowance
  burstWindow?: number; // Burst time window
  bucketSize: number;   // Bucket size for sliding window
  dailyLimit?: number;  // Optional daily cap
}

**Usage**

typescript
// In API route
const allowed = await RateLimitService.enforce(
  env,
  request,
  RateLimitType.API_ENDPOINT,
  user
);

if (!allowed) {
  throw new RateLimitExceededError('Too many requests');
}

**LLM Model-Specific Rates**

Different models have different rate increments:

  • GPT-4o: 10 units
  • GPT-4o-mini: 1 unit
  • Claude Sonnet: 15 units
  • Gemini Pro: 20 units
  • Gemini Flash: 5 units

Why? Expensive models consume more quota to prevent abuse.


GitHub Service

Location: /worker/services/github/GitHubService.ts

Purpose: Export generated apps to GitHub repositories

**Key Operations**

1. Create Repository

typescript
static async createUserRepository(options: {
  token: string;           // User's GitHub PAT
  name: string;            // Repo name
  description?: string;
  private: boolean;        // Public or private
  auto_init?: boolean;     // Create with README
})

2. Push Generated Code

typescript
static async pushCodeToRepository({
  token,
  owner,
  repo,
  gitObjects,           // Agent's git objects
  templateDetails,      // Template base
  appQuery,            // Original user prompt
  branch = 'main'
})

Process:

  1. Build git repo with GitCloneService (rebases on template)
  2. Push all commits to GitHub via Octokit
  3. Add README with app description + Cloudflare deploy button
  4. Return repository URL

3. Add Deploy to Cloudflare Button

typescript
static async addCloudflareDeployButton({
  token,
  owner,
  repo,
  templateName
})

Appends markdown button to README for one-click Cloudflare deployment.


OAuth Service

Location: /worker/services/oauth/

Providers: Google, GitHub

**Base Pattern** (`base.ts`)

All providers extend BaseOAuthProvider:

typescript
abstract class BaseOAuthProvider {
  abstract getAuthorizationUrl(params): string;
  abstract exchangeCodeForToken(code, verifier): Promise<TokenResponse>;
  abstract getUserInfo(token): Promise;
}

**OAuth Flow**

Step 1: Generate Auth URL

typescript
const provider = OAuthProviderFactory.create('google', env);
const { url, state, codeVerifier } = await provider.getAuthorizationUrl({
  redirectUri: 'https://app.com/auth/callback',
  state: csrfToken,
  scopes: ['openid', 'email', 'profile']
});

// Store state + verifier in oauthStates table
// Redirect user to url

Step 2: Handle Callback

typescript
// Verify state (CSRF protection)
const storedState = await db.getOAuthState(state);
if (!storedState || storedState.used) throw new Error('Invalid state');

// Exchange code for token
const tokenData = await provider.exchangeCodeForToken(
  code,
  storedState.codeVerifier
);

// Get user info
const userInfo = await provider.getUserInfo(tokenData.access_token);

// Create or update user
const user = await authService.findOrCreateOAuthUser({
  provider: 'google',
  providerId: userInfo.id,
  email: userInfo.email,
  displayName: userInfo.name
});

// Create session
const session = await sessionService.createSession(user);

Step 3: Cleanup

typescript
// Mark state as used
await db.markOAuthStateUsed(state);

// Cleanup expired states (runs periodically)
await db.cleanupExpiredOAuthStates();

**PKCE (Proof Key for Code Exchange)**

Purpose: Prevent authorization code interception

Flow:

  1. Generate random codeVerifier (128 chars)
  2. Create codeChallenge = SHA256(codeVerifier)
  3. Send challenge in auth URL
  4. Store verifier in oauthStates table
  5. Send verifier in token exchange
  6. Provider verifies: SHA256(verifier) == challenge

Google Implementation:

  • Uses code_challenge_method=S256
  • Requires openid scope

GitHub Implementation:

  • Standard OAuth 2.0 (no PKCE)
  • Uses state for CSRF only

Analytics Service

Location: /worker/services/analytics/

Purpose: Track app views, stars, user activity

**Database Service**

File: /worker/database/services/AnalyticsService.ts

Key Operations:

1. Track View

typescript
await analyticsService.trackView(appId, userId, ipAddress);
  • Creates view record
  • Deduplicates by IP (1 view per IP per day)
  • Updates app.viewsCount

2. Star App

typescript
const result = await analyticsService.toggleStar(appId, userId);
// result: { starred: true } or { starred: false }
  • Adds/removes star
  • Updates app.starsCount
  • Returns new state

3. Get Activity Stats

typescript
const stats = await analyticsService.getUserActivity(userId, days = 30);
// Returns: appsCreated, totalViews, totalStars, recentActivity[]

**Ranking Impact**

Views and stars affect app rankings:

  • Popular: (views ร— 1) + (stars ร— 3) DESC
  • Trending: (recent_activity ร— 1000000 + recency_bonus) DESC

Cache Service

Location: /worker/services/cache/

Purpose: Cache expensive operations

**Cache Strategies**

1. Git Packfile Cache

typescript
// Cache generated packfiles for git clone
await cacheService.set(
  `git:packfile:${agentId}`,
  packfileBuffer,
  3600 // 1 hour TTL
);

2. Static Analysis Cache

typescript
// Cache TypeScript analysis results
await cacheService.set(
  `analysis:${fileHash}`,
  analysisResults,
  300 // 5 min TTL
);

3. Template Cache

typescript
// Cache template file trees
await cacheService.set(
  `template:${templateName}`,
  templateFiles,
  86400 // 24 hours
);

**Implementation**

Uses Cloudflare Cache API:

typescript
const cache = caches.default;
await cache.put(request, response);
const cached = await cache.match(request);

CSRF Protection

Location: /worker/services/csrf/

Purpose: Prevent cross-site request forgery

**Token Generation**

typescript
// Generate token for OAuth state
const csrfToken = await crypto.subtle.digest(
  'SHA-256',
  crypto.getRandomValues(new Uint8Array(32))
);

**Validation**

typescript
// In OAuth callback
if (callbackState !== storedState.state) {
  throw new SecurityError('CSRF token mismatch');
}

**Storage**

CSRF tokens stored in oauthStates table:

  • state column = CSRF token
  • expiresAt = 10 minutes
  • used flag prevents replay

User Secrets Store (Durable Object)

Location: /worker/services/secrets/

Purpose: Secure, encrypted storage for user API keys and secrets with key rotation support

**Architecture**

Storage: Durable Object with SQLite backend

  • One DO instance per user (userId as DO ID)
  • XChaCha20-Poly1305 encryption (AEAD)
  • Hierarchical key derivation: MEK โ†’ UMK โ†’ DEK
  • Key rotation metadata tracking

Core Components:

  1. UserSecretsStore (UserSecretsStore.ts) - Main DO class
  2. KeyDerivation (KeyDerivation.ts) - PBKDF2-based key derivation
  3. EncryptionService (EncryptionService.ts) - XChaCha20-Poly1305 encryption
  4. Types (types.ts) - Type definitions

**Key Features**

1. Hierarchical Key Derivation

text
Master Encryption Key (MEK)
    โ†“ PBKDF2 with userId salt
User Master Key (UMK)
    โ†“ PBKDF2 with secret-specific salt
Data Encryption Key (DEK) - unique per secret

2. Encryption

  • Algorithm: XChaCha20-Poly1305 (AEAD)
  • Unique salt per secret (16 bytes)
  • Unique nonce per encryption (24 bytes)
  • Authentication tag for integrity verification

3. Key Rotation

  • Tracks master key fingerprint (SHA-256)
  • Detects key changes automatically
  • Re-encrypts all secrets with new key
  • Maintains rotation statistics

4. Security Features

  • Access counting (tracks how many times secret accessed)
  • Secret expiration timestamps
  • Soft deletion (90-day retention)
  • Key preview masking (shows first/last 4 chars)

**Database Schema**

Tables:

sql
-- Main secrets table
CREATE TABLE secrets (
    id TEXT PRIMARY KEY,
    user_id TEXT NOT NULL,
    name TEXT NOT NULL,
    secret_type TEXT NOT NULL,
    encrypted_value BLOB NOT NULL,
    nonce BLOB NOT NULL,
    salt BLOB NOT NULL,
    key_preview TEXT NOT NULL,
    metadata TEXT,
    access_count INTEGER DEFAULT 0,
    created_at INTEGER NOT NULL,
    updated_at INTEGER NOT NULL,
    expires_at INTEGER,
    is_active INTEGER DEFAULT 1,
    key_fingerprint TEXT NOT NULL
);

-- Key rotation tracking
CREATE TABLE key_rotation_metadata (
    id INTEGER PRIMARY KEY DEFAULT 1,
    current_key_fingerprint TEXT NOT NULL,
    last_rotation_at INTEGER NOT NULL,
    rotation_count INTEGER DEFAULT 0
);

**API Methods (RPC - No Exceptions)**

Critical: All DO RPC methods return null or boolean on error, never throw exceptions.

typescript
// Store new secret
async storeSecret(request: StoreSecretRequest): Promise<SecretMetadata | null>

// Get decrypted value
async getSecretValue(secretId: string): Promise<SecretWithValue | null>

// List secrets (metadata only)
async listSecrets(): Promise<SecretMetadata[]>

// Update secret
async updateSecret(secretId: string, updates: UpdateSecretRequest): Promise<SecretMetadata | null>

// Delete secret (soft delete)
async deleteSecret(secretId: string): Promise

// Get key rotation info
async getKeyRotationInfo(): Promise<KeyRotationInfo>

**Type Definitions**

typescript
interface StoreSecretRequest {
    name: string;
    secretType: 'api_key' | 'oauth_token' | 'webhook_secret' | 'encryption_key' | 'other';
    value: string;
    metadata?: Record<string, unknown>;
    expiresAt?: number;
}

interface SecretMetadata {
    id: string;
    userId: string;
    name: string;
    secretType: string;
    keyPreview: string;
    metadata?: Record<string, unknown>;
    accessCount: number;
    createdAt: number;
    updatedAt: number;
    expiresAt?: number;
}

interface SecretWithValue {
    value: string;
    metadata: SecretMetadata;
}

interface KeyRotationInfo {
    currentKeyFingerprint: string;
    lastRotationAt: number;
    rotationCount: number;
    totalSecrets: number;
    secretsRotated: number;
}

**Usage Example**

typescript
// Get DO stub
const id = env.UserSecretsStore.idFromName(user.id);
const store = env.UserSecretsStore.get(id);

// Store secret
const metadata = await store.storeSecret({
    name: 'OpenAI API Key',
    secretType: 'api_key',
    value: 'sk-...',
    metadata: { provider: 'openai' }
});

if (!metadata) {
    throw new Error('Failed to store secret');
}

// Retrieve decrypted value
const secret = await store.getSecretValue(metadata.id);

if (!secret) {
    throw new Error('Secret not found or expired');
}

console.log(secret.value); // Decrypted value
console.log(secret.metadata.accessCount); // Incremented on each access

// List all secrets (no values)
const secrets = await store.listSecrets();

// Update secret
const updated = await store.updateSecret(metadata.id, {
    name: 'OpenAI API Key (Production)',
    expiresAt: Date.now() + 86400000 // 24 hours
});

// Delete secret
const deleted = await store.deleteSecret(metadata.id);

**Controller Integration**

Location: /worker/api/controllers/user-secrets/controller.ts

typescript
// Example: Get secret value
static async getSecretValue(
    request: Request,
    env: Env,
    ctx: ExecutionContext,
    context: RouteContext
): Promise<ControllerResponse<ApiResponse>> {
    const user = context.user!;
    const secretId = context.pathParams.secretId;
    
    const stub = this.getUserSecretsStub(env, user.id);
    const result = await stub.getSecretValue(secretId);
    
    if (!result) {
        return UserSecretsController.createErrorResponse(
            'Secret not found or has expired',
            404
        );
    }
    
    return UserSecretsController.createSuccessResponse(result);
}

**Key Rotation Process**

Automatic Detection:

  1. On DO initialization, checks current master key fingerprint
  2. Compares with stored fingerprint in database
  3. If different, triggers key rotation

Re-encryption:

typescript
async performKeyRotation() {
    // 1. Fetch all active secrets
    const secrets = this.ctx.storage.sql.exec(`
        SELECT * FROM secrets WHERE is_active = 1
    `);
    
    // 2. Decrypt with old key, encrypt with new key
    for (const secret of secrets) {
        const decrypted = await this.decrypt(secret.encrypted_value, ...);
        const encrypted = await this.encrypt(decrypted);
        // 3. Update in database atomically
    }
    
    // 4. Update rotation metadata
}

**Security Considerations**

โœ… Good Practices:

  • Master key stored in Worker environment variable
  • Unique salt per secret
  • AEAD encryption with integrity verification
  • Key rotation support
  • Soft deletion for recovery
  • Access tracking for audit

โš ๏ธ Important Notes:

  • DO RPC methods return null/boolean instead of throwing exceptions
  • Master key must be 64 hex characters (32 bytes)
  • Expired secrets automatically filtered from results
  • Soft deleted secrets retained for 90 days

**Testing**

Location: /test/worker/services/secrets/

Comprehensive test suite with 90+ tests (3 test files):

  • KeyDerivation.test.ts - 17 unit tests for key derivation
  • EncryptionService.test.ts - 18 unit tests for encryption/decryption
  • UserSecretsStore.test.ts - 55+ E2E tests for full DO lifecycle

Run tests:

bash
npm test test/worker/services/secrets
# Or with Bun:
bun run test:bun test/worker/services/secrets

Test Coverage:

  • CRUD operations
  • Encryption/decryption
  • Key rotation
  • Expiration handling
  • Concurrency (10 parallel operations)
  • Large scale (20+ secrets, 5KB values)
  • Data integrity verification
  • Error handling

**Configuration**

Wrangler Configuration:

jsonc
{
  "durable_objects": {
    "bindings": [
      {
        "name": "UserSecretsStore",
        "class_name": "UserSecretsStore"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v3",
      "new_sqlite_classes": ["UserSecretsStore"]
    }
  ]
}

๐ŸŽจ FRONTEND RENDERING PATTERNS

Component Architecture

**Atomic Design Structure**

Hierarchy:

text
1. Primitives (ui/) - shadcn/ui base components
   โ†“
2. Shared (shared/) - App-specific reusable components
   โ†“
3. Features (routes/) - Page-specific components
   โ†“
4. Pages (routes/*.tsx) - Full page views

**Example: Button Hierarchy**

typescript
// 1. Primitive: /components/ui/button.tsx
export const Button = forwardRef<HTMLButtonElement, ButtonProps>((
  { className, variant, size, ...props },
  ref
) => {
  return (
    
  );
});

// 2. Shared: /components/shared/AppCard.tsx
export function AppCard({ app }: { app: App }) {
  return (
    <Card>
      <CardHeader>
        <h3>{app.title}</h3>
      </CardHeader>
      <CardFooter>
         navigate(`/app/${app.id}`)}>
          View App
        
      </CardFooter>
    </Card>
  );
}

// 3. Feature: /routes/apps/apps-list.tsx
export function AppsList() {
  const { apps } = useApps();
  
  return (
    
      {apps.map(app => <AppCard key={app.id} app={app} />)}
    
  );
}

State Management Patterns

**1. Local State (useState)**

Use for: UI-only state (modals, dropdowns, form inputs)

typescript
const [isOpen, setIsOpen] = useState(false);
const [selectedFile, setSelectedFile] = useState<FileType | null>(null);

**2. Server State (Custom Hooks)**

Use for: Data from API

typescript
// /hooks/use-apps.ts
export function useApps(filters?: AppFilters) {
  const [apps, setApps] = useState<App[]>([]);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    apiClient.getApps(filters).then(setApps);
  }, [filters]);
  
  return { apps, loading, refetch };
}

// Usage
const { apps, loading } = useApps({ sortBy: 'popular' });

**3. Global State (Context)**

Use for: Cross-component shared state (auth, theme)

typescript
// /contexts/auth-context.tsx
const AuthContext = createContext<AuthContextType>(null!);

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  
  return (
    <AuthContext.Provider value={{ user, setUser }}>
      {children}
    </AuthContext.Provider>
  );
}

// Usage
const { user } = useAuth();

**4. WebSocket State (use-chat hook)**

Use for: Real-time agent state

Location: /src/routes/chat/hooks/use-chat.ts

Pattern:

typescript
export function useChat(chatId: string) {
  // Local state
  const [messages, setMessages] = useState<ChatMessage[]>([]);
  const [files, setFiles] = useState<FileType[]>([]);
  const [isGenerating, setIsGenerating] = useState(false);
  
  // WebSocket connection
  const [websocket, setWebSocket] = useState<WebSocket | null>(null);
  
  // Message handler
  useEffect(() => {
    if (!websocket) return;
    
    websocket.onmessage = (event) => {
      const message = JSON.parse(event.data);
      handleWebSocketMessage(message, {
        setMessages,
        setFiles,
        setIsGenerating,
        // ... other setters
      });
    };
  }, [websocket]);
  
  return {
    messages,
    files,
    isGenerating,
    websocket,
    sendMessage: (text) => {
      websocket?.send(JSON.stringify({ type: 'USER_MESSAGE', text }));
    }
  };
}

Rendering Optimization

**1. useMemo for Expensive Computations**

typescript
const sortedFiles = useMemo(() => {
  return files.sort((a, b) => a.path.localeCompare(b.path));
}, [files]);

**2. useCallback for Event Handlers**

typescript
const handleFileClick = useCallback((fileId: string) => {
  setSelectedFile(files.find(f => f.id === fileId));
}, [files]);

**3. React.memo for Pure Components**

typescript
export const FileTreeNode = memo(({ file, onSelect }: Props) => {
  return (
     onSelect(file.id)}>
      {file.name}
    
  );
});

**4. Virtual Scrolling for Large Lists**

typescript
// For 1000+ items
import { useVirtualizer } from '@tanstack/react-virtual';

const virtualizer = useVirtualizer({
  count: apps.length,
  getScrollElement: () => containerRef.current,
  estimateSize: () => 200, // Card height
});

Data Fetching Patterns

**1. Single Resource**

typescript
// /hooks/use-app.ts
export function useApp(appId?: string) {
  const [app, setApp] = useState<App | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  
  useEffect(() => {
    if (!appId) return;
    
    setLoading(true);
    apiClient.getApp(appId)
      .then(setApp)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [appId]);
  
  return { app, loading, error, refetch };
}

**2. Paginated List**

typescript
// /hooks/use-apps.ts
export function useApps(filters?: AppFilters) {
  const [apps, setApps] = useState<App[]>([]);
  const [hasMore, setHasMore] = useState(true);
  const currentPageRef = useRef(1);
  const isLoadingMoreRef = useRef(false);
  
  const fetchApps = useCallback(async (loadMore = false) => {
    if (isLoadingMoreRef.current) return;
    isLoadingMoreRef.current = true;
    
    const page = loadMore ? currentPageRef.current + 1 : 1;
    const result = await apiClient.getApps({ ...filters, page });
    
    if (loadMore) {
      setApps(prev => [...prev, ...result.apps]);
    } else {
      setApps(result.apps);
    }
    
    setHasMore(result.hasMore);
    currentPageRef.current = page;
    isLoadingMoreRef.current = false;
  }, [filters]);
  
  const loadMore = () => fetchApps(true);
  
  return { apps, hasMore, loadMore };
}

**3. Infinite Scroll**

typescript
const { apps, hasMore, loadMore } = useApps();

const observerRef = useRef();
const sentinelRef = useRef<HTMLDivElement>(null);

useEffect(() => {
  observerRef.current = new IntersectionObserver(entries => {
    if (entries[0].isIntersecting && hasMore) {
      loadMore();
    }
  });
  
  if (sentinelRef.current) {
    observerRef.current.observe(sentinelRef.current);
  }
  
  return () => observerRef.current?.disconnect();
}, [hasMore, loadMore]);

return (
  
    {apps.map(app => <AppCard key={app.id} app={app} />)}
     {/* Sentinel element */}
  
);

Form Handling

**Controlled Components**

typescript
const [formData, setFormData] = useState({
  title: '',
  isPublic: true
});

const handleChange = (field: keyof typeof formData) => (
  e: React.ChangeEvent<HTMLInputElement>
) => {
  setFormData(prev => ({ ...prev, [field]: e.target.value }));
};

const handleSubmit = async (e: FormEvent) => {
  e.preventDefault();
  await apiClient.createApp(formData);
};

return (
  <form onSubmit={handleSubmit}>
    
    Create
  </form>
);

**Form Validation**

typescript
const [errors, setErrors] = useState<Record<string, string>>({});

const validate = () => {
  const newErrors: Record<string, string> = {};
  
  if (!formData.title) {
    newErrors.title = 'Title is required';
  }
  if (formData.title.length < 3) {
    newErrors.title = 'Title must be at least 3 characters';
  }
  
  setErrors(newErrors);
  return Object.keys(newErrors).length === 0;
};

const handleSubmit = async (e: FormEvent) => {
  e.preventDefault();
  if (!validate()) return;
  
  await apiClient.createApp(formData);
};

**Simple Modal State**

typescript
const [isOpen, setIsOpen] = useState(false);

return (
  <>
     setIsOpen(true)}>Open Modal
    
    <Dialog open={isOpen} onOpenChange={setIsOpen}>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Modal Title</DialogTitle>
        </DialogHeader>
        {/* Modal content */}
      </DialogContent>
    </Dialog>
  </>
);
typescript
const [selectedApp, setSelectedApp] = useState<App | null>(null);

return (
  <>
    {apps.map(app => (
       setSelectedApp(app)}>
        Edit
      
    ))}
    
    {selectedApp && (
      <EditAppModal
        app={selectedApp}
        onClose={() => setSelectedApp(null)}
      />
    )}
  </>
);

Error Handling

**Error Boundary**

typescript
// /components/ErrorBoundary.tsx
export class ErrorBoundary extends Component {
  state = { hasError: false, error: null };
  
  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }
  
  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
    // Send to Sentry
  }
  
  render() {
    if (this.state.hasError) {
      return <ErrorFallback error={this.state.error} />;
    }
    return this.props.children;
  }
}

**API Error Handling**

typescript
try {
  const app = await apiClient.getApp(appId);
  setApp(app);
} catch (error) {
  if (error instanceof ApiError) {
    if (error.status === 404) {
      setError('App not found');
    } else if (error.status === 403) {
      setError('Access denied');
    } else {
      setError('Something went wrong');
    }
  }
}

๐Ÿ—๏ธ CORE AGENT SYSTEM (Durable Objects)

Overview

SimpleCodeGeneratorAgent is the brain of vibesdk - a Durable Object that orchestrates entire app generation lifecycle.

Key responsibilities:

  • Blueprint generation from user prompts
  • Phase-by-phase code generation
  • File management and versioning
  • Sandbox deployment and monitoring
  • Conversation handling
  • Debug session orchestration

Agent Operations (State Machine)

**1. Blueprint Generation**

Trigger: User submits initial prompt
Flow:

  1. LLM analyzes prompt โ†’ generates complete PRD (Blueprint)
  2. Blueprint includes: project structure, phases, tech stack, UI design, color palette
  3. Saved to state, shown to user for confirmation
  4. User can iterate or approve

**2. Phase Generation**

Trigger: User starts generation or requests new feature
Flow:

  1. Agent determines next phase from blueprint
  2. Uses PhaseGeneration operation to plan files
  3. Updates currentDevState = PHASE_GENERATING
  4. Generates phase concept (files to create, purposes)

**3. Phase Implementation**

Trigger: Phase concept ready
Flow:

  1. PhaseImplementation operation generates all files for phase
  2. Uses LLM with file generation tools
  3. Tracks progress per-file
  4. Updates generatedFilesMap with new files
  5. Commits to git (isomorphic-git in SQLite)
  6. Sets currentDevState = PHASE_IMPLEMENTING

**4. Code Review & Fixing**

Trigger: Phase complete, auto-triggered or user-requested
Flow:

  1. PostPhaseCodeFixer runs TypeScript static analysis
  2. Identifies type errors, missing imports, etc.
  3. Automatically fixes common issues (TS2304, TS2307, etc.)
  4. Re-analyzes until clean or max iterations
  5. Updates files in generatedFilesMap

**5. Deployment to Sandbox**

Trigger: Files ready, user clicks preview
Flow:

  1. DeploymentManager.deployToSandbox()
  2. Syncs all files to remote sandbox container
  3. Executes install commands (npm install, etc.)
  4. Starts dev server
  5. Returns preview URL
  6. Monitors health with periodic checks

**6. User Conversation**

Trigger: User sends message during generation
Flow:

  1. UserConversationProcessor handles chat
  2. Queues feature requests if generating
  3. Processes immediately if idle
  4. Has access to tools: queue_request, deep_debug, deploy, etc.
  5. Streams responses via WebSocket

**7. Deep Debugging**

Trigger: User reports bug or runtime error
Flow:

  1. Agent checks not currently generating (conflict prevention)
  2. DeepCodeDebugger assistant spawned
  3. Has access to: read files, static analysis, runtime errors, logs, regenerate files
  4. Iteratively diagnoses and fixes
  5. Saves transcript for context in next session
  6. Deploys fixes automatically

Agent Services (Delegation Pattern)

Agent delegates specific responsibilities to service classes:

Location: /worker/agents/services/implementations/

  1. FileManager - File CRUD, validation, deduplication
  2. DeploymentManager - Sandbox lifecycle, deployment, health checks
  3. GitService - Commit, history, clone service
  4. CodingAgent (Proxy) - Exposes agent methods to tools (runs in DO context)

Why services?

  • Separation of concerns
  • Testability
  • Code reuse
  • Clean interfaces

๐Ÿงช SANDBOX SYSTEM

Overview

Sandboxes are ephemeral containers that run user's generated apps in isolated environments.

Technology: Remote sandbox service (separate infrastructure)
Communication: HTTP API with bearer token auth
Lifecycle: Created on-demand, destroyed after inactivity


Sandbox Architecture

text
/* Detailed source-code truncated for AI context efficiency. */

Sandbox Operations

**1. Instance Creation**

Method: createInstance(templateName, projectName, webhookUrl?, envVars?)

Flow:

  1. Agent calls with template (react-vite, nextjs, etc.)
  2. Sandbox service spins up container
  3. Clones template from git
  4. Installs base dependencies
  5. Returns instanceId + preview URL

Response: { instanceId, url, status: 'ready' }

**2. File Synchronization**

Method: writeFiles(instanceId, files, commitMessage?)

Flow:

  1. Agent sends array of files: [{ path, content, encoding }]
  2. Sandbox writes to container filesystem
  3. Triggers hot reload if dev server running
  4. Optionally commits to git with message

Used for: Initial deployment, incremental updates, fixes

**3. Command Execution**

Method: executeCommands(instanceId, commands, timeout?)

Flow:

  1. Agent sends shell commands (npm install, npm run build, etc.)
  2. Sandbox executes in container
  3. Returns stdout, stderr, exit code
  4. Timeout after 60s default

Security: Commands validated/filtered before execution to prevent dangerous operations

**4. Static Analysis**

Method: getStaticAnalysis(instanceId)

Flow:

  1. Sandbox runs TypeScript compiler (tsc --noEmit)
  2. Collects all errors with file/line/column
  3. Returns structured error list

Used by: PostPhaseCodeFixer, deep debugger

**5. Runtime Error Monitoring**

Method: getRuntimeErrors(instanceId)

Flow:

  1. Sandbox monitors browser console errors
  2. Collects stack traces, error messages
  3. Deduplicates and categorizes
  4. Returns recent errors

Triggers: Websocket webhook to agent when errors occur

**6. Log Retrieval**

Method: getLogs(instanceId, lines?, filter?)

Flow:

  1. Returns recent console output from container
  2. Includes dev server logs, build output, console.log statements
  3. Filtered by pattern if provided

Note: Logs only appear when user interacts with app

**7. Instance Shutdown**

Method: shutdownInstance(instanceId)

Flow:

  1. Stops dev server
  2. Destroys container
  3. Frees resources

Auto-triggered: After 30 min inactivity or explicit user close


Session Management

Each agent has a sessionId that maps to a sandbox instance:

  • Stored in: CodeGenState.sessionId
  • Purpose: Ensures deployment goes to correct container
  • Reset on: Timeout errors, critical failures
  • Cached client: DeploymentManager caches sandbox client per session

Health Checks:

  • Periodic ping to sandbox every 30s
  • If unhealthy, resets sessionId
  • Forces redeployment on next attempt

๐Ÿš€ DEPLOYMENT FLOW

Complete Deployment Process

**Trigger:** User clicks "Preview" button

Step-by-step:

  1. Pre-deployment Validation

    • Check files exist in generatedFilesMap
    • Verify no generation in progress
    • Get or create sessionId
  2. Sandbox Instance Check

    • If no sandboxInstanceId: create new instance
    • If exists: check health status
    • If unhealthy: reset session, create new instance
  3. Create Instance (if needed)

    text
    โ†’ createInstance(templateName, projectName, webhookUrl)
    โ† { instanceId, url, status }
    โ†’ Save instanceId to state
  4. File Synchronization

    text
    โ†’ Collect all files from generatedFilesMap
    โ†’ Format as { path, content, encoding: 'utf-8' }[]
    โ†’ writeFiles(instanceId, files, "Deploy generated code")
    โ† { success: true, filesWritten: 42 }
  5. Package.json Sync

    text
    โ†’ Check if package.json changed
    โ†’ If changed: executeCommands(['npm install'])
    โ†’ Wait for completion (timeout: 60s)
    โ†’ Cache new package.json in state
  6. Bootstrap Commands (if needed)

    text
    โ†’ Execute commandsHistory (previously run user commands)
    โ†’ Validates/filters dangerous commands
    โ†’ Runs: npm install, setup scripts, etc.
  7. Start Dev Server

    text
    โ†’ Already running from instance creation
    โ†’ Or trigger via command if stopped
    โ†’ Monitor startup logs
  8. Health Check Loop

    text
    โ†’ setInterval(30s): ping sandbox
    โ†’ Check status endpoint
    โ†’ If unhealthy: log warning, may reset
  9. Return Preview URL

    text
    โ†’ Send URL to frontend via WebSocket
    โ†’ User can open in iframe or new tab
    โ†’ App is live and interactive

Redeployment (Incremental Updates)

When files change after initial deploy:

  1. Diff Detection

    • Compare file hashes in generatedFilesMap
    • Only sync changed files
  2. Partial Sync

    text
    โ†’ writeFiles(instanceId, [changedFiles])
    โ† Hot reload triggered automatically
  3. No Full Rebuild

    • Dev server hot reloads changes
    • Fast iteration (< 1s typically)

Deployment Errors & Recovery

Common errors:

  1. Timeout (60s)

    • Cause: npm install too slow, network issues
    • Recovery: Reset sessionId, retry with fresh instance
  2. Instance Not Found

    • Cause: Container crashed or evicted
    • Recovery: Create new instance, redeploy all files
  3. Command Execution Failed

    • Cause: Invalid package.json, dependency conflicts
    • Recovery: Show error to user, allow editing
  4. Health Check Failed

    • Cause: Dev server crashed, port conflict
    • Recovery: Reset session on next deploy attempt

๐Ÿค– LLM INFERENCE SYSTEM

Overview

Location: /worker/agents/inferutils/

Centralized inference engine that all operations use to call LLMs.

Key features:

  • Multi-provider support (OpenAI, Anthropic via Cloudflare AI Gateway)
  • Streaming responses
  • Tool calling with recursive execution
  • Retry logic with exponential backoff
  • Cancellation support (AbortController)
  • Token tracking

Inference Flow

text
Operation (PhaseImplementation, UserConversationProcessor, etc.)
        โ†“
   getOperationOptions() โ†’ InferenceContext
        โ†“
   executeInference(args, context)
        โ†“
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚   Retry Loop (max 3 attempts)  โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ†“
            infer(args)
                 โ†“
   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚  OpenAI SDK (via AI Gateway)   โ”‚
   โ”‚  - Model selection              โ”‚
   โ”‚  - Token streaming              โ”‚
   โ”‚  - Tool call parsing            โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ†“
          Tool calls present?
                 โ”‚
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        Yes              No
         โ”‚                โ”‚
         โ†“                โ†“
  Execute tools    Return response
  Recursive infer

Model Selection

Location: /worker/agents/inferutils/config.ts

Available models:

  1. GPT-4o - Fast, good for most tasks
  2. GPT-4o-mini - Cheapest, simple operations
  3. Claude 3.5 Sonnet - Best for complex reasoning
  4. Gemini 2.0 Flash - Fast, experimental
  5. Gemini 2.5 Pro - Highest quality, deep debugging

Selection by operation:

  • Blueprint generation: GPT-4o
  • Phase planning: GPT-4o
  • File generation: GPT-4o
  • Conversation: GPT-4o-mini
  • Deep debugging: Gemini 2.5 Pro (reasoning_effort: high)
  • Code review: GPT-4o

Streaming

When enabled:

  • User conversation responses
  • Deep debugger output
  • Real-time code generation feedback

How it works:

  1. LLM sends Server-Sent Events (SSE)
  2. infer() yields chunks via async generator
  3. Operation accumulates + forwards to WebSocket
  4. Frontend renders progressively

Tool Calling

Recursive execution:

  1. LLM response includes tool_calls array
  2. infer() executes each tool in parallel
  3. Results collected
  4. Filtered (empty/null results skipped)
  5. If results exist: call LLM again with tool outputs
  6. Repeat until LLM provides final response

Max depth: Configurable per operation


Retry Logic

Triggers retry:

  • Rate limit errors (429)
  • Network timeouts
  • Temporary API failures (5xx)

Does NOT retry:

  • Cancelled operations (AbortError)
  • Invalid API key (401)
  • Malformed requests (400)

Backoff: Exponential (1s, 2s, 4s)


Cancellation

Each operation gets AbortSignal:

text
User clicks stop button
     โ†“
WebSocket: STOP_GENERATION
     โ†“
agent.cancelCurrentInference()
     โ†“
AbortController.abort()
     โ†“
OpenAI SDK cancels HTTP request
     โ†“
infer() throws InferError('cancelled')
     โ†“
No retry, immediate propagation

Nested operations: Share same AbortController
Tool calls: All cancelled together


๐Ÿ” AUTHENTICATION & AUTHORIZATION SYSTEM

Overview

The auth system implements a comprehensive JWT-based authentication with OAuth 2.0 social login (Google, GitHub), session management, API keys, and security auditing. All auth operations are centralized through services that interact with D1 database.

Core Components:

  1. AuthService - Main authentication orchestrator
  2. SessionService - JWT session management with D1 persistence
  3. JWTUtils - Token creation, verification, signing
  4. OAuth Providers - Google & GitHub implementations with PKCE
  5. Middleware - Route protection and token extraction
  6. Security - Password hashing, rate limiting, audit logs

Auth Architecture Flow

text
/* Detailed source-code truncated for AI context efficiency. */

Key Database Tables

**users Table**

Stores user identity, OAuth provider info, preferences, and security settings.

Key fields:

  • Identity: id, email, username, displayName, avatarUrl
  • OAuth: provider (github/google/email), providerId, emailVerified
  • Security: passwordHash (email provider only), failedLoginAttempts, lockedUntil
  • Preferences: theme, timezone
  • Timestamps: createdAt, updatedAt, deletedAt (soft delete)

Indexed on: email, provider+providerId (unique), username

**sessions Table**

Manages JWT sessions with device tracking and revocation support.

Key fields:

  • Session ID, userId (FK to users)
  • Device tracking: deviceInfo, userAgent, ipAddress
  • Token hashes: accessTokenHash, refreshTokenHash (SHA-256)
  • Revocation: isRevoked, revokedAt, revokedReason
  • Expiry: expiresAt (default 3 days), lastActivity

Configuration: Max 5 sessions per user, 3 concurrent devices

**oauthStates Table**

Temporary storage for OAuth flow state tokens (CSRF protection).

Key fields:

  • state (unique CSRF token), provider (google/github)
  • codeVerifier (PKCE), redirectUri
  • isUsed (one-time use), expiresAt (10 minutes)

Security: Prevents CSRF attacks, implements PKCE flow

**apiKeys Table**

Stores hashed API keys for programmatic access.

Key fields:

  • name, keyHash (SHA-256), keyPreview
  • scopes (JSON array), isActive
  • Usage: lastUsed, requestCount
  • Optional: expiresAt

**authAttempts Table**

Audit log for all authentication attempts.

Purpose: Track login/register attempts, detect suspicious activity
Fields: identifier (email), attemptType, success, ipAddress, timestamp

**verificationOtps Table**

Email verification codes. The email-OTP verification flow has been removed (users are auto-verified on registration), so nothing reads or writes this table; the table is retained only to avoid a destructive migration.

Fields: email, otp (hashed), used, expiresAt (15 min)

**auditLogs Table**

Detailed audit trail for security events.

Fields: userId, entityType/entityId, action, oldValues/newValues (JSON), ipAddress, userAgent


AuthService - Core Operations

Location: /worker/database/services/AuthService.ts

Handles all authentication operations (login, register, OAuth) and delegates session management to SessionService.

**register() Flow**

  1. Validate email format and password strength (min 8 chars, mixed case, numbers)
  2. Check email doesn't already exist
  3. Hash password with bcrypt (12 rounds)
  4. Create user with emailVerified=true (no OTP currently)
  5. Auto-login: create session + generate JWT
  6. Log attempt to authAttempts table
  7. Return user + accessToken + sessionId

**login() Flow**

  1. Find user by email (case-insensitive), check not deleted
  2. Verify passwordHash exists
  3. Compare password with bcrypt.verify()
  4. Create session + generate JWT
  5. Log attempt (success/fail) with IP + user agent
  6. Return user + accessToken + sessionId

Security: Failed attempts logged, passwords never logged

**OAuth Flow**

Step 1: getOAuthAuthorizationUrl()

  1. Cleanup expired OAuth states
  2. Validate redirect URL (same-origin only)
  3. Generate CSRF state token + PKCE code verifier
  4. Store in oauthStates table (10 min expiry)
  5. Build authorization URL with state + code_challenge
  6. Return URL to redirect user to provider

Step 2: handleOAuthCallback()

  1. Verify state token (not used, not expired)
  2. Mark state as used
  3. Exchange code for tokens using PKCE verifier
  4. Fetch user info from provider
  5. Find or create user (update OAuth info if exists)
  6. Create session + JWT
  7. Return user + token + intended redirectUrl

Security: CSRF protected, PKCE prevents code interception, one-time state tokens

**Other Key Methods**

getUserForAuth(userId): Fetch user by ID (checks not deleted) - used by middleware

validateTokenAndGetUser(token): Complete pipeline: verify JWT signature โ†’ check expiration โ†’ fetch user โ†’ return user + sessionId


JWTUtils - Token Management

Location: /worker/utils/jwtUtils.ts

Singleton class for JWT operations using jose library.

Token payload contains: userId (sub), email, sessionId, type (access/refresh), iat/exp timestamps

Key operations:

  • createAccessToken() - Sign JWT with HS256, 3-day expiry
  • verifyToken() - Verify signature, check expiration, return payload
  • hashToken() - SHA-256 hash for database storage (security: prevents token leakage from DB breaches)

SessionService - Session Management

Location: /worker/database/services/SessionService.ts

Config: Max 5 sessions/user, 3-day TTL, max 3 concurrent devices

Key operations:

  1. createSession() - Cleanup old sessions (keep 5 most recent) โ†’ generate session ID โ†’ create JWT โ†’ hash token โ†’ extract request metadata (IP, user agent, Cloudflare headers) โ†’ store in D1

  2. revokeUserSession() - Mark session as revoked with reason

  3. revokeAllUserSessions() - Revoke all user sessions (for password change, security breach)

  4. getUserSessions() - List active sessions (not revoked, not expired)

  5. getUserSecurityStatus() - Analyze security: count active sessions + recent security events โ†’ calculate risk level (high: >5 events/24h or hijacking; medium: >2 events or >3 devices; low: normal)

  6. forceLogoutAllOtherSessions() - Delete all sessions except current (for suspected compromise)

  7. cleanupExpiredSessions() - Delete expired sessions (run via cron)


OAuth Providers

Location: /worker/services/oauth/

Abstract base class provides common OAuth 2.0 flow with PKCE.

PKCE Flow:

  1. Generate code_verifier (random 32 bytes)
  2. Hash to create code_challenge (SHA-256)
  3. Send challenge in authorization URL
  4. Provider stores challenge
  5. Exchange code + verifier for tokens
  6. Provider verifies: hash(verifier) === stored_challenge

Purpose: Prevents authorization code interception attacks

**Google OAuth**

  • Scopes: openid, email, profile
  • Fetches user info from Google API
  • Returns: id, email, name, picture, verified_email
  • Env vars: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET

**GitHub OAuth**

  • Scopes: read:user, user:email (minimal, no repo access)
  • Special handling: Email not always in /user endpoint, fetches from /user/emails if needed
  • Returns: id, email (primary verified), name, avatar_url
  • Env vars: GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET

Authentication Middleware

Location: /worker/middleware/auth/routeAuth.ts

Three auth levels:

  1. public - No auth required
  2. authenticated - Requires valid JWT
  3. owner-only - Requires ownership of resource (e.g., user can only edit their own apps)

Flow:

  1. Route declares auth level via setAuthLevel() middleware
  2. enforceAuthRequirement() checks:
    • Public: pass through
    • Authenticated/Owner: extract token โ†’ validate JWT โ†’ fetch user โ†’ check ownership if needed
  3. User injected into request context: c.set('user', user)
  4. Route handler executes with authenticated user

Token extraction priority: Authorization header (APIs) โ†’ Cookie (browser) โ†’ Query param (WebSocket)


Security Features

**Password Security**

  • Hashing: bcrypt with 12 rounds (~250ms, intentionally slow to prevent brute force)
  • Validation: Min 8 chars, mixed case, numbers, special chars, not common password, no sequential patterns (12345)
  • Strength scoring: 0-4 scale

**Rate Limiting**

  • User-configurable limits (default: 100 requests/min)
  • Separate limits for auth endpoints
  • Tracked per user/IP in Durable Objects or KV

**CSRF Protection**

  • OAuth state tokens: cryptographically random, 10-min expiry, one-time use
  • Verified on callback to prevent cross-site request forgery

**Session Security**

  • Tokens hashed (SHA-256) in database
  • 3-day expiry by default
  • Device + IP tracking
  • Max 5 sessions per user, 3 concurrent devices
  • Force logout feature for security incidents

**Audit Logging**

  • All auth attempts logged to authAttempts table
  • Includes: IP, user agent, timestamp, success/failure
  • Used for security analysis and anomaly detection

๐Ÿ—„๏ธ DATABASE LAYER

Overview

The database layer uses Cloudflare D1 (SQLite) with Drizzle ORM for type-safe queries. All database operations are abstracted through service classes that extend BaseService.

Key Technologies:

  • D1 Database: Serverless SQLite on Cloudflare's edge
  • Drizzle ORM: Type-safe SQL query builder
  • D1 Sessions API: Read replicas for lower latency
  • Migrations: SQL-based schema migrations

Database Architecture

text
/* Detailed source-code truncated for AI context efficiency. */

BaseService Pattern

Location: /worker/database/services/BaseService.ts

**Purpose**

Provides common database functionality to all domain services:

  • Database connection management
  • Read replica access (D1 Sessions API)
  • Type-safe where condition building
  • Error handling patterns
  • Logging

**Implementation**

typescript
abstract class BaseService {
  protected logger = createLogger(this.constructor.name);
  protected db: DatabaseService;
  protected env: Env;
  
  constructor(env: Env) {
    this.db = createDatabaseService(env);
    this.env = env;
  }
  
  // Direct database access (primary)
  protected get database() {
    return this.db.db;
  }
  
  // Read replica access (optimized latency)
  protected getReadDb(strategy: 'fast' | 'fresh' = 'fast') {
    return this.db.getReadDb(strategy);
  }
  
  // Build type-safe WHERE conditions
  protected buildWhereConditions(
    conditions: (SQL | undefined)[]
  ): SQL | undefined {
    const validConditions = conditions.filter(
      (c): c is SQL => c !== undefined
    );
    if (validConditions.length === 0) return undefined;
    if (validConditions.length === 1) return validConditions[0];
    return and(...validConditions);
  }
  
  // Standard error handling
  protected handleDatabaseError(
    error: unknown,
    operation: string,
    context?: Record<string, unknown>
  ): never {
    this.logger.error(`Database error in ${operation}`, { error, context });
    throw error;
  }
}

D1 Sessions API - Read Replicas

**What is D1 Sessions?**

Cloudflare D1 Sessions API provides read replicas for D1 databases distributed globally. This dramatically reduces latency for read queries by serving them from the nearest replica.

**Strategies**

Location: /worker/database/database.ts

typescript
class DatabaseService {
  getReadDb(strategy: 'fast' | 'fresh' = 'fast') {
    if (strategy === 'fast') {
      // Lowest latency - may be slightly stale
      return drizzle(this.env.DB, { ... });
    } else {
      // Latest data - may have higher latency
      return drizzle(this.env.DB.withSession({ strategy: 'fresh' }), { ... });
    }
  }
}

**When to Use Each Strategy**

**'fast' Strategy (Default)**

Use for:

  • Public app listings
  • Public app details
  • Analytics and stats
  • Search results
  • Any read-only public data

Benefits:

  • Lowest latency (served from nearest replica)
  • Suitable for data that can tolerate slight staleness (few seconds)
  • Most queries should use this

Example:

typescript
// Public apps - use fast replicas
async getPublicApps(options: PublicAppQueryOptions) {
  const readDb = this.getReadDb('fast');  // โ† Use fast strategy
  
  const apps = await readDb
    .select()
    .from(schema.apps)
    .where(eq(schema.apps.visibility, 'public'));
}

**'fresh' Strategy**

Use for:

  • User's own data (own apps, favorites)
  • Immediately after writes (read-after-write)
  • Auth/session validation
  • Account settings
  • Any data where staleness is unacceptable

Benefits:

  • Latest data from primary or recent replica
  • Ensures user sees their own changes immediately

Example:

typescript
// User's own apps - use fresh data
async getUserApps(userId: string) {
  const readDb = this.getReadDb('fresh');  // โ† Use fresh strategy
  
  const apps = await readDb
    .select()
    .from(schema.apps)
    .where(eq(schema.apps.userId, userId));
}

**NEVER Use Read Replicas For:**

โŒ Write operations - Always use primary (this.database)
โŒ Auth validation - Use primary to avoid security issues
โŒ Immediately after INSERT/UPDATE - Read from primary
โŒ Critical consistency - Password changes, payments, etc.


Drizzle ORM Patterns

**Basic Queries**

**SELECT**

typescript
// Simple select
const users = await db
  .select()
  .from(schema.users)
  .where(eq(schema.users.email, email));

// Select specific columns
const users = await db
  .select({
    id: schema.users.id,
    email: schema.users.email
  })
  .from(schema.users);

// With JOIN
const apps = await db
  .select({
    app: schema.apps,
    userName: schema.users.displayName
  })
  .from(schema.apps)
  .leftJoin(schema.users, eq(schema.apps.userId, schema.users.id));

**INSERT**

typescript
// Insert one
const [user] = await db
  .insert(schema.users)
  .values({
    id: generateId(),
    email: '[email protected]',
    displayName: 'User',
    createdAt: new Date()
  })
  .returning();

// Insert many
await db
  .insert(schema.apps)
  .values([
    { id: id1, title: 'App 1', ... },
    { id: id2, title: 'App 2', ... }
  ]);

**UPDATE**

typescript
await db
  .update(schema.users)
  .set({ 
    displayName: 'New Name',
    updatedAt: new Date()
  })
  .where(eq(schema.users.id, userId));

**DELETE**

typescript
// Hard delete
await db
  .delete(schema.sessions)
  .where(eq(schema.sessions.id, sessionId));

// Soft delete (preferred)
await db
  .update(schema.users)
  .set({ deletedAt: new Date() })
  .where(eq(schema.users.id, userId));

**Complex Queries**

**Aggregations**

typescript
// COUNT
const result = await db
  .select({ count: sql<number>`COUNT(*)` })
  .from(schema.apps)
  .where(eq(schema.apps.visibility, 'public'));

const total = result[0].count;

// SUM, AVG
const stats = await db
  .select({
    totalViews: sql<number>`SUM(${schema.appViews.id})`,
    avgViews: sql<number>`AVG(view_count)`
  })
  .from(schema.apps);

**Subqueries**

typescript
// Subquery in WHERE
const apps = await db
  .select()
  .from(schema.apps)
  .where(
    inArray(
      schema.apps.id,
      db.select({ id: schema.favorites.appId })
        .from(schema.favorites)
        .where(eq(schema.favorites.userId, userId))
    )
  );

**Conditional WHERE Clauses**

typescript
// Use BaseService.buildWhereConditions()
const conditions: WhereCondition[] = [];

if (framework) {
  conditions.push(eq(schema.apps.framework, framework));
}

if (search) {
  conditions.push(
    or(
      sql`LOWER(${schema.apps.title}) LIKE ${`%${search}%`}`,
      sql`LOWER(${schema.apps.description}) LIKE ${`%${search}%`}`
    )
  );
}

const whereClause = this.buildWhereConditions(conditions);

const apps = await db
  .select()
  .from(schema.apps)
  .where(whereClause);

Domain Services

**Available Services**

Location: /worker/database/services/

  1. AuthService - Authentication, login, OAuth
  2. SessionService - JWT sessions, token management
  3. UserService - User CRUD, profiles
  4. AppService - App CRUD, public listings, search, ranking
  5. AnalyticsService - Views, stars, activity tracking
  6. SecretsService - Encrypted secrets storage
  7. ModelConfigService - User model overrides
  8. ApiKeyService - API key generation, validation

Each extends BaseService, uses Drizzle ORM, follows standard CRUD patterns.


AppService - Public App Ranking

Key methods: createApp, getPublicApps (paginated with filters), getUserAppsWithFavorites, toggleAppStar, updateDeploymentId, updateGitHubRepository, updateAppScreenshot

Ranking algorithms:

  • Popular: (views ร— 1 + stars ร— 3) DESC
  • Trending: (recent_activity ร— 1000000 + recency_bonus) DESC
  • Recent: updatedAt DESC
  • Starred: COUNT(stars) DESC

Read replica usage: Public queries use 'fast', user's own data uses 'fresh'


Database Migrations

Location: /migrations/ (SQL files + meta snapshots)

Commands:

  • npm run db:generate - Generate migration from schema changes
  • npm run db:migrate:local - Apply to local D1
  • npm run db:migrate:remote - Apply to production D1
  • npm run db:push:local - Direct push (dev only)

Tool: Drizzle Kit with d1-http driver


๐Ÿ“š Key Files Reference

**Frontend Core Files**

  • /src/api-types.ts - ALL shared API types (single source of truth)
  • /src/lib/api-client.ts - ALL API calls defined here
  • /src/routes/chat/chat.tsx - Main chat interface (1208 lines)
  • /src/routes/chat/hooks/use-chat.ts - Chat state management (BRAIN)
  • /src/routes/chat/utils/handle-websocket-message.ts - WebSocket handler (831 lines)
  • /src/routes/chat/utils/deduplicate-messages.ts - Message deduplication utilities
  • /src/routes/chat/components/phase-timeline.tsx - Phase progress UI
  • /src/routes/chat/components/messages.tsx - User/AI message rendering
  • /src/hooks/useAuthGuard.ts - Authentication guards
  • /src/hooks/use-image-upload.ts - Image upload handling

**Backend Core Files**

  • /worker/agents/core/simpleGeneratorAgent.ts - Base agent DO class
  • /worker/agents/core/state.ts - CodeGenState interface
  • /worker/agents/core/websocket.ts - WebSocket message handler (250 lines)
  • /worker/agents/constants.ts - WebSocket message type constants
  • /worker/agents/inferutils/core.ts - LLM inference engine
  • /worker/agents/inferutils/infer.ts - Inference execution wrapper
  • /worker/agents/inferutils/config.ts - Model configurations
  • /worker/agents/assistants/codeDebugger.ts - Deep debugger assistant
  • /worker/agents/operations/UserConversationProcessor.ts - Orange AI (818 lines)
  • /worker/agents/tools/customTools.ts - Tool registration
  • /worker/api/routes/index.ts - Main API router
  • /worker/database/schema.ts - Database schema (618 lines)

**Configuration Files**

  • /worker/agents/inferutils/config.ts - LLM model configs
  • /wrangler.jsonc - Cloudflare Workers config
  • /vite.config.ts - Frontend build config
  • /tsconfig.json - TypeScript config
  • /drizzle.config.local.ts - Local database config
  • /drizzle.config.remote.ts - Remote database config

โœ… Checklist for Changes

Before submitting any change, verify:

  • Types are properly defined (no any)
  • Existing patterns are followed
  • Code is DRY (no duplication)
  • Comments are clear and concise
  • File naming matches conventions
  • API calls use api-client.ts
  • Database operations use service classes
  • Error handling is comprehensive
  • AbortController lifecycle is correct (if applicable)
  • WebSocket messages are handled (if applicable)
  • This document is updated (if needed)

๐Ÿ”ง Troubleshooting Common Issues

**Issue: "Cannot find module" errors**

Cause: Import path incorrect or module not installed

Fix:

  1. Check import path matches file location
  2. For workspace imports, use worker/... not ../../../...
  3. Run npm install if package missing
  4. Check tsconfig.json path mappings

**Issue: Durable Object not receiving WebSocket messages**

Check:

  1. Message type in constants: /worker/agents/constants.ts
  2. Handler in /worker/agents/core/websocket.ts โ†’ handleWebSocketMessage()
  3. Frontend sending correct type (check browser console)
  4. WebSocket connection established (check agent_connected received)

Debug:

typescript
// Add to websocket.ts handleWebSocketMessage()
logger.info('Received WebSocket message', { type: message.type, data: message });

**Issue: LLM not calling tools**

Common causes:

  1. Tool description unclear โ†’ LLM doesn't know when to use it
  2. Tool not registered in buildTools() or buildDebugTools()
  3. Parameter schema too complex โ†’ simplify
  4. System prompt doesn't mention tool

Fix:

  • Keep tool description to 2-3 clear lines
  • Make parameters simple (prefer strings over complex objects)
  • Add tool to relevant system prompt

**Issue: Database query returning stale data**

Cause: Using read replica for data that needs to be fresh

Fix:

typescript
// WRONG - uses fast replica
const readDb = this.getReadDb('fast');

// RIGHT - uses fresh data
const readDb = this.getReadDb('fresh');

// OR use primary for critical consistency
const result = await this.database.select()...

**Issue: "Rate limit exceeded" during development**

Quick fix:

typescript
// In UserConversationProcessor.ts or codeDebugger.ts
// Temporarily increase max_tokens or reduce frequency

Better fix: Use cheaper model for testing

typescript
// In config.ts
conversationalResponse: {
  name: GEMINI_2_5_FLASH,  // Fast & cheap
  max_tokens: 4000,
}

**Issue: Type errors after schema change**

Steps:

  1. Regenerate Drizzle types: npm run db:generate
  2. Restart TypeScript server in IDE
  3. Check migration applied: npm run db:migrate:local

**Issue: Sandbox deployment failing**

Check logs:

typescript
// In DeploymentManager.ts, enable verbose logging
this.logger.info('Deployment attempt', { 
  sessionId: this.getSessionId(),
  filesCount: files.length 
});

Common causes:

  1. Sandbox service unreachable
  2. Invalid template name
  3. sessionId mismatch (check agent.state.sessionId)
  4. npm install timeout โ†’ increase timeout or split commands

**Issue: Agent state not persisting**

Verify:

  1. Check DO storage: Cloudflare dashboard โ†’ Durable Objects
  2. Ensure setState() called after changes
  3. Check for exceptions in state serialization

Test:

typescript
const currentState = this.getState();
this.logger().info('State before save', { currentState });
this.setState(newState);
this.logger().info('State after save', { newState });

**Where to Look for Logs**

Local development:

  • Frontend: Browser console
  • Worker: Terminal where npm run dev:worker is running
  • Durable Objects: Same terminal, prefixed with DO ID

Production:

  • Cloudflare dashboard โ†’ Workers & Pages โ†’ Logs
  • Real-time logs via wrangler tail
  • Sentry (if configured)

**Useful Debug Snippets**

Log all WebSocket messages:

typescript
// In websocket.ts
logger.info('[WS_IN]', { type: message.type, keys: Object.keys(message) });

Log all tool calls:

typescript
// In customTools.ts executeToolWithDefinition()
logger.info('[TOOL_CALL]', { name: toolDef.function.name, args });

Log state transitions:

typescript
// In simpleGeneratorAgent.ts launchStateMachine()
logger.info('[STATE_TRANSITION]', { 
  from: currentDevState, 
  to: executionResults.currentDevState 
});

๐Ÿ”’ RATE LIMITING

Overview

Location: /worker/middleware/rate-limiter.ts

Rate limiting protects API endpoints from abuse using token bucket algorithm with Durable Object storage.


Implementation

Middleware: Applied to all API routes except health checks

Strategy:

  • Token bucket algorithm - Tokens refill over time
  • Per-user basis - Keyed by userId (authenticated) or IP (anonymous)
  • Durable Object storage - Distributed rate limit state
  • Graceful degradation - Falls back on DO errors

Rate Limits

User Type Requests Window Burst
Authenticated 100 1 minute 150
Anonymous 20 1 minute 30
API Keys 300 1 minute 400

Burst: Maximum requests in short burst before throttling


Response Headers

http
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1698765432

On rate limit exceeded:

http
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json

{
  "error": "Rate limit exceeded",
  "retryAfter": 42
}

Frontend Handling

Location: /src/routes/chat/utils/message-helpers.ts

typescript
export function handleRateLimitError(
  error: RateLimitExceededError,
  setMessages: (fn: (prev: ChatMessage[]) => ChatMessage[]) => void
) {
  const retryAfter = error.retryAfter || 60;
  const message = `Rate limit exceeded. Please wait ${retryAfter} seconds.`;
  
  setMessages(prev => [
    ...prev,
    createAIMessage('rate-limit', message)
  ]);
}

Usage:

typescript
catch (error) {
  if (error instanceof RateLimitExceededError) {
    handleRateLimitError(error, setMessages);
    return;
  }
  // ... other error handling
}

Bypassing for Internal Tools

Some endpoints bypass rate limiting:

  • Health checks (/health, /api/health)
  • WebSocket connections (rate limited separately)
  • Internal service-to-service calls (authenticated with service tokens)

Configuration:

typescript
// In rate-limiter.ts
const EXEMPT_PATHS = ['/health', '/api/health'];

Monitoring

Cloudflare Analytics:

  • 429 response rate
  • Peak request times
  • Top rate-limited IPs

Custom Logs:

typescript
logger.warn('Rate limit exceeded', {
  userId: ctx.userId,
  ip: ctx.ip,
  path: ctx.path,
  remaining: 0
});

Last Updated: 2024-10-31
Maintainers: All AI assistants working on this project


File: docs/POSTMAN_COLLECTION_README.md

Legacy V1 Dev API Postman Collection

This collection documents the legacy V1 Dev/phasic API surface and has not been migrated to the current Think behavior. Verify routes and payloads against worker/api/routes/ before using it for current integrations.

The collection remains available for compatibility testing of supported legacy endpoints, OAuth setup, and CSRF behavior.

๐Ÿ“‹ Overview

This collection includes 100+ API endpoints organized into logical groups:

  • ๐Ÿ” Authentication - OAuth, email auth, session management (16 endpoints)
  • ๐Ÿค– Agent & Code Generation - AI-powered webapp creation (5 endpoints)
  • ๐Ÿ“ฑ Apps Management - CRUD operations, public feed, favorites (10 endpoints)
  • ๐Ÿ‘ค User Management - Profile, apps with pagination (2 endpoints)
  • ๐Ÿ“Š Analytics & Stats - User stats, AI Gateway analytics (4 endpoints)
  • ๐Ÿค– Model Configuration - AI model settings, BYOK providers (8 endpoints)
  • ๐Ÿข Custom Model Providers - OpenAI-compatible API management (6 endpoints)
  • ๐Ÿ” Secrets Management - API keys, credentials with templates (5 endpoints)
  • ๐Ÿ™ GitHub Integration - Repository export, OAuth (2 endpoints)

๐Ÿš€ Quick Setup

1. Import Collection & Environment

  1. Import Collection:

    • Open Postman โ†’ Import โ†’ Upload v1dev-api-collection.postman_collection.json
  2. Import Environment:

    • Import โ†’ Upload v1dev-environment.postman_environment.json
  3. Select Environment:

    • Choose "V1 Dev Environment" from the environment dropdown

2. Configure Base URL

Update the baseUrl environment variable:

  • Production: https://your-production-domain.com
  • Local Development: http://localhost:8787 (Wrangler dev server)

3. OAuth Setup in Postman

โš ๏ธ IMPORTANT: OAuth endpoints redirect to external providers (Google/GitHub) and cannot be tested directly in Postman.

  1. Use the OAuth Helper requests:

    • Run "๐ŸŒ OAuth Helper - Get Google URL"
    • Check the Console tab in Postman for the OAuth URL
    • Copy the URL and open it in your browser
  2. Complete authentication in browser:

    • Follow the OAuth flow in your browser
    • After successful auth, you'll be redirected back to your app
    • Session cookies are now set for your domain
  3. Return to Postman:

    • Session cookies will work automatically for same-domain requests
    • Test with "Get User Profile" to verify authentication

Alternative: Manual URL Construction

If helpers don't work, manually construct URLs:

  • Google OAuth: {{baseUrl}}/api/auth/oauth/google
  • GitHub OAuth: {{baseUrl}}/api/auth/oauth/github
  • Open these URLs directly in your browser

Why Direct OAuth Requests Show HTML

  • OAuth endpoints return HTTP redirects (302) to provider websites
  • Postman shows the redirect HTML instead of following it
  • This is normal behavior - OAuth requires browser-based flows

4. CSRF Token Automation

The collection automatically handles CSRF tokens:

  • Pre-request scripts fetch CSRF tokens when needed
  • Tokens are stored in the csrf_token environment variable
  • All state-changing requests include the token automatically

๐Ÿ”‘ Authentication Methods

1. Email Authentication

json
POST /api/auth/register
{
  "email": "[email protected]",
  "password": "SecurePassword123!",
  "name": "Test User"
}

POST /api/auth/login
{
  "email": "[email protected]", 
  "password": "SecurePassword123!"
}

2. OAuth Authentication

  • Google OAuth: GET /api/auth/oauth/google
  • GitHub OAuth: GET /api/auth/oauth/github

3. Session-Based Authentication

  • Uses secure HTTP-only cookies
  • Sessions are automatically maintained across requests
  • CSRF protection via X-CSRF-Token header

๐Ÿ“ฑ Core API Workflows

1. Create a New App with AI

bash
# 1. Login or use OAuth
POST /api/auth/login

# 2. Start code generation
POST /api/agent
{
  "query": "Create a React todo app with TypeScript and Tailwind CSS",
  "agentMode": "smart",
  "language": "typescript", 
  "frameworks": ["react", "tailwindcss"],
  "selectedTemplate": "react-typescript"
}

# 3. Connect to WebSocket for real-time updates
GET /api/agent/{agentId}/ws (WebSocket)

# 4. Deploy preview when ready
GET /api/agent/{agentId}/preview

2. Browse and Interact with Apps

bash
# Get public apps (no auth required)
GET /api/apps/public?page=1&limit=20&sort=stars&order=desc

# Get app details (no auth required)
GET /api/apps/{appId}

# Star an app (requires auth)
POST /api/apps/{appId}/star

# Fork an app (requires auth) 
POST /api/apps/{appId}/fork

3. Configure AI Models

bash
# Get available models and providers
GET /api/model-configs/byok-providers

# Update model configuration for specific agent action
PUT /api/model-configs/planner
{
  "modelName": "claude-3-5-sonnet-20241022",
  "maxTokens": 4096,
  "temperature": 0.7,
  "reasoningEffort": "medium"
}

# Test model configuration
POST /api/model-configs/test
{
  "agentActionName": "planner",
  "useUserKeys": true
}

4. Manage API Keys and Secrets

bash
# Get secret templates
GET /api/secrets/templates

# Store an API key
POST /api/secrets
{
  "templateId": "openai_api_key",
  "name": "My OpenAI API Key",
  "envVarName": "OPENAI_API_KEY",
  "value": "sk-your-api-key-here"
}

# Create custom model provider  
POST /api/user/providers
{
  "name": "My Custom OpenAI Provider",
  "baseUrl": "https://api.openai.com/v1",
  "apiKey": "sk-your-key",
  "models": [...]
}

๐Ÿ”ง Advanced Features

Environment Variables

The collection uses these automatically managed variables:

Variable Description Auto-populated
csrf_token CSRF protection token โœ…
user_id Current user ID โœ…
session_id Current session ID โœ…
agent_id Current agent/app ID โœ…
app_id Current app ID โœ…
provider_id Model provider ID Manual
secret_id Secret ID Manual

Request Automation

  • CSRF Tokens: Automatically fetched and included
  • Session Management: Cookies handled transparently
  • Variable Population: IDs extracted from responses
  • Error Handling: Test scripts validate responses

WebSocket Testing

For WebSocket endpoints like agent communication:

  1. Use a WebSocket client (wscat, Postman WebSocket, etc.)
  2. Connect to: ws://localhost:8787/api/agent/{agentId}/ws
  3. Include authentication cookies
  4. Send/receive real-time messages during code generation

๐Ÿ› ๏ธ Development Setup

Local Development

  1. Start Wrangler Dev Server:

    bash
    cd /path/to/vibesdk
    bun run dev
  2. Update Environment:

    • Set baseUrl to http://localhost:8787
    • Ensure .dev.vars contains required environment variables
  3. Test Authentication:

    • OAuth may require ngrok for localhost callback URLs
    • Email auth works directly with localhost

Production Testing

  1. Update baseUrl to your production domain
  2. Ensure OAuth apps are configured with correct callback URLs
  3. Test with real OAuth credentials

๐Ÿ“š API Documentation

Authentication Levels

  • Public: No authentication required
  • Authenticated: Requires valid session
  • Owner Only: Requires ownership of the resource

Common Parameters

Parameter Description Example
page Page number for pagination 1
limit Items per page 20
sort Sort field createdAt, stars
order Sort order asc, desc
period Time period filter today, week, month, all
search Search query "todo app"

Response Format

All API responses follow this structure:

json
{
  "success": true,
  "data": { ... },
  "message": "Optional message",
  "pagination": {  // For paginated responses
    "page": 1,
    "limit": 20, 
    "total": 100,
    "totalPages": 5
  }
}

Error Responses

json
{
  "success": false,
  "error": "Error message",
  "code": "ERROR_CODE",
  "details": { ... }  // Optional additional details
}

๐Ÿšจ Troubleshooting

Common Issues

  1. CSRF Token Errors:

    • Ensure pre-request scripts are enabled
    • Manually run "Get CSRF Token" request
    • Check that X-CSRF-Token header is included in POST/PUT/DELETE requests
  2. Authentication Issues:

    • Verify cookies are enabled in Postman
    • Check that OAuth callback URLs match your configuration
    • Ensure session hasn't expired (check "Get User Profile")
  3. WebSocket Connection Issues:

    • WebSockets require active session authentication
    • Use external WebSocket client if Postman WebSocket support is limited
    • Verify agent ownership for WebSocket connections
  4. Local Development Issues:

    • Ensure Vite is running (bun run dev)
    • Check that .dev.vars contains required environment variables
    • Verify D1 migrations are applied (bun run db:migrate:local)

Getting Help

  1. Check API Response: Look at response body for detailed error messages
  2. Verify Environment: Ensure correct baseUrl is set
  3. Test Authentication: Run "Check Auth Status" to verify session
  4. Review Logs: Check browser DevTools or Wrangler logs for additional context

๐ŸŽฏ Testing Workflows

Complete User Journey

  1. Register/Login โ†’ Authentication working
  2. Create App โ†’ AI generation working
  3. Browse Public Apps โ†’ Public feed working
  4. Star/Fork App โ†’ Social features working
  5. Configure Models โ†’ AI customization working
  6. Manage Secrets โ†’ Security features working
  7. Export to GitHub โ†’ Integration working

Quick Health Check

Run these requests to verify the system:

  1. GET /api/auth/providers - System status
  2. GET /api/apps/public - Public API working
  3. POST /api/auth/login - Authentication working
  4. GET /api/model-configs - AI system working
  5. GET /api/stats - Analytics working

This collection provides comprehensive coverage of all V1 Dev APIs with proper authentication, error handling, and real-world usage examples. Perfect for development, testing, and integration work!


File: docs/setup.md

VibeSDK Setup Guide

Set up VibeSDK for local development and production deployment.

Make sure to read through the entire guide for important notes, and have all the required information ready before starting.

Current generated-app previews use SpaceDO, a Worker Loader binding, and Dynamic Workers. Cloudflare Artifacts is optional behind ENABLE_ARTIFACTS; it is not required for the default SQLite workspace filesystem. Previews do not require a sandbox container or persistent preview server.

Prerequisites

Before getting started, make sure you have:

Required

  • Node.js (v22 or later)
  • Cloudflare account with API access
  • Cloudflare API Token with appropriate permissions
  • Bun
  • Custom domain configured in Cloudflare (for production deployment)

For Production Features

  • Workers Paid Plan (for remote Cloudflare resources)
  • Workers for Platforms subscription (for app deployment features)
  • Advanced Certificate Manager (if using first-level subdomains)

Quick Start

The fastest way to get VibeSDK running is with our automated setup script:

bash
# Bun is recommended. Install it first if needed.
curl -fsSL https://bun.sh/install | bash
# Then install dependencies and run setup
bun install
bun run setup

This interactive script will guide you through the entire setup process, including:

  • Package manager setup (installs Bun automatically for better performance)
  • Cloudflare credentials collection (Account ID and API Token)
  • Domain configuration (custom domain or localhost for development)
  • Remote setup (optional production deployment configuration)
  • AI Gateway configuration (Cloudflare AI Gateway recommended)
  • API key collection (OpenAI, Anthropic, Google AI Studio, etc.)
  • OAuth setup (Google, GitHub login - optional)
  • Resource creation (KV namespaces, D1 databases, R2 buckets, AI Gateway)
  • File generation (.dev.vars and optionally .prod.vars)
  • Configuration updates (wrangler.jsonc and vite.config.ts)
  • Database setup (schema generation and migrations)
  • Template deployment (example app templates to R2)
  • Readiness report (comprehensive status and next steps)

What You'll Need During Setup

The setup script will ask you for the following information:

Cloudflare Account Information

  1. Account ID: Found in your Cloudflare dashboard sidebar

  2. API Token: In you Cloudflare dashboard under "My Profile" > "API Tokens", create a token (Using the "Edit Cloudflare Workers" template is recommended) with the following configurations:

    • Your Account - Workers KV Storage:Edit, Workers Scripts:Edit, Account Settings:Read, Workers Tail:Read, Workers R2 Storage:Edit, Cloudflare Pages:Edit, Workers Builds Configuration:Edit, Workers Agents Configuration:Edit, Workers Observability:Edit, Containers:Edit, D1:Edit, AI Gateway:Read, AI Gateway:Edit, AI Gateway:Run, Cloudchamber:Edit, Browser Rendering:Edit
    • All zones - Workers Routes:Edit
    • All users - User Details:Read, Memberships:Read

    If using the Edit Cloudflare Workers template, make sure to add the missing permissions above manually.

    Important: Some features like D1 databases and R2 may require a paid Cloudflare plan.

Domain Configuration

With Custom Domain:

bash
Enter your custom domain (or press Enter to skip): myapp.com
โœ… Custom domain set: myapp.com
Use remote Cloudflare resources (KV, D1, R2, etc.)? (Y/n): 
Configure for production deployment? (Y/n):

Without Custom Domain:

bash
Enter your custom domain (or press Enter to skip): [press Enter]
โš ๏ธ  No custom domain provided.
   โ€ข Remote Cloudflare resources: Not available
   โ€ข Production deployment: Not available
   โ€ข Only local development will be configured

Continue with local-only setup? (Y/n):

AI Gateway Configuration

Cloudflare AI Gateway (Recommended)

  • Automatic token setup: When selected, CLOUDFLARE_AI_GATEWAY_TOKEN is automatically set to your API token
  • No manual configuration: The script handles all AI Gateway authentication
  • Better performance: Caching, rate limiting, and monitoring included

Custom OpenAI URL (Alternative)

  • For users with existing OpenAI-compatible endpoints
  • Requires manual model configuration in worker/agents/inferutils/config.ts

AI Provider Selection

The setup script offers multiple AI providers with intelligent multi-selection:

Available Providers:

  1. OpenAI (for GPT models)
  2. Anthropic (for Claude models)
  3. Google AI Studio (for Gemini models) - Default & Recommended
  4. Cerebras (for open source models)
  5. OpenRouter (for various models)
  6. Custom provider (for any other provider)

Provider Selection:

  • Select multiple providers with comma-separated numbers (e.g., 1,2,3)
  • Each selected provider will prompt for its API key
  • Custom providers automatically generate PROVIDER_NAME_API_KEY variables
  • Custom providers are automatically added to worker-configuration.d.ts

Important Model Configuration Notes

Google AI Studio (Recommended):

Other Providers:

  • Strong warning: You MUST edit worker/agents/inferutils/config.ts
  • Change default model configurations from Gemini to your selected providers
  • Model format: /<model-name> (e.g., openai/gpt-4, anthropic/claude-3.5-sonnet)
  • Review fallback model configurations

Without AI Gateway:

  • Manual config.ts editing required for all model configurations
  • Model names must follow /<model-name> format

OAuth Configuration

The script will also ask for OAuth credentials:

  • Google OAuth: For user authentication and login (not AI Studio access)
  • GitHub OAuth: For user authentication and login
  • GitHub Export OAuth: For exporting generated apps to GitHub repositories (separate from login OAuth)

If you don't provide OAuth credentials, by default at login, you will only be able to use email-based registration/login.

Login with Cloudflare

You can let users sign in with their Cloudflare account. The same consent also
connects their Cloudflare AI Gateway, so generations can run on their own credits
("Use my AI Gateway" toggle in settings).

1. Create an OAuth client

Create an OAuth client in the Cloudflare dashboard:
https://dash.cloudflare.com/?to=/:account/oauth-clients

Configure these redirect URLs on the client (replace the origin with your
deployment's URL; for local development this is http://localhost:5173):

  • https://your-domain.com/api/auth/callback/cloudflare โ€” "Login with Cloudflare"
  • https://your-domain.com/auth/callback โ€” connect AI Gateway (from settings)

Grant the client these scopes (Cloudflare uses dotted identifiers, not OIDC
email/profile):

text
openid user-details.read ai.read ai.write aig.read aig.run aig.write offline_access

The scopes and the Cloudflare OAuth endpoint URLs are hardcoded in the worker
(worker/services/oauth/cloudflare-connect.ts) and are not configurable โ€” just make
sure the OAuth client is authorized for all of these scopes, or the authorization
request fails with invalid_scope.

2. Set the environment variables

Add the client credentials to .dev.vars (and .prod.vars for production):

bash
CLOUDFLARE_OAUTH_CLIENT_ID="<your-oauth-client-id>"        # required for Login with Cloudflare
CLOUDFLARE_OAUTH_CLIENT_SECRET="<your-oauth-client-secret>"
CF_OAUTH_ENCRYPTION_KEY="<32-byte base64 key>"             # required for AI Gateway; encrypts the token cookie

Set ENABLE_CLOUDFLARE_LIMITS="true" in the Cloudflare dashboard for production, or in .dev.vars for local development.

The "Login with Cloudflare" button appears as soon as CLOUDFLARE_OAUTH_CLIENT_ID
and CLOUDFLARE_OAUTH_CLIENT_SECRET are set โ€” identity login needs nothing else.

The AI Gateway connect/auto-connect (running generations on the user's own
credits) additionally requires the dashboard-managed ENABLE_CLOUDFLARE_LIMITS="true" and
CF_OAUTH_ENCRYPTION_KEY (generate with openssl rand -base64 32). If the key is
missing, the gateway feature is disabled (same as leaving ENABLE_CLOUDFLARE_LIMITS
unset) and login simply skips the gateway auto-connect โ€” users fall back to the free
tier and can connect later.

Generated-app preview requirements

Generated-app previews use the SPACE_DO and LOADER bindings. Add the ARTIFACTS binding for Artifacts-backed spaces. Docker is not required for the current Think/SpaceDO preview path. SandboxDockerfile and container setup remain only for legacy tooling.

Dashboard-managed feature toggles

Feature settings are intentionally omitted from the committed wrangler vars. For deployed environments, set them in the Cloudflare dashboard; keep_vars: true preserves their values when wrangler deploy runs. For local development, set them in .dev.vars. Do not add these settings back to wrangler.jsonc or wrangler.staging.jsonc.

Variable Effect Unset default Notes
ENABLE_ARTIFACTS Uses Artifacts-backed spaces Off Requires the ARTIFACTS binding.
ENABLE_READ_REPLICAS Enables D1 read replicas Off Set to "true" to enable.
ENABLE_EMAIL_AUTH Enables email/password authentication On Set to "false" to make the deployment OAuth-only.
ENABLE_CLOUDFLARE_LIMITS Enables AI Gateway connect Off Requires CF_OAUTH_ENCRYPTION_KEY; set to "true" to enable.
ENABLE_USER_ACCOUNT_DEPLOY Deploys Think apps to the user's Cloudflare account Off Set to "true" to enable.
ALLOWED_EMAIL Restricts sign-in to one email address Off Set the allowed address; empty or unset disables the allowlist.
ALLOCATION_STRATEGY Selects the legacy sandbox allocation strategy Default strategy Managed in the dashboard rather than through production secrets.
USE_CLOUDFLARE_IMAGES Enables Cloudflare Images uploads Off Set a non-empty value to enable.
USE_TUNNEL_FOR_PREVIEW Uses a tunnel for local previews Off Dev-only; set in .dev.vars, not the production dashboard.

Existing deployments retain previously configured dashboard values when this configuration is deployed. New deployments must explicitly set ENABLE_READ_REPLICAS="true" or ENABLE_CLOUDFLARE_LIMITS="true" in the dashboard to preserve the former committed defaults.

Manual Setup (Alternative)

If you prefer to set up manually:

1. Create `.dev.vars` file

Copy .dev.vars.example to .dev.vars and fill in your values:

bash
cp .dev.vars.example .dev.vars

2. Configure Required Variables

bash
# Essential
CLOUDFLARE_API_TOKEN="your-api-token"
CLOUDFLARE_ACCOUNT_ID="your-account-id"

# Security
JWT_SECRET="generated-secret"

# Domain (optional)
CUSTOM_DOMAIN="your-domain.com"

3. Create Cloudflare Resources

Create required resources in your Cloudflare account:

  • KV Namespace for VibecoderStore
  • D1 Database named vibesdk-db
  • R2 Bucket named vibesdk-templates

4. Update `wrangler.jsonc`

Update resource IDs in wrangler.jsonc with the IDs from step 3.

Starting Development

After setup is complete:

bash
# Set up database
bun run db:migrate:local

# Start development server
bun run dev

Visit your app at http://localhost:5173

Important Note: If you didn't specifiy any oauth credentials during setup, You would need to register an account for the first time.

Troubleshooting

Common Issues

D1 Database "Unauthorized" Error: This usually means:

  • Your API token lacks "D1:Edit" permissions
  • Your account doesn't have access to D1 (may require paid plan)
  • You've exceeded your D1 database quota
  • Solution: Update your API token permissions or upgrade your Cloudflare plan

Permission Errors: Ensure your API token has all required permissions listed above.

Domain Not Found: Make sure your domain is:

  • Added to Cloudflare
  • DNS is properly configured
  • API token has zone permissions

Resource Creation Failed: Check that your account has:

  • Available KV namespace quota (10 on free plan)
  • D1 database quota (may require paid plan)
  • R2 bucket quota (may require paid plan)
  • Appropriate plan level for requested features

R2 Bucket "Unauthorized" Error: This usually means:

  • Your API token lacks "R2:Edit" permissions
  • Your account doesn't have access to R2 (may require paid plan)
  • You've exceeded your R2 bucket quota
  • Solution: Update your API token permissions or upgrade your Cloudflare plan

AI Configuration Issues:

  • "AI Gateway token already configured" but token not in .dev.vars: Re-run setup, this was a bug that's now fixed
  • Models not working with custom providers: Edit worker/agents/inferutils/config.ts to change default model configurations
  • Custom provider not recognized: Check that the provider was added to worker-configuration.d.ts
  • AI Gateway creation failed: Ensure your API token has AI Gateway permissions

Dynamic Worker Preview Issues:

  • Confirm the SPACE_DO and LOADER bindings are configured; confirm ARTIFACTS only when ENABLE_ARTIFACTS="true".
  • Check the branch deployment and signed preview URL.
  • Use bun run dev:browser when local browser-console inspection is needed.

Deploy to Cloudflare Button Issues (Chat Interface):

  • "Deploy button not working locally": Chat interface deploy button requires custom domain, initial deployment, and remote dispatch bindings
  • "Dispatch namespace not found": Deploy your VibeSDK project to Cloudflare at least once first
  • "Deploy fails with authentication error": Ensure your custom domain is properly configured and deployed
  • Note: This refers to deploying generated apps from the chat interface, not GitHub repository deployments

Legacy Corporate Container Setup:
The following certificate setup applies only when intentionally running legacy Docker-based tooling:

  1. Copy your corporate root CA certificate to the project root (don't commit to git!)
  2. Edit SandboxDockerfile to include your certificate:
dockerfile
# Add your company's Root CA certificate for corporate network access
COPY your-root-ca.pem /usr/local/share/ca-certificates/your-root-ca.crt
RUN update-ca-certificates

# Set SSL environment variables for cloudflared and other tools
ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/your-root-ca.crt
ENV CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt

โš ๏ธ Security Warning: Never commit corporate CA certificates to public repositories. Use .gitignore to exclude certificate files and only use this for local development.

Getting Help

  1. Check the setup report for specific issues and suggestions
  2. Review the Cloudflare Workers documentation
  3. Ensure all prerequisites are met

Production Deployment

If you configured remote deployment during setup, you'll have a .prod.vars file ready for production. Deploy with:

bash
bun run deploy

This will:

  • Build the application
  • Update Cloudflare resources
  • Deploy to Cloudflare Workers
  • Apply database migrations
  • Configure custom domain routing (if specified)

Production-Only Setup

If you only set up for local development initially, you can configure production later:

  1. Run setup again and choose "yes" for remote deployment configuration
  2. Provide production domain when prompted
  3. Deploy using bun run deploy

Manual Production Setup

Alternatively, create .prod.vars manually based on .dev.vars but with:

  • Production domain in CUSTOM_DOMAIN
  • Production API keys and secrets
  • ENVIRONMENT="prod"

Next Steps

Once setup is complete:

  1. Start developing with bun run dev
  2. Visit http://localhost:5173 to access VibeSDK
  3. Try generating your first AI-powered application
  4. Deploy to production when ready with bun run deploy

File Structure After Setup

The setup script creates and modifies these files:

text
vibesdk/
โ”œโ”€โ”€ .dev.vars              # Local development environment variables
โ”œโ”€โ”€ .prod.vars             # Production environment variables (if configured)
โ”œโ”€โ”€ wrangler.jsonc         # Updated with resource IDs and domain
โ”œโ”€โ”€ vite.config.ts         # Updated for remote/local bindings
โ”œโ”€โ”€ migrations/            # Database migration files
โ””โ”€โ”€ templates/             # Template repository (downloaded)

Summary

The VibeSDK setup script provides a comprehensive, intelligent configuration experience:

**Key Features:**

  • Simplified domain setup - One-time domain configuration with clear feature implications
  • Intelligent AI provider selection - Multi-provider support with automatic configuration
  • AI Gateway automation - Automatic token setup and configuration
  • Custom provider support - Dynamic API key generation and worker configuration updates
  • Production-ready - Both local development and production deployment configuration
  • User-friendly defaults - Y/n prompts with clear default indicators

**What Gets Configured:**

  • Cloudflare resources (KV, D1, R2, AI Gateway, dispatch namespaces)
  • Environment variables (.dev.vars and .prod.vars)
  • Worker configuration (wrangler.jsonc, worker-configuration.d.ts)
  • Database setup and migrations
  • Template deployment
  • ARM64 compatibility

The setup script handles everything from basic Cloudflare resource creation to advanced AI provider configuration, making it easy to get started regardless of your Cloudflare plan or AI provider preferences.

For any issues during setup, check the troubleshooting section above or refer to the comprehensive status report the script provides at the end.

Important Caveats & Known Issues

**Legacy tunnel and container configuration**

USE_TUNNEL_FOR_PREVIEW, SandboxDockerfile, and container instance settings belong to the retired sandbox preview path. Current generated-app previews run as Dynamic Workers loaded by SpaceDO. Do not troubleshoot the current preview path as a Docker or cloudflared tunnel unless you are intentionally running legacy tooling.

**"Deploy to Cloudflare" Button Limitations (Chat Interface)**

The "Deploy to Cloudflare" button in the chat interface (for generated apps) has specific requirements for local development:

Note: This refers to the deployment button within the VibeSDK platform's chat interface, not the GitHub repository deploy button.

Requirements:

  1. Custom domain must be properly configured during setup
  2. Initial deployment - Project must be deployed at least once to your Cloudflare account
  3. Remote dispatch bindings - wrangler.jsonc must have remote dispatch namespace enabled
  4. Dispatch worker - A dispatch worker must be running in your account

Why These Requirements?

  • The deploy feature uses Cloudflare's dispatch namespace system
  • Dispatch requires a running worker in your account to handle deployment requests
  • Local-only development isn't yet supported for this in vibesdk

Current Status: Making "Deploy to Cloudflare" work completely in local-only mode is not yet implemented.

**Dynamic Worker preview troubleshooting**

For current previews, verify the SpaceDO Durable Object binding, Worker Loader binding, branch deployment, and signed preview URL. Verify the Artifacts namespace only when ENABLE_ARTIFACTS="true". Build failures originate in @cloudflare/worker-bundler; application runtime failures should be inspected through browser console logs. If an issue persists, open a GitHub issue with the setup report and deployment error.


File: docs/usage-limits-ui.md

Usage Limits UI Rules

Defines when each limits-related UI element renders, based on UsageSummary from GET /api/limits/usage.

Inputs

Field Meaning
config.unlimited True when the user has no free-tier cap (Cloudflare-connected, or self-hosted with ENABLE_CLOUDFLARE_LIMITS != 'true').
config.limit.maxValue Free-tier cap for the current window. Infinity when unlimited (serializes to null).
config.limit.window daily (calendar-aligned UTC) or rolling.
config.limit.resetAt ISO timestamp when the free window resets.
hasUserToken Server has a decrypted Cloudflare OAuth token for the user (HttpOnly cookie).
hasCloudflareConfigured User has selected an account + AI Gateway.
cloudflareCredits Fetched Cloudflare AI Gateway balance (nullable).
limitCheck.withinLimits False when free-tier is exhausted.
cloudflareConnectEnabled Feature flag; when false, connect/limits UI is hidden.

Cloudflare OAuth tokens are server-side only (HttpOnly cookie). There is no browser-side token check.

Derived state is centralized in useUsageLimitsBadgeState() (src/components/usage-limits-badge.tsx) and reused by the sidebar CTA and auth button.

Derived flag Meaning
hidden cloudflareConnectEnabled is false (after load).
needsConfiguration hasUserToken && !hasCloudflareConfigured.
showCredits hasUserToken && hasCloudflareConfigured && cloudflareCredits present.
showUsage config.limit exists AND !config.unlimited.
isExhausted !limitCheck.withinLimits (when free-tier usage is tracked).

Full-width brand button above the auth row in SidebarFooter. Collapsed sidebar shows glyph-only size-9.

User state CTA
Not authenticated / loading / hidden Hidden
!hasUserToken "Connect Cloudflare" โ†’ CSRF-protected OAuth initiation (POST /api/cloudflare/connect)
needsConfiguration "Configure AI Gateway" โ†’ /settings
Configured (hasUserToken && hasCloudflareConfigured) Hidden โ€” even if cloudflareCredits is null

Do not gate this CTA on !showCredits. That incorrectly re-prompts OAuth when the user is configured but the credits payload failed to load.

Auth button status + menu โ€” `src/components/auth/auth-button.tsx`

Uses the same useUsageLimitsBadgeState() hook. Status line under the avatar (sidebar display); limits actions in the account dropdown.

Status line (sidebar display)

User state Status text
Loading "Checking credits..."
needsConfiguration "Configure AI Gateway" (amber)
showCredits $X.XX credits (or with gateway name)
Exhausted, no token "Free limit exhausted" (red)
Free tier remaining usage text (e.g. N free credits left)
Otherwise "Connect Cloudflare"
User state Menu item
needsConfiguration "Configure AI Gateway" โ†’ /settings
showCredits "Manage AI Gateway" โ†’ /settings
!hasUserToken (Connect) No menu item โ€” Connect lives on the sidebar CTA only

Settings and Sign Out always remain.

Badge helper โ€” `src/components/usage-limits-badge.tsx`

useUsageLimitsBadgeState() is the shared derivation layer. The visual UsageLimitsBadge component is retained but not mounted in the shell (header badge is commented out in global-header.tsx).

User state Derived / badge content
Loading loading
Token missing, free tier remaining Connect + showUsage
Token present, gateway not selected needsConfiguration
Connected (token + gateway + credits) showCredits + optional showUsage
Connected, free tier exhausted, credits known showCredits only
Connected but cloudflareCredits missing no showCredits (status may still say Connect; sidebar CTA stays hidden)
Free tier exhausted, no token isExhausted + Connect

Rules:

  • Credit balance requires all three: hasUserToken AND hasCloudflareConfigured AND cloudflareCredits present.
  • Free-tier usage only when config.limit exists AND !config.unlimited.
  • Badge click target (if remounted): /settings when needsConfiguration or showCredits, otherwise OAuth connect.

Credits banner โ€” `src/components/credits-banner.tsx`

User state Banner content CTA
No limitsData / dismissed Hidden โ€”
config.unlimited + connected (BYOK / feature flag on) $X.XX balance ยท free credits reset in Yh Dismiss only
config.unlimited + not connected (self-hosted) Hidden โ€”
Free tier active, not connected N free credits remaining ยท resets in Yh "Connect" + dismiss
Free tier active, connected Same free-tier text Dismiss only (no connect CTA)
Free tier exhausted, connected Gateway balance ยท free credits reset in Yh Dismiss only

Threshold: Banner is also hidden when remaining free credits or gateway credits are โ‰ฅ CREDITS_BANNER_THRESHOLD (shared/constants/limits.ts, default 10). This is intentional noise reduction and is not part of the popup / connect gating.

Note: Default backend policy (excludeCloudflareConnected: false) keeps connected users on the free-tier counter until exhausted. They only hit the unlimited row when in BYOK mode (excludeBYOKUsers bypass) or when excludeCloudflareConnected is explicitly enabled.

Rules:

  • Reset text prefers config.limit.resetAt (server-provided); falls back to client-side calculation via getResetDate(window, periodSeconds).
  • rolling window without resetAt uses the verb "resets within" (upper bound); everything else uses "resets in".
  • Connect CTA renders only when !isConnected (where isConnected = hasUserToken && hasCloudflareConfigured).

Limit popups โ€” `src/utils/usage-limit-checker.tsx`

Triggered by checkCanSendPrompt (pre-flight) and getBackendLimitDialog (on backend USAGE_LIMIT_EXCEEDED).

Condition Dialog Primary action
`loading !limitsData`
limitCheck.withinLimits (pre-flight only) None โ€”
!hasUserToken Daily free limit exhausted "Connect Cloudflare" (OAuth)
hasUserToken && !hasCloudflareConfigured Configure AI Gateway Navigate to /settings?config_needed=true
hasUserToken && hasCloudflareConfigured && credits < MINIMUM_CLOUDFLARE_BALANCE Insufficient credits ($X.XX) Open dash.cloudflare.com/{accountId}/ai/ai-gateway/credits
Everything else None (allow) โ€”

MINIMUM_CLOUDFLARE_BALANCE is defined in shared/constants/limits.ts.

Deploy gate popup โ€” `src/utils/usage-limit-checker.tsx` (`getDeployGateDialog`)

Unlike the pre-flight limit popups, this one is backend-error-triggered: a user-account deploy (target: 'user', think behavior) can fail with a structured code on the cloudflare_deployment_error WebSocket message (worker/api/websocketTypes.ts). handle-websocket-message.ts forwards the code via onCloudflareDeployGate; chat.tsx renders the dialog. The toast + chat message still show in all cases.

code Emitted when Dialog Primary action
cloudflare_not_connected No decrypted Cloudflare OAuth token for the user Connect Cloudflare to deploy CSRF-protected OAuth initiation (POST /api/cloudflare/connect)
cloudflare_not_configured Multiple accounts connected and none selected (a sole connected account is used automatically โ€” deploying needs no AI Gateway) Select a Cloudflare account Navigate to /settings?config_needed=true
(absent) Any other deploy failure None (toast only) โ€”

Think deploy targets are gated by the worker flag ENABLE_USER_ACCOUNT_DEPLOY (surfaced to the frontend as userAccountDeploy on GET /api/capabilities): when on, the think deploy button sends target: 'user' ("Deploy to My Account"); when off it sends target: 'platform' ("Deploy") and the backend publishes to the platform dispatch namespace with platform credentials โ€” no Cloudflare connection required, so the popup never fires.

Backend interactions

Behavior Source
BYOK users bypass LLM rate limits llmConfig.excludeBYOKUsers (default: true)
Optionally exempt all connected users from limits llmConfig.excludeCloudflareConnected (default: false)
Free tier uses calendar-daily window (UTC midnight reset) llmConfig.calendarDaily
App-creation limits are independent of LLM limits Separate appCreation config; not touched by connected/BYOK bypass
Limits feature disabled entirely ENABLE_CLOUDFLARE_LIMITS != 'true' โ†’ checkUsageAndBalance returns limit: Infinity immediately

File: sdk/README.md

@cf-vibesdk/sdk

Official TypeScript SDK for the VibeSDK platform - build and deploy fullstack apps with AI.

Compatibility

Works in any JavaScript runtime with native WebSocket support:

Runtime Support
Cloudflare Workers Native WebSocket
Browsers Native WebSocket
Bun Native WebSocket
Node.js 22+ Native WebSocket

Installation

bash
npm install @cf-vibesdk/sdk

Quick Start

ts
import { AgenticClient } from '@cf-vibesdk/sdk';

const client = new AgenticClient({
  baseUrl: 'https://build.cloudflare.dev',
  apiKey: process.env.VIBESDK_API_KEY!,
});

// Build a new app
const session = await client.build('Build a todo app with React');

// Wait until deployable and deploy
await session.wait.deployable();
session.deployPreview();
const preview = await session.wait.previewDeployed();

console.log('Preview URL:', preview.previewURL);
console.log('Files:', session.files.listPaths());

session.close();

Authentication

Method Use Case
apiKey Recommended. Automatically exchanged for a short-lived JWT.
token Use when you already have a JWT access token.
ts
// Using API key (recommended)
const client = new PhasicClient({
  baseUrl: 'https://build.cloudflare.dev',
  apiKey: 'vibe_xxxxxxxxxxxx',
});

// Using pre-minted JWT
const client = new PhasicClient({
  baseUrl: 'https://build.cloudflare.dev',
  token: 'eyJhbGciOiJIUzI1NiIs...',
});

Clients

Client Default Behavior
AgenticClient behaviorType: 'agentic' (Think-powered model-and-tool loop)
PhasicClient behaviorType: 'phasic' (legacy phase-based generation)
VibeClient No default; specify behaviorType in build options

All clients share the same API. The specialized clients simply set a default behaviorType.

Building Apps

`client.build(prompt, options?)`

Create a new app from a natural language prompt.

ts
const session = await client.build('Build a weather dashboard', {
  projectType: 'app',           // 'app' | 'component' | 'api'
  behaviorType: 'agentic',      // 'agentic' | 'phasic'
  language: 'typescript',       // Optional
  frameworks: ['react'],        // Optional
  selectedTemplate: 'vite',     // Optional template name
  autoConnect: true,            // Auto-connect WebSocket (default: true)
  autoGenerate: true,           // Auto-start generation (default: true)
  credentials: { ... },         // Optional provider API keys
  onBlueprintChunk: (chunk) => console.log(chunk),
});

`client.connect(agentId)`

Connect to an existing app session. State is automatically restored from the agent, including:

  • Phase timeline with completion status
  • Generated files
  • Agent metadata (query, projectName, behaviorType, etc.)
ts
const session = await client.connect('agent-id-here', {
  credentials: { ... },  // Optional
});
await session.connect();

// State is now seeded from the agent
console.log('Original query:', session.state.get().query);
console.log('Phases:', session.phases.list());
console.log('Files:', session.files.listPaths());

App Management

ts
// List apps
const publicApps = await client.apps.listPublic({ limit: 20, sort: 'recent' });
const myApps = await client.apps.listMine();
const recent = await client.apps.listRecent();
const favorites = await client.apps.listFavorites();

// Get app details
const app = await client.apps.get('app-id');

// Manage apps
await client.apps.delete('app-id');
await client.apps.setVisibility('app-id', 'public'); // 'public' | 'private'
await client.apps.toggleStar('app-id');
await client.apps.toggleFavorite('app-id');

// Git clone token
const { cloneUrl, expiresAt } = await client.apps.getGitCloneToken('app-id');

BuildSession

The BuildSession object provides real-time interaction with code generation.

Properties

Property Description
agentId Unique identifier for this session
behaviorType 'phasic' or 'agentic'
projectType Type of project being built

Commands

ts
session.startGeneration();      // Start code generation
session.stop();                 // Stop generation
session.resume();               // Resume generation
session.deployPreview();        // Deploy to preview environment
session.deployCloudflare();     // Deploy to Cloudflare Workers
session.clearConversation();    // Clear conversation history
session.close();                // Close the session

// Send follow-up message
session.followUp('Add dark mode support', {
  images: [{ base64: '...', mimeType: 'image/png' }],  // Optional
});

Wait Helpers

High-level async waits for generation milestones:

ts
// Generation lifecycle
await session.wait.generationStarted({ timeoutMs: 60_000 });
await session.wait.generationComplete({ timeoutMs: 600_000 });

// Phase events (phasic mode)
await session.wait.phase({ type: 'phase_validated' });

// Deployment readiness
const { files, reason } = await session.wait.deployable();

// Deployment completion
const preview = await session.wait.previewDeployed();
console.log(preview.previewURL);

const cf = await session.wait.cloudflareDeployed();
console.log(cf.deploymentUrl);

Events

ts
// High-level events
session.on('connected', (msg) => { ... });
session.on('generation', (msg) => { ... });  // started, complete, stopped, resumed
session.on('phase', (msg) => { ... });       // generating, generated, implementing, etc.
session.on('file', (msg) => { ... });        // generating, generated, regenerated
session.on('preview', (msg) => { ... });     // started, completed, failed
session.on('cloudflare', (msg) => { ... });  // started, completed, error
session.on('error', ({ error }) => { ... });

// WebSocket events
session.on('ws:open', () => { ... });
session.on('ws:close', ({ code, reason }) => { ... });
session.on('ws:error', ({ error }) => { ... });
session.on('ws:reconnecting', ({ attempt, delayMs }) => { ... });

// Listen to specific message type
session.onMessageType('file_generated', (msg) => {
  console.log('Generated:', msg.file.filePath);
});

// Wait for specific message
const msg = await session.waitForMessageType('generation_complete', 60_000);

File Access

ts
const paths = session.files.listPaths();        // ['src/App.tsx', ...]
const content = session.files.read('src/App.tsx');
const snapshot = session.files.snapshot();      // { 'src/App.tsx': '...', ... }
const tree = session.files.tree();              // Nested file tree structure

Phase Timeline

Access the full phase timeline for phasic builds. The timeline is automatically seeded when connecting to an existing agent and updated as phases progress.

ts
// Get all phases
const phases = session.phases.list();
// [{ id: 'phase-0', name: 'Core Setup', status: 'completed', files: [...] }, ...]

// Get current active phase
const current = session.phases.current();
if (current) {
  console.log(`Working on: ${current.name}`);
  console.log(`Status: ${current.status}`);  // 'generating' | 'implementing' | 'validating'
}

// Get completed phases
const done = session.phases.completed();
console.log(`Progress: ${done.length}/${session.phases.count()}`);

// Check if all phases are done
if (session.phases.allCompleted()) {
  console.log('Build complete!');
}

// Get phase by ID
const phase = session.phases.get('phase-0');

// Subscribe to phase changes
const unsubscribe = session.phases.onChange((event) => {
  console.log(`Phase ${event.type}:`, event.phase.name);
  console.log(`Status: ${event.phase.status}`);
  console.log(`Total phases: ${event.allPhases.length}`);
});
// Later: unsubscribe();

The onChange callback receives a PhaseTimelineEvent:

ts
type PhaseTimelineEvent = {
  type: 'added' | 'updated';  // New phase vs status/file change
  phase: PhaseInfo;           // The affected phase
  allPhases: PhaseInfo[];     // All phases after this change
};

Each phase contains:

ts
type PhaseInfo = {
  id: string;           // 'phase-0', 'phase-1', etc.
  name: string;         // 'Core Setup', 'Authentication', etc.
  status: PhaseStatus;  // 'pending' | 'generating' | 'implementing' | 'validating' | 'completed' | 'cancelled'
  files: PhaseFile[];   // Files in this phase
};

type PhaseFile = {
  path: string;         // 'src/App.tsx'
  purpose: string;      // 'Main application component'
  status: PhaseFileStatus;  // 'pending' | 'generating' | 'completed' | 'cancelled'
};

State

ts
// Current state
const state = session.state.get();
console.log(state.connection);  // 'disconnected' | 'connecting' | 'connected'
console.log(state.generation);  // { status: 'idle' | 'running' | 'stopped' | 'complete', ... }
console.log(state.phase);       // { status: 'idle' | 'generating' | ... }
console.log(state.preview);     // Preview deployment state
console.log(state.cloudflare);  // Cloudflare deployment state

// Phase timeline (array of all phases)
console.log(state.phases);      // [{ id, name, status, files }, ...]

// Agent metadata (seeded from agent_connected)
console.log(state.behaviorType);      // 'phasic' | 'agentic'
console.log(state.projectType);       // 'app' | 'workflow' | etc.
console.log(state.query);             // Original user prompt
console.log(state.projectName);       // Project name from blueprint
console.log(state.shouldBeGenerating); // Whether agent is actively generating

// Subscribe to changes
session.state.onChange((next, prev) => {
  console.log('State changed:', next);
});

// Workspace file changes
session.workspace.onChange((change) => {
  console.log(change.type, change.path); // 'upsert' | 'delete' | 'reset'
});

WebSocket Reliability

Connections automatically reconnect with exponential backoff.

ts
// Custom retry config
await session.connect({
  retry: {
    enabled: true,        // Default: true
    initialDelayMs: 1000, // Default: 1000
    maxDelayMs: 30000,    // Default: 30000
    maxRetries: 10,       // Default: Infinity
  },
});

// Disable auto-reconnect
await session.connect({ retry: { enabled: false } });

HTTP Retry

HTTP requests automatically retry on 5xx errors.

ts
const client = new AgenticClient({
  baseUrl: 'https://build.cloudflare.dev',
  apiKey: 'vibe_xxx',
  retry: {
    enabled: true,        // Default: true
    initialDelayMs: 1000, // Default: 1000
    maxDelayMs: 10000,    // Default: 10000
    maxRetries: 3,        // Default: 3
  },
});

Utilities

Blueprint Parsing

ts
import { BlueprintStreamParser, blueprintToMarkdown } from '@cf-vibesdk/sdk';

// Parse streaming blueprint chunks
const parser = new BlueprintStreamParser();
parser.append(chunk1);
parser.append(chunk2);
const markdown = parser.toMarkdown();

// Convert blueprint object to markdown
const md = blueprintToMarkdown(blueprint);

Timeout Helper

ts
import { withTimeout, TimeoutError } from '@cf-vibesdk/sdk';

try {
  const result = await withTimeout(someAsyncOperation(), 30000, 'Operation timed out');
} catch (e) {
  if (e instanceof TimeoutError) {
    console.log('Timed out!');
  }
}

Error Handling

All API methods return an ApiResponse<T> discriminated union:

ts
const result = await client.apps.get('app-id');

if (result.success) {
  console.log(result.data);
} else {
  console.error(result.error.message);
}

TypeScript

All types are exported:

ts
import type {
  // Client & Session
  VibeClientOptions,
  BuildOptions,
  BuildSession,
  SessionState,
  SessionFiles,
  SessionPhases,
  
  // Phase Timeline
  PhaseInfo,
  PhaseFile,
  PhaseStatus,
  PhaseFileStatus,
  PhaseEventType,
  PhaseTimelineEvent,
  PhaseTimelineChangeType,
  
  // API
  ApiResponse,
  AppDetails,
  Credentials,
  BehaviorType,
  ProjectType,
  // ... and more
} from '@cf-vibesdk/sdk';

Testing

bash
cd sdk

# Unit tests
bun test

# Integration tests (requires API key)
VIBESDK_INTEGRATION_API_KEY=your_key bun run test:integration

License

MIT


File: space/README.md

@space-do/space

A reusable Cloudflare Durable Object class โ€” SpaceDO โ€” that provides an
isolated workspace and files with preview and deploy support on the Workers
runtime. It is the file/preview/deploy backend used by VibeSDK's ThinkAgent;
the agentic loop itself is powered by @cloudflare/think.

Install

bash
npm install @space-do/space

Peer expectations: compatibility_date >= 2024-09-23 and
compatibility_flags = ["nodejs_compat"]. The class uses SQLite-backed
Durable Object storage and a LOADER Worker Loader binding for Dynamic Worker
previews. Set ENABLE_ARTIFACTS="true" with an ARTIFACTS binding to use
Cloudflare Artifacts for git/version history; otherwise SpaceDO uses its
SQLite-backed workspace filesystem.

Usage

Re-export SpaceDO from your worker entrypoint and declare it in
wrangler.toml:

ts
// src/worker.ts
import type { Env } from "@space-do/space"
export { SpaceDO } from "@space-do/space"
toml
[[durable_objects.bindings]]
name = "SPACE_DO"
class_name = "SpaceDO"

[[artifacts]]
binding = "ARTIFACTS"
namespace = "your-artifacts-namespace"

[[worker_loaders]]
binding = "LOADER"

[[migrations]]
tag = "v1"
new_sqlite_classes = ["SpaceDO"]

Each named SpaceDO instance is an isolated workspace and file layer. Its
filesystem backend is selected when the instance is first initialized and
remains pinned for that app: ENABLE_ARTIFACTS="true" selects Artifacts for
git/version history, while the default uses the SQLite workspace filesystem.
Changing the flag affects only newly-created spaces. Address SpaceDO by
forwarding a Request to a stub from env.SPACE_DO.get(...), or call its typed
RPC methods directly via DO RPC.

HTTP contract

Path Notes
GET|POST /repo.git/* Git Smart HTTP backed by Artifacts synchronization
* /preview/:branch[/*] Dynamic Worker preview for a deployed branch
* /* Typed workspace, git, deploy, rollback, and inspection operations

App database inspector

The generated app exports class App extends DurableObject. SpaceDO hosts
it as a Facet with isolated SQLite storage and exposes inspection through the
listAppTables, queryAppTable, and wipeAppDatabase RPC methods. The result
types are exported for typed consumers:

ts
import type {
  AppDatabaseTable,
  AppDatabaseColumn,
  AppDatabaseReadResult,
} from "@space-do/space"

Building

bash
npm run build       # esbuild โ†’ dist/index.js
npm run typecheck

2. Official Technical Reference & Guides (cloudflare/docs)

AMPProject.org [](https://travis-ci.org/ampproject/docs)

You're looking at the source code of the official website of the AMP Project.
Even if you don't plan on contributing, explore how we've built the site for
a good example of how to build a canonical, responsive standalone AMP site.

How to build the site

Install

Install Grow and npm dependencies:

sh
$ curl https://install.growsdk.org | bash
$ npm install

Get a GitHub token or app id/secret

The docs in the reference section of the AMP Project site are not part of this repository; instead they are read from the amphtml GitHub repository "source of truth." When building the site these docs are fetched via GitHub.

Due to GitHub quotas this fetching will quickly fail unless additional information is provided in the request. This can be either a personal access token or an application id/secret.

Personal access token

  1. Request a GitHub personal access token. Since the docs are fetched from a public repository you only need to select the "public_repo" scope when creating the token. (It's a good practice to limit the token to the scope you need.)
  2. In your shell export the generated personal access token as AMP_DOC_TOKEN

Application client id and secret

If you prefer not to use a personal access token, you can also create a GitHub application and use its id and secret.

  1. Register for a GitHub application
  2. In your shell export the application client id as AMP_DOC_ID and the client secret as AMP_DOC_SECRET

Build

sh
$ grow build

This will generate a static, complete build of the site into the *build folder.

Develop

Note: Be sure to run grow build at least once to pull in reference docs before running the following command.

sh
$ grow run

You can now open http://localhost:8080/ and continue working on the source files, then reload the page to see changes appear.

Support

If you've found an error or inconsistency, please file an issue:
https://github.com/ampproject/docs/issues

Patches are encouraged, and may be submitted by forking this project and
submitting a pull request through GitHub.

License

All image and audio files except in folders "source/img/partners",
"source/img/publishers" and "source/img/quotes" (including *.png, *.jpg, *.svg,
*.mp3, *.wav and *.ogg) are licensed under the CC-BY-NC license. Images in the
excluded folders are not licensed.

All other files are licensed under the Apache 2 license.


Copyright 2015 Google, Inc.

Licensed to the Apache Software Foundation (ASF) under one or more contributor
license agreements. See the NOTICE file distributed with this work for
additional information regarding copyright ownership. The ASF licenses this
file to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.