Skip to main content

Overview

This is a small, functional Blink agent:
import { convertToModelMessages, streamText, tool } from "ai";
import * as blink from "blink";
import { z } from "zod";

const agent = new blink.Agent();

agent.on("chat", async ({ messages }) => {
  return streamText({
    model: "anthropic/claude-sonnet-4.5",
    system: "You are a helpful assistant.",
    messages: convertToModelMessages(messages),
    tools: {
      get_favorite_number: tool({
        inputSchema: z.object({}),
        execute() {
          return 42;
        },
      }),
    },
  });
});

agent.serve();
Conversations are organized into chats. When you send a message, the Blink platform invokes the chat handler - your message and its response become part of the chat’s history. Subsequent invocations access the history via the messages parameter.

Chat Loop

The chat handler is called in a loop, continuing as long as:
  • The agent’s last response contains tool calls
  • All tool calls have completed (output available or errored)
  • No tool calls are pending approval
The loop stops when there are no tool calls, a tool is awaiting approval, or an error occurs.

Chat Handler

agent.on("chat", async ({ messages, id, abortSignal }) => {
  return streamText({
    // ... configuration
  });
});
The chat handler is invoked 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
The chat handler can return responses using the ai SDK, the OpenAI SDK, the Anthropic SDK, the xAI SDK, or the Google SDK.

Chat Management

Blink automatically manages chat state when you use it via the web UI, but for more advanced use cases, you can manage chats manually.

Creating Chats

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

// Or use structured keys
const chat = await agent.chat.upsert(["slack", teamId, channelId, threadTs]);
Chat keys should be:
  • Unique for each conversation context
  • JSON-serializable

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

Webhooks

Blink agents can handle webhooks from external services by defining a request event handler:
agent.on("request", async (request) => {
  return new Response("Hello, world!", { status: 200 });
});
You may use it to trigger actions in your agent, such as sending a message to a chat.
agent.on("request", async (request) => {
  const { chatName, message } = await request.json();
  const chat = await agent.chat.upsert([chatName]);
  await agent.chat.sendMessages(chat.id, [
    {
      role: "user",
      parts: [{ type: "text", text: message }],
    },
  ]);
  return new Response("Message sent", { status: 200 });
});

Storage API

Blink agents can persist data across invocations using the key-value storage agent.store API:
// 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

Tool Approvals

Blink agents can require manual approval for destructive operations using the agent.tools.withApproval API:
tools: {
  // Safe tools - no approval needed
  read_file: tool({ /* ... */ }),

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