Skip to main content

Overview

When you run blink init, Blink scaffolds a complete agent project with all the files you need to start building. This page explains what each file does and how the pieces fit together. Understanding the structure of a Blink agent helps you navigate the codebase, add new features, and customize your agent’s behavior effectively.

Project Files

Running blink init creates the following files in your directory:

agent.ts

The heart of your agent - This is where your agent’s logic lives. It defines how your agent handles chat messages, processes requests, and integrates with external services.

package.json

Project configuration - Defines your project metadata, dependencies, and npm scripts.
{
  "name": "my-agent",
  "main": "agent.ts",
  "type": "module",
  "private": true,
  "scripts": {
    "dev": "blink dev",
    "deploy": "blink deploy"
  }
}
Key points:
  • "type": "module" enables ES module syntax
  • "main": "agent.ts" tells Blink where your agent code is
  • Scripts provide convenient commands for development and deployment

tsconfig.json

TypeScript configuration - Configures the TypeScript compiler with settings optimized for Blink agents. Key settings:
  • "moduleResolution": "bundler" - Modern module resolution
  • "strict": true - Enables strict type checking
  • "noEmit": true - No compilation output (Blink handles bundling)
  • "types": ["node"] - Node.js type definitions

.env.local

Development environment variables - Stores API keys and configuration for local development.
# Store local environment variables here.
# They will be used by blink dev for development.
# ANTHROPIC_API_KEY=
# GITHUB_TOKEN=
Important:
  • Used by blink dev during development
  • Automatically hot-reloaded when changed
  • Never committed to version control

.env.production

Production environment variables - Stores secrets for your deployed agent.
# Store production environment variables here.
# They will be upserted as secrets on blink deploy.
# ANTHROPIC_API_KEY=
# GITHUB_TOKEN=
Important:
  • Used by blink deploy to set production secrets
  • Automatically encrypted and stored in Blink Cloud
  • Never committed to version control

.gitignore

Git ignore patterns - Tells Git which files to exclude from version control.
# dependencies
node_modules

# config and build
data

# dotenv environment variables file
.env
.env.*

# Finder (MacOS) folder config
.DS_Store

AGENTS.md

Agent development guide - A comprehensive reference document that provides context and instructions for the Edit Mode agent (the agent that helps you build agents). This file contains:
  • Communication guidelines
  • Agent development best practices
  • Technical knowledge about Blink APIs
  • Code quality standards

Understanding agent.ts

The agent.ts file is structured around a few key concepts. Let’s break down the default template:

Complete Default Agent

import { convertToModelMessages, streamText, tool } from "ai";
import * as blink from "blink";
import { z } from "zod";

// 1. Create the agent instance
const agent = blink.agent();

// 2. Define the chat event handler
agent.on("chat", async ({ messages }) => {
  return streamText({
    // 3. Configure the LLM model
    model: blink.model("anthropic/claude-sonnet-4.5"),

    // 4. Set the system prompt
    system: `You are a basic agent the user will customize.

Suggest the user enters edit mode with Ctrl+T or /edit to customize the agent.
Demonstrate your capabilities with the IP tool.`,

    // 5. Convert and pass messages
    messages: convertToModelMessages(messages),

    // 6. Define tools
    tools: {
      get_ip_info: tool({
        description: "Get IP address information of the computer.",
        inputSchema: z.object({}),
        execute: async () => {
          const response = await fetch("https://ipinfo.io/json");
          return response.json();
        },
      }),
    },
  });
});

// 7. Start the HTTP server
agent.serve();
Let’s examine each section in detail:

Core Components

1. Agent Instance

const agent = blink.agent();
Creates your agent instance. This is the foundation - it manages:
  • HTTP server lifecycle
  • Chat state management
  • Event routing
  • Storage and persistence

2. Chat Event Handler

agent.on("chat", async ({ messages }) => {
  return streamText({
    // ... configuration
  });
});
The chat event is triggered whenever:
  • A user sends a message
  • The model makes tool calls (automatically loops)
  • External services trigger a chat via webhooks
Parameters:
  • messages - Array of all messages in the conversation
  • id - Unique chat identifier
  • abortSignal - For cancellation support
Return value: Must return a streamText() result, Response, or ReadableStream.

3. Model Configuration

model: blink.model("anthropic/claude-sonnet-4.5")
Specifies which LLM to use. Two options: Option 1: Blink Gateway (Quick Start)
model: blink.model("anthropic/claude-sonnet-4.5")
model: blink.model("openai/gpt-4o")
  • Requires blink login or BLINK_TOKEN environment variable
  • Great for getting started quickly
  • Blink handles billing and rate limiting
Option 2: Direct Provider (Production Recommended)
import { anthropic } from "@ai-sdk/anthropic";

model: anthropic("claude-sonnet-4.5", {
  apiKey: process.env.ANTHROPIC_API_KEY
})
  • More control and transparency
  • Direct billing from provider
  • Better for production deployments

4. System Prompt

system: `You are a helpful assistant that...`
The system prompt defines your agent’s:
  • Personality and tone
  • Capabilities and limitations
  • Instructions for behavior
  • Context about available tools
Tips:
  • Be specific about what the agent should and shouldn’t do
  • Describe available tools and when to use them
  • Include any domain-specific knowledge
  • Keep it focused and concise

5. Message Conversion

messages: convertToModelMessages(messages)
Converts Blink’s message format to the AI SDK’s format. The convertToModelMessages function handles:
  • Format transformation
  • Tool call conversion
  • Filtering incomplete tool calls

6. Tools

tools: {
  tool_name: tool({
    description: "What this tool does",
    inputSchema: z.object({
      param: z.string().describe("Parameter description"),
    }),
    execute: async (args, opts) => {
      // Tool implementation
      return result;
    },
  }),
}
Tools give your agent capabilities. Each tool has:
  • description - Tells the model what the tool does
  • inputSchema - Validates parameters using Zod
  • execute - The actual implementation
Example: File Reading Tool
read_file: tool({
  description: "Read the contents of a file",
  inputSchema: z.object({
    path: z.string().describe("Path to the file"),
  }),
  execute: async ({ path }) => {
    const fs = await import("fs/promises");
    return await fs.readFile(path, "utf-8");
  },
})

7. Server Initialization

agent.serve();
Starts the HTTP server that:
  • Listens for incoming requests
  • Routes events to handlers
  • Manages WebSocket connections
  • Handles graceful shutdown

Event Handlers

Blink agents can handle multiple types of events:

Chat Events

agent.on("chat", async ({ messages, id, abortSignal }) => {
  // Handle AI chat processing
  return streamText({ /* ... */ });
});
Triggered when a chat needs AI processing.

HTTP Request Events

agent.on("request", async (request) => {
  // Handle webhooks, OAuth, custom endpoints
  if (request.url.pathname === "/webhook") {
    return new Response("OK", { status: 200 });
  }
  // Return void to pass through to default handling
});
Triggered before Blink processes requests. Use for:
  • Webhook endpoints (Slack, GitHub, etc.)
  • OAuth callbacks
  • Custom API endpoints
  • Request interception

UI Options Events

agent.on("ui", async () => {
  return {
    options: [
      { label: "Option 1", value: "opt1" },
      { label: "Option 2", value: "opt2" },
    ],
  };
});
Provides dynamic UI configuration for chat interfaces.

Error Events

agent.on("error", async (error) => {
  console.error("Agent error:", error);
  // Custom error handling
});
Global error handler for the agent.

Chat Management

Blink automatically manages chat state, but you can control it for advanced use cases:

Creating Chats

// Create or get existing chat with a unique key
const chat = await agent.chat.upsert("unique-key");

// For Slack: Use structured keys
const chat = await agent.chat.upsert([
  "slack",
  teamId,
  channelId,
  threadTs
]);
Chat keys should be:
  • Unique for each conversation context
  • Serializable (strings, numbers, arrays, objects)
  • Consistent across requests

Sending Messages

await agent.chat.sendMessages(chat.id, [
  {
    role: "user",
    parts: [{ type: "text", text: "Hello!" }],
  }
], {
  behavior: "interrupt" // or "enqueue" or "append"
});
Behaviors:
  • interrupt - Stop current processing, handle immediately
  • enqueue - Queue message, process after current chat finishes
  • append - Add to history without triggering processing

Storage API

Persist data across agent restarts:
// Store data
await agent.store.set("user-preferences", {
  theme: "dark",
  notifications: true
});

// Retrieve data
const prefs = await agent.store.get("user-preferences");

// Delete data
await agent.store.delete("user-preferences");

// List keys by prefix
const keys = await agent.store.list("user-", { limit: 100 });
Use cases:
  • OAuth tokens
  • User preferences
  • Cache data
  • Chat associations
  • Rate limiting counters

Advanced Patterns

Multiple Event Handlers

const agent = blink.agent();

// Handle webhooks
agent.on("request", async (request) => {
  if (request.url.pathname === "/slack/events") {
    // Handle Slack webhook
    return handleSlackWebhook(request);
  }
});

// Handle chats
agent.on("chat", async ({ messages }) => {
  return streamText({ /* ... */ });
});

// Handle errors
agent.on("error", async (error) => {
  console.error("Error:", error);
});

agent.serve();

Tool Approvals

Require manual approval for destructive operations:
tools: {
  // Safe tools - no approval needed
  read_file: tool({ /* ... */ }),

  // Destructive tools - require approval
  ...blink.tools.withApproval({
    messages,
    tools: {
      delete_file: tool({ /* ... */ }),
      run_command: tool({ /* ... */ }),
    },
  }),
}

Tool Context

Add authentication or configuration to SDK tools:
import github from "@blink-sdk/github";

tools: {
  ...blink.tools.withContext(github.tools, {
    accessToken: process.env.GITHUB_TOKEN,
  }),
}

Tool Prefixing

Avoid naming collisions when combining multiple SDKs:
tools: {
  ...blink.tools.prefix(github.tools, "github_"),
  ...blink.tools.prefix(gitlab.tools, "gitlab_"),
}

Development Workflow

Local Development

# Start development server with hot-reload
blink dev
Features:
  • Automatic code reload on file changes
  • Environment variable hot-reload from .env.local
  • Reverse tunnel for webhook testing
  • Terminal-based chat interface

Deployment

# Deploy to Blink Cloud
blink deploy
Automatically:
  • Bundles your agent code
  • Uploads environment variables from .env.production
  • Starts your agent in the cloud
  • Provides a public URL

Best Practices

State Management:
  • Never use module-level variables (Maps, Sets, etc.)
  • Always use agent.store for persistent state
  • Agent processes restart on code changes
Tool Design:
  • Write clear, specific descriptions
  • Use Zod to validate all parameters
  • Return structured, parseable data
  • Handle errors gracefully
System Prompts:
  • Be specific about agent behavior
  • Describe available tools and when to use them
  • Include domain-specific context
  • Keep prompts focused and concise
Security:
  • Store secrets in .env.local and .env.production
  • Never commit environment files to Git
  • Use tool approvals for destructive operations
  • Validate all external inputs
Testing:
  • Test locally with blink dev before deploying
  • Verify webhooks with the reverse tunnel
  • Check environment variables are set correctly
  • Test tool integrations thoroughly

Next Steps

Now that you understand the structure of a Blink agent, you can:
I