Daydreams Logo
Advanced

Extensions

Building your own modular Daydreams extensions.

What Are Extensions?

Extensions are feature packages for your agent. Think of them like apps on your phone - each one adds specific capabilities without you having to build everything from scratch.

Real Examples

Here's what extensions look like in practice:

Using Built-in Extensions

using-extensions.ts
import { createDreams } from "@daydreamsai/core";
import { discord } from "@daydreamsai/discord";
import { twitter } from "@daydreamsai/twitter";
 
const agent = createDreams({
  model: openai("gpt-4o"),
 
  // Add extensions like installing apps
  extensions: [
    discord, // Now agent can read/send Discord messages
    twitter, // Now agent can read/send tweets
  ],
});
 
// That's it! Your agent now speaks Discord and Twitter

What Each Extension Gives You

extension-features.ts
// The Discord extension adds:
// ✅ Automatic Discord client connection
// ✅ Listen for Discord messages (inputs)
// ✅ Send Discord replies (outputs)
// ✅ Track conversation context (contexts)
// ✅ Discord-specific actions (ban, kick, etc.)
 
// The Twitter extension adds:
// ✅ Twitter API client management
// ✅ Listen for mentions/DMs (inputs)
// ✅ Send tweets/replies (outputs)
// ✅ Track follower context (contexts)
// ✅ Twitter actions (follow, like, retweet)

The Problem: Building Everything From Scratch

Without extensions, you'd have to build every feature manually:

manual-agent-building.ts
// ❌ Without extensions - hundreds of lines of setup code
const agent = createDreams({
  model: openai("gpt-4o"),
 
  // Manually define every context
  contexts: {
    discordGuild: context({
      /* Discord server context */
    }),
    discordChannel: context({
      /* Discord channel context */
    }),
    discordUser: context({
      /* Discord user context */
    }),
    twitterUser: context({
      /* Twitter user context */
    }),
    twitterThread: context({
      /* Twitter thread context */
    }),
    // ... 50+ more contexts
  },
 
  // Manually define every action
  actions: [
    action({ name: "send-discord-message" /* ... lots of code ... */ }),
    action({ name: "ban-discord-user" /* ... lots of code ... */ }),
    action({ name: "create-discord-channel" /* ... lots of code ... */ }),
    action({ name: "send-tweet" /* ... lots of code ... */ }),
    action({ name: "follow-twitter-user" /* ... lots of code ... */ }),
    // ... 100+ more actions
  ],
 
  // Manually set up all the inputs/outputs
  inputs: {
    "discord:message": input({
      /* Complex Discord API setup */
    }),
    "discord:reaction": input({
      /* More Discord API code */
    }),
    "twitter:mention": input({
      /* Complex Twitter API setup */
    }),
    // ... dozens more
  },
 
  // Plus manage all the API clients, authentication, etc.
  // This would be thousands of lines of code!
});

The Solution: Extensions Package Everything

With extensions, complex features become simple one-liners:

simple-agent-building.ts
// ✅ With extensions - clean and simple
const agent = createDreams({
  model: openai("gpt-4o"),
 
  extensions: [
    discord, // Hundreds of lines of Discord integration
    twitter, // Hundreds of lines of Twitter integration
  ],
 
  // Done! Everything just works
});

Building Your First Extension

Let's build a simple weather extension step by step:

1. Define What Your Extension Does

weather-extension-plan.ts
// Weather extension should provide:
// - Action to get current weather
// - Action to get weather forecast
// - Context to remember user's preferred location
// - Service to manage weather API client

2. Create the Service (API Management)

weather-service.ts
import { service } from "@daydreamsai/core";
 
const weatherService = service({
  name: "weather",
 
  // How to create the weather API client
  register: (container) => {
    container.singleton("weatherClient", () => ({
      apiKey: process.env.WEATHER_API_KEY,
      baseUrl: "https://api.openweathermap.org/data/2.5",
 
      async getCurrentWeather(location: string) {
        const response = await fetch(
          `${this.baseUrl}/weather?q=${location}&appid=${this.apiKey}&units=metric`
        );
        return response.json();
      },
 
      async getForecast(location: string) {
        const response = await fetch(
          `${this.baseUrl}/forecast?q=${location}&appid=${this.apiKey}&units=metric`
        );
        return response.json();
      },
    }));
  },
 
  // Initialize when agent starts
  boot: async (container) => {
    const client = container.resolve("weatherClient");
    console.log("Weather service ready!");
  },
});

3. Create the Context (User Preferences)

weather-context.ts
import { context } from "@daydreamsai/core";
import { z } from "zod";
 
const weatherContext = context({
  type: "weather-preferences",
  schema: z.object({ userId: z.string() }),
 
  create: () => ({
    defaultLocation: null,
    units: "metric", // celsius by default
    lastChecked: null,
    favoriteLocations: [],
  }),
 
  render: (state) => `
User Weather Preferences:
- Default location: ${state.memory.defaultLocation || "Not set"}
- Units: ${state.memory.units}
- Favorite locations: ${state.memory.favoriteLocations.join(", ") || "None"}
- Last checked: ${state.memory.lastChecked || "Never"}
  `,
});

4. Create the Actions (What Agent Can Do)

weather-actions.ts
import { action } from "@daydreamsai/core";
import { z } from "zod";
 
const getCurrentWeatherAction = action({
  name: "get-current-weather",
  description: "Get the current weather for a location",
  schema: z.object({
    location: z.string().describe("City name, e.g., 'San Francisco, CA'"),
  }),
 
  handler: async ({ location }, ctx) => {
    const weatherClient = ctx.container.resolve("weatherClient");
 
    try {
      const weather = await weatherClient.getCurrentWeather(location);
 
      // Update user's context
      ctx.memory.lastChecked = new Date().toISOString();
      if (!ctx.memory.defaultLocation) {
        ctx.memory.defaultLocation = location;
      }
 
      return {
        success: true,
        location: weather.name,
        temperature: `${weather.main.temp}°C`,
        description: weather.weather[0].description,
        humidity: `${weather.main.humidity}%`,
        windSpeed: `${weather.wind.speed} m/s`,
      };
    } catch (error) {
      return {
        success: false,
        error: "Could not fetch weather data",
        message: error.message,
      };
    }
  },
});
 
const setDefaultLocationAction = action({
  name: "set-default-weather-location",
  description: "Set user's default location for weather",
  schema: z.object({
    location: z.string(),
  }),
 
  handler: async ({ location }, ctx) => {
    ctx.memory.defaultLocation = location;
 
    // Add to favorites if not already there
    if (!ctx.memory.favoriteLocations.includes(location)) {
      ctx.memory.favoriteLocations.push(location);
    }
 
    return {
      success: true,
      message: `Default location set to ${location}`,
    };
  },
});

5. Bundle Everything Into an Extension

weather-extension.ts
import { extension } from "@daydreamsai/core";
 
export const weather = extension({
  name: "weather",
 
  // Services this extension needs
  services: [weatherService],
 
  // Contexts this extension provides
  contexts: {
    "weather-preferences": weatherContext,
  },
 
  // Actions this extension provides
  actions: [getCurrentWeatherAction, setDefaultLocationAction],
 
  // Optional: Run setup when extension is added
  install: async (agent) => {
    console.log("Weather extension installed!");
    // Could do additional setup here if needed
  },
});

6. Use Your Extension

weather-agent.ts
import { createDreams } from "@daydreamsai/core";
import { weather } from "./weather-extension";
 
const agent = createDreams({
  model: openai("gpt-4o"),
  extensions: [weather],
});
 
await agent.start();
 
// Now your agent can:
// - Check weather for any location
// - Remember user's preferred locations
// - Set default locations for users
// - All with proper API management

Extension Lifecycle

Here's what happens when your agent starts:

extension-lifecycle.txt
1. Agent Creation
   └── Extensions added to agent.extensions[]

2. agent.start() called
   ├── For each extension:
   │   ├── Register all services
   │   ├── Merge contexts into agent
   │   ├── Merge actions into agent
   │   ├── Merge inputs into agent
   │   └── Merge outputs into agent

   ├── Boot all services (connect to APIs, databases, etc.)

   ├── Call extension.install() for each extension

   └── Start all inputs (begin listening for events)

3. Agent Ready
   └── All extension features available to LLM

Advanced Extension Features

Inputs and Outputs

Extensions can also define how agents listen and respond:

weather-inputs-outputs.ts
const weatherExtension = extension({
  name: "weather",
 
  // ... services, contexts, actions ...
 
  // Listen for weather-related events
  inputs: {
    "weather:alert": input({
      subscribe: (send, agent) => {
        // Listen for severe weather alerts
        const weatherClient = agent.container.resolve("weatherClient");
 
        setInterval(async () => {
          const alerts = await weatherClient.getAlerts();
 
          for (const alert of alerts) {
            send({
              type: "weather:alert",
              data: {
                type: alert.type,
                severity: alert.severity,
                location: alert.location,
                message: alert.message,
              },
            });
          }
        }, 60000); // Check every minute
      },
    }),
  },
 
  // Send weather notifications
  outputs: {
    "weather:notification": output({
      schema: z.object({
        message: z.string(),
        location: z.string(),
        urgency: z.enum(["low", "medium", "high"]),
      }),
 
      handler: async ({ message, location, urgency }, ctx) => {
        // Could send via email, SMS, Discord, etc.
        console.log(
          `[${urgency.toUpperCase()}] Weather alert for ${location}: ${message}`
        );
 
        // Could also trigger other actions based on urgency
        if (urgency === "high") {
          // Maybe send emergency notifications
        }
      },
    }),
  },
});

Extension Dependencies

Extensions can depend on other extensions:

weather-discord-extension.ts
import { discord } from "@daydreamsai/discord";
import { weather } from "./weather-extension";
 
const weatherDiscordBot = extension({
  name: "weather-discord-bot",
 
  // This extension requires both Discord and Weather
  services: [], // No additional services needed
 
  // Add a Discord-specific weather command
  actions: [
    action({
      name: "send-weather-to-discord",
      description: "Send weather info to a Discord channel",
      schema: z.object({
        channelId: z.string(),
        location: z.string(),
      }),
 
      handler: async ({ channelId, location }, ctx) => {
        // Use weather extension's client
        const weatherClient = ctx.container.resolve("weatherClient");
        const weather = await weatherClient.getCurrentWeather(location);
 
        // Use Discord extension's client
        const discordClient = ctx.container.resolve("discordClient");
        const channel = await discordClient.channels.fetch(channelId);
 
        await channel.send(
          `🌤️ Weather in ${location}: ${weather.main.temp}°C, ${weather.weather[0].description}`
        );
 
        return { success: true };
      },
    }),
  ],
});
 
// Use all three extensions together
const agent = createDreams({
  model: openai("gpt-4o"),
  extensions: [
    discord, // Provides Discord functionality
    weather, // Provides weather functionality
    weatherDiscordBot, // Combines both for Discord weather bot
  ],
});

Best Practices

1. Focus on One Domain

focused-extension.ts
// ✅ Good - focused on weather
const weather = extension({
  name: "weather",
  // All features related to weather
});
 
// ❌ Bad - mixing unrelated features
const everything = extension({
  name: "everything",
  // Weather + Discord + Trading + Gaming features mixed together
});

2. Provide Complete Functionality

complete-extension.ts
// ✅ Good - provides everything needed for weather
const weather = extension({
  name: "weather",
  services: [weatherService], // API management
  contexts: { preferences: weatherContext }, // User preferences
  actions: [getCurrentWeather, setLocation], // Core functionality
  inputs: { alerts: weatherAlerts }, // Listen for alerts
  outputs: { notify: weatherNotify }, // Send notifications
});
 
// ❌ Bad - incomplete, missing key features
const incompleteWeather = extension({
  name: "weather",
  actions: [getCurrentWeather], // Only one action, no context or API management
});

3. Clear Service Dependencies

clear-dependencies.ts
// ✅ Good - clear what services this extension needs
const trading = extension({
  name: "trading",
  services: [
    alpacaService, // For executing trades
    marketDataService, // For getting quotes
    riskService, // For risk management
  ],
  // ... rest of extension
});
 
// ❌ Bad - unclear dependencies
const trading = extension({
  name: "trading",
  services: [
    everythingService, // What does this provide?
  ],
});

4. Meaningful Names

meaningful-names.ts
// ✅ Good - clear what each extension does
const discord = extension({ name: "discord" });
const weather = extension({ name: "weather" });
const tradingBot = extension({ name: "trading-bot" });
 
// ❌ Bad - unclear names
const ext1 = extension({ name: "ext1" });
const myStuff = extension({ name: "my-stuff" });
const helper = extension({ name: "helper" });

Publishing Extensions

Once you've built an extension, you can share it:

1. Package Structure

extension-package.txt
my-weather-extension/
├── src/
│   ├── services/
│   │   └── weather-service.ts
│   ├── contexts/
│   │   └── weather-context.ts
│   ├── actions/
│   │   ├── get-weather.ts
│   │   └── set-location.ts
│   ├── index.ts              # Export the extension
│   └── types.ts              # TypeScript types
├── package.json
├── tsconfig.json
└── README.md

2. Package.json

package.json
{
  "name": "@yourorg/daydreams-weather",
  "version": "1.0.0",
  "description": "Weather extension for Daydreams agents",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "peerDependencies": {
    "@daydreamsai/core": "^1.0.0"
  },
  "dependencies": {
    "zod": "^3.0.0"
  }
}

3. Export Your Extension

src/index.ts
export { weather } from "./weather-extension";
export type { WeatherData, WeatherAlert } from "./types";

4. Usage by Others

using-published-extension.ts
import { createDreams } from "@daydreamsai/core";
import { weather } from "@yourorg/daydreams-weather";
 
const agent = createDreams({
  model: openai("gpt-4o"),
  extensions: [weather],
});

Next Steps

Key Takeaways

  • Extensions are feature packages - Like apps for your agent
  • Bundle related functionality - Contexts, actions, inputs, outputs together
  • Automatic lifecycle management - Services boot, features register automatically
  • Reusable and shareable - Build once, use everywhere
  • Clean agent configuration - Add complex features with one line

Extensions let you build powerful agents by combining focused, reusable feature packages.