From 19e9d1b3d3f870bbeaa12181491a1e9abfc85554 Mon Sep 17 00:00:00 2001
From: ponderingdemocritus <ponderingdemocritus@protonmail.com>
Date: Sun, 24 Nov 2024 11:05:25 +1100
Subject: [PATCH 1/4] fix: voice

---
 agent/src/index.ts                   |   3 +-
 packages/client-discord/src/voice.ts | 178 +++++++++++++++++++--------
 2 files changed, 131 insertions(+), 50 deletions(-)

diff --git a/agent/src/index.ts b/agent/src/index.ts
index 1d8f1c0817f..afd04bb125a 100644
--- a/agent/src/index.ts
+++ b/agent/src/index.ts
@@ -34,6 +34,7 @@ import path from "path";
 import { fileURLToPath } from "url";
 import { character } from "./character.ts";
 import type { DirectClient } from "@ai16z/client-direct";
+import blobert from "./blobert.ts";
 
 const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
 const __dirname = path.dirname(__filename); // get the name of the directory
@@ -317,7 +318,7 @@ const startAgents = async () => {
 
     let charactersArg = args.characters || args.character;
 
-    let characters = [character];
+    let characters = [blobert];
 
     if (charactersArg) {
         characters = await loadCharacters(charactersArg);
diff --git a/packages/client-discord/src/voice.ts b/packages/client-discord/src/voice.ts
index c59dafcb289..a1ec22eb35b 100644
--- a/packages/client-discord/src/voice.ts
+++ b/packages/client-discord/src/voice.ts
@@ -21,10 +21,12 @@ import {
     NoSubscriberBehavior,
     StreamType,
     VoiceConnection,
+    VoiceConnectionStatus,
     createAudioPlayer,
     createAudioResource,
     getVoiceConnection,
     joinVoiceChannel,
+    entersState,
 } from "@discordjs/voice";
 import {
     BaseGuildVoiceChannel,
@@ -230,6 +232,7 @@ export class VoiceManager extends EventEmitter {
                 console.error("Error leaving voice channel:", error);
             }
         }
+
         const connection = joinVoiceChannel({
             channelId: channel.id,
             guildId: channel.guild.id,
@@ -238,38 +241,103 @@ export class VoiceManager extends EventEmitter {
             selfMute: false,
         });
 
-        const me = channel.guild.members.me;
-        if (me?.voice && me.permissions.has("DeafenMembers")) {
-            await me.voice.setDeaf(false);
-            await me.voice.setMute(false);
-        } else {
-            elizaLogger.log("Bot lacks permission to modify voice state");
-        }
+        try {
+            // Wait for either Ready or Signalling state
+            await Promise.race([
+                entersState(connection, VoiceConnectionStatus.Ready, 20_000),
+                entersState(
+                    connection,
+                    VoiceConnectionStatus.Signalling,
+                    20_000
+                ),
+            ]);
+
+            // Log connection success
+            elizaLogger.log(
+                `Voice connection established in state: ${connection.state.status}`
+            );
 
-        for (const [, member] of channel.members) {
-            if (!member.user.bot) {
-                this.monitorMember(member, channel);
-            }
-        }
+            // Set up ongoing state change monitoring
+            connection.on("stateChange", async (oldState, newState) => {
+                elizaLogger.log(
+                    `Voice connection state changed from ${oldState.status} to ${newState.status}`
+                );
 
-        connection.on("error", (error) => {
-            console.error("Voice connection error:", error);
-        });
+                if (newState.status === VoiceConnectionStatus.Disconnected) {
+                    elizaLogger.log("Handling disconnection...");
 
-        connection.receiver.speaking.on("start", (userId: string) => {
-            const user = channel.members.get(userId);
-            if (!user?.user.bot) {
-                this.monitorMember(user as GuildMember, channel);
-                this.streams.get(userId)?.emit("speakingStarted");
+                    try {
+                        // Try to reconnect if disconnected
+                        await Promise.race([
+                            entersState(
+                                connection,
+                                VoiceConnectionStatus.Signalling,
+                                5_000
+                            ),
+                            entersState(
+                                connection,
+                                VoiceConnectionStatus.Connecting,
+                                5_000
+                            ),
+                        ]);
+                        // Seems to be reconnecting to a new channel
+                        elizaLogger.log("Reconnecting to channel...");
+                    } catch (e) {
+                        // Seems to be a real disconnect, destroy and cleanup
+                        elizaLogger.log(
+                            "Disconnection confirmed - cleaning up..." + e
+                        );
+                        connection.destroy();
+                        this.connections.delete(channel.id);
+                    }
+                } else if (
+                    newState.status === VoiceConnectionStatus.Destroyed
+                ) {
+                    this.connections.delete(channel.id);
+                } else if (
+                    !this.connections.has(channel.id) &&
+                    (newState.status === VoiceConnectionStatus.Ready ||
+                        newState.status === VoiceConnectionStatus.Signalling)
+                ) {
+                    this.connections.set(channel.id, connection);
+                }
+            });
+
+            connection.on("error", (error) => {
+                elizaLogger.log("Voice connection error:", error);
+                // Don't immediately destroy - let the state change handler deal with it
+                elizaLogger.log(
+                    "Connection error - will attempt to recover..."
+                );
+            });
+
+            // Store the connection
+            this.connections.set(channel.id, connection);
+
+            // Continue with voice state modifications
+            const me = channel.guild.members.me;
+            if (me?.voice && me.permissions.has("DeafenMembers")) {
+                try {
+                    await me.voice.setDeaf(false);
+                    await me.voice.setMute(false);
+                } catch (error) {
+                    elizaLogger.log("Failed to modify voice state:", error);
+                    // Continue even if this fails
+                }
             }
-        });
 
-        connection.receiver.speaking.on("end", async (userId: string) => {
-            const user = channel.members.get(userId);
-            if (!user?.user.bot) {
-                this.streams.get(userId)?.emit("speakingStopped");
+            // Set up member monitoring
+            for (const [, member] of channel.members) {
+                if (!member.user.bot) {
+                    await this.monitorMember(member, channel);
+                }
             }
-        });
+        } catch (error) {
+            elizaLogger.log("Failed to establish voice connection:", error);
+            connection.destroy();
+            this.connections.delete(channel.id);
+            throw error;
+        }
     }
 
     private async monitorMember(
@@ -780,7 +848,7 @@ export class VoiceManager extends EventEmitter {
 
         audioPlayer.on(
             "stateChange",
-            (oldState: any, newState: { status: string }) => {
+            (_oldState: any, newState: { status: string }) => {
                 if (newState.status == "idle") {
                     const idleTime = Date.now();
                     console.log(
@@ -792,34 +860,46 @@ export class VoiceManager extends EventEmitter {
     }
 
     async handleJoinChannelCommand(interaction: any) {
-        const channelId = interaction.options.get("channel")?.value as string;
-        if (!channelId) {
-            await interaction.reply("Please provide a voice channel to join.");
-            return;
-        }
-        const guild = interaction.guild;
-        if (!guild) {
-            return;
-        }
-        const voiceChannel = interaction.guild.channels.cache.find(
-            (channel: VoiceChannel) =>
-                channel.id === channelId &&
-                channel.type === ChannelType.GuildVoice
-        );
+        try {
+            // Defer the reply immediately to prevent interaction timeout
+            await interaction.deferReply();
+
+            const channelId = interaction.options.get("channel")
+                ?.value as string;
+            if (!channelId) {
+                await interaction.editReply(
+                    "Please provide a voice channel to join."
+                );
+                return;
+            }
 
-        if (!voiceChannel) {
-            await interaction.reply("Voice channel not found!");
-            return;
-        }
+            const guild = interaction.guild;
+            if (!guild) {
+                await interaction.editReply("Could not find guild.");
+                return;
+            }
 
-        try {
-            this.joinChannel(voiceChannel as BaseGuildVoiceChannel);
-            await interaction.reply(
+            const voiceChannel = interaction.guild.channels.cache.find(
+                (channel: VoiceChannel) =>
+                    channel.id === channelId &&
+                    channel.type === ChannelType.GuildVoice
+            );
+
+            if (!voiceChannel) {
+                await interaction.editReply("Voice channel not found!");
+                return;
+            }
+
+            await this.joinChannel(voiceChannel as BaseGuildVoiceChannel);
+            await interaction.editReply(
                 `Joined voice channel: ${voiceChannel.name}`
             );
         } catch (error) {
             console.error("Error joining voice channel:", error);
-            await interaction.reply("Failed to join the voice channel.");
+            // Use editReply instead of reply for the error case
+            await interaction
+                .editReply("Failed to join the voice channel.")
+                .catch(console.error);
         }
     }
 

From 06aeacf6c90bc66d1b5f830bef618abc2a843a10 Mon Sep 17 00:00:00 2001
From: ponderingdemocritus <ponderingdemocritus@protonmail.com>
Date: Sun, 24 Nov 2024 14:28:15 +1100
Subject: [PATCH 2/4] fix: null issue

---
 packages/adapter-postgres/src/index.ts | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/packages/adapter-postgres/src/index.ts b/packages/adapter-postgres/src/index.ts
index 9b0f0f7b78f..3b84b2caf3a 100644
--- a/packages/adapter-postgres/src/index.ts
+++ b/packages/adapter-postgres/src/index.ts
@@ -59,14 +59,16 @@ export class PostgresDatabaseAdapter
             while (retryCount < maxRetries) {
                 try {
                     const delay = baseDelay * Math.pow(2, retryCount);
-                    elizaLogger.log(`Attempting to reconnect in ${delay}ms...`);
+                    elizaLogger.warn(
+                        `Attempting to reconnect in ${delay}ms...`
+                    );
                     await new Promise((resolve) => setTimeout(resolve, delay));
 
                     // Create new pool with same config
                     this.pool = new pg.Pool(this.pool.options);
                     await this.testConnection();
 
-                    elizaLogger.log("Successfully reconnected to database");
+                    elizaLogger.success("Successfully reconnected to database");
                     return;
                 } catch (error) {
                     retryCount++;
@@ -116,7 +118,7 @@ export class PostgresDatabaseAdapter
         try {
             client = await this.pool.connect();
             const result = await client.query("SELECT NOW()");
-            elizaLogger.log(
+            elizaLogger.success(
                 "Database connection test successful:",
                 result.rows[0]
             );
@@ -215,7 +217,7 @@ export class PostgresDatabaseAdapter
         if (rows.length === 0) return null;
 
         const account = rows[0];
-        elizaLogger.log("account", account);
+        elizaLogger.debug("account", account);
         return {
             ...account,
             details:
@@ -346,7 +348,7 @@ export class PostgresDatabaseAdapter
         if (!params.roomId) throw new Error("roomId is required");
         let sql = `SELECT * FROM memories WHERE type = $1 AND "agentId" = $2 AND "roomId" = $3`;
         const values: any[] = [params.tableName, params.agentId, params.roomId];
-        let paramCount = 2;
+        let paramCount = 3; // Updated to start at 3 since we already have 3 parameters
 
         if (params.start) {
             paramCount++;
@@ -366,9 +368,9 @@ export class PostgresDatabaseAdapter
 
         sql += ' ORDER BY "createdAt" DESC';
 
-        if (params.count) {
+        if (params.count && typeof params.count === "number") {
             paramCount++;
-            sql += ` LIMIT $${paramCount}`;
+            sql += ` LIMIT $${paramCount}::integer`; // Cast to integer
             values.push(params.count);
         }
 
@@ -628,7 +630,7 @@ export class PostgresDatabaseAdapter
             );
 
             if (existingParticipant.rows.length > 0) {
-                elizaLogger.log(
+                elizaLogger.error(
                     `Participant with userId ${userId} already exists in room ${roomId}.`
                 );
                 return true; // Exit early if the participant already exists
@@ -643,7 +645,7 @@ export class PostgresDatabaseAdapter
             return true;
         } catch (error) {
             if (error instanceof DatabaseError) {
-                elizaLogger.log("Error adding participant", error);
+                elizaLogger.error("Error adding participant", error);
                 // This is to prevent duplicate participant error in case of a race condition
                 // Handle unique constraint violation error (code 23505)
                 if (error.code === "23505") {

From f14e9e0fd6568e1c83e6720d8896f2acd000a174 Mon Sep 17 00:00:00 2001
From: ponderingdemocritus <ponderingdemocritus@protonmail.com>
Date: Sun, 24 Nov 2024 16:33:43 +1100
Subject: [PATCH 3/4] fix: revert

---
 agent/src/index.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/agent/src/index.ts b/agent/src/index.ts
index afd04bb125a..383e1ef3350 100644
--- a/agent/src/index.ts
+++ b/agent/src/index.ts
@@ -34,7 +34,6 @@ import path from "path";
 import { fileURLToPath } from "url";
 import { character } from "./character.ts";
 import type { DirectClient } from "@ai16z/client-direct";
-import blobert from "./blobert.ts";
 
 const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
 const __dirname = path.dirname(__filename); // get the name of the directory
@@ -179,6 +178,7 @@ function initializeDatabase(dataDir: string) {
     if (process.env.POSTGRES_URL) {
         const db = new PostgresDatabaseAdapter({
             connectionString: process.env.POSTGRES_URL,
+            parseInputs: true,
         });
         return db;
     } else {
@@ -318,7 +318,7 @@ const startAgents = async () => {
 
     let charactersArg = args.characters || args.character;
 
-    let characters = [blobert];
+    let characters = [character];
 
     if (charactersArg) {
         characters = await loadCharacters(charactersArg);

From f44f0dc4b8e46688591ce5772516ba19a5d7da66 Mon Sep 17 00:00:00 2001
From: ponderingdemocritus <ponderingdemocritus@protonmail.com>
Date: Sun, 24 Nov 2024 22:45:15 +1100
Subject: [PATCH 4/4] feat: lint

---
 agent/package.json                            |   1 +
 agent/src/index.ts                            |   3 +-
 packages/core/src/runtime.ts                  |   6 +-
 packages/core/src/types.ts                    |   5 +-
 packages/plugin-eternum/.npmignore            |   6 +
 packages/plugin-eternum/package.json          |  27 ++
 packages/plugin-eternum/readme.md             |  54 +++
 packages/plugin-eternum/src/actions/dummy.ts  | 120 +++++
 .../plugin-eternum/src/actions/generative.ts  | 266 +++++++++++
 .../src/actions/generativeActions.ts          |  21 +
 packages/plugin-eternum/src/client/dojo.ts    |  47 ++
 packages/plugin-eternum/src/enviroment.ts     |  40 ++
 packages/plugin-eternum/src/index.ts          |  26 ++
 .../plugin-eternum/src/providers/index.ts     | 106 +++++
 packages/plugin-eternum/src/types/index.ts    |   8 +
 packages/plugin-eternum/src/utils/execute.ts  |  85 ++++
 packages/plugin-eternum/src/utils/index.ts    |  14 +
 packages/plugin-eternum/tsconfig.json         |  21 +
 packages/plugin-eternum/tsup.config.ts        |  21 +
 pnpm-lock.yaml                                | 430 +++++++++++++++---
 scripts/build.sh                              |   1 +
 21 files changed, 1235 insertions(+), 73 deletions(-)
 create mode 100644 packages/plugin-eternum/.npmignore
 create mode 100644 packages/plugin-eternum/package.json
 create mode 100644 packages/plugin-eternum/readme.md
 create mode 100644 packages/plugin-eternum/src/actions/dummy.ts
 create mode 100644 packages/plugin-eternum/src/actions/generative.ts
 create mode 100644 packages/plugin-eternum/src/actions/generativeActions.ts
 create mode 100644 packages/plugin-eternum/src/client/dojo.ts
 create mode 100644 packages/plugin-eternum/src/enviroment.ts
 create mode 100644 packages/plugin-eternum/src/index.ts
 create mode 100644 packages/plugin-eternum/src/providers/index.ts
 create mode 100644 packages/plugin-eternum/src/types/index.ts
 create mode 100644 packages/plugin-eternum/src/utils/execute.ts
 create mode 100644 packages/plugin-eternum/src/utils/index.ts
 create mode 100644 packages/plugin-eternum/tsconfig.json
 create mode 100644 packages/plugin-eternum/tsup.config.ts

diff --git a/agent/package.json b/agent/package.json
index 2eb890ba8a5..cf1a8725455 100644
--- a/agent/package.json
+++ b/agent/package.json
@@ -26,6 +26,7 @@
         "@ai16z/plugin-solana": "workspace:*",
         "@ai16z/plugin-starknet": "workspace:*",
         "@ai16z/plugin-coinbase": "workspace:*",
+        "@ai16z/plugin-eternum": "workspace:*",
         "readline": "^1.3.0",
         "ws": "^8.18.0",
         "yargs": "17.7.2"
diff --git a/agent/src/index.ts b/agent/src/index.ts
index 383e1ef3350..036729b0b0c 100644
--- a/agent/src/index.ts
+++ b/agent/src/index.ts
@@ -34,6 +34,7 @@ import path from "path";
 import { fileURLToPath } from "url";
 import { character } from "./character.ts";
 import type { DirectClient } from "@ai16z/client-direct";
+import blobert from "./blobert.ts";
 
 const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
 const __dirname = path.dirname(__filename); // get the name of the directory
@@ -318,7 +319,7 @@ const startAgents = async () => {
 
     let charactersArg = args.characters || args.character;
 
-    let characters = [character];
+    let characters = [blobert];
 
     if (charactersArg) {
         characters = await loadCharacters(charactersArg);
diff --git a/packages/core/src/runtime.ts b/packages/core/src/runtime.ts
index 18469e35bea..1c535712206 100644
--- a/packages/core/src/runtime.ts
+++ b/packages/core/src/runtime.ts
@@ -1166,7 +1166,10 @@ Text: ${attachment.text}
         return { ...initialState, ...actionState } as State;
     }
 
-    async updateRecentMessageState(state: State): Promise<State> {
+    async updateRecentMessageState(
+        state: State,
+        additionalKeys: { [key: string]: unknown } = {}
+    ): Promise<State> {
         const conversationLength = this.getConversationLength();
         const recentMessagesData = await this.messageManager.getMemories({
             roomId: state.roomId,
@@ -1227,6 +1230,7 @@ Text: ${attachment.text}
             ),
             recentMessagesData,
             attachments: formattedAttachments,
+            ...additionalKeys,
         } as State;
     }
 }
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 4864c526e8c..44862f1a348 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -1008,7 +1008,10 @@ export interface IAgentRuntime {
         additionalKeys?: { [key: string]: unknown }
     ): Promise<State>;
 
-    updateRecentMessageState(state: State): Promise<State>;
+    updateRecentMessageState(
+        state: State,
+        additionalKeys?: { [key: string]: unknown }
+    ): Promise<State>;
 }
 
 export interface IImageDescriptionService extends Service {
diff --git a/packages/plugin-eternum/.npmignore b/packages/plugin-eternum/.npmignore
new file mode 100644
index 00000000000..078562eceab
--- /dev/null
+++ b/packages/plugin-eternum/.npmignore
@@ -0,0 +1,6 @@
+*
+
+!dist/**
+!package.json
+!readme.md
+!tsup.config.ts
\ No newline at end of file
diff --git a/packages/plugin-eternum/package.json b/packages/plugin-eternum/package.json
new file mode 100644
index 00000000000..9296d191269
--- /dev/null
+++ b/packages/plugin-eternum/package.json
@@ -0,0 +1,27 @@
+{
+    "name": "@ai16z/plugin-eternum",
+    "version": "0.1.4-alpha.3",
+    "main": "dist/index.js",
+    "type": "module",
+    "types": "dist/index.d.ts",
+    "dependencies": {
+        "@ai16z/eliza": "0.1.4-alpha.3",
+        "@ai16z/plugin-trustdb": "0.1.4-alpha.3",
+        "@avnu/avnu-sdk": "^2.1.1",
+        "@uniswap/sdk-core": "^6.0.0",
+        "@unruggable_starknet/core": "^0.1.0",
+        "langsmith": "^0.2.7",
+        "openai": "^4.73.0",
+        "starknet": "^6.17.0",
+        "tsup": "^8.3.5",
+        "vitest": "^2.1.4"
+    },
+    "scripts": {
+        "build": "tsup --format esm --dts",
+        "test": "vitest run",
+        "test:watch": "vitest"
+    },
+    "peerDependencies": {
+        "whatwg-url": "7.1.0"
+    }
+}
diff --git a/packages/plugin-eternum/readme.md b/packages/plugin-eternum/readme.md
new file mode 100644
index 00000000000..e06eb4f4413
--- /dev/null
+++ b/packages/plugin-eternum/readme.md
@@ -0,0 +1,54 @@
+# Eternum Plugin
+
+## Overview
+
+The idea here is that we provide the agent with context about the game:
+
+1. Decide Task
+2. Decide Action
+3. Execute Action
+
+### Runtime
+
+Each of these steps could themselves be a few steps, this is very rough.
+
+Look at [eternum/src/provider/index.ts](./eternum/src/provider/index.ts) for rough example of how the messages could
+work.
+
+_Query 1:_
+
+1. Look at goal, state of realm. So resource balances, buildings etc (this is fetched from a DB right?)
+2. Decide what information is needed for the context. (this is done by an LLM?) Create a graphql query for it, and fetch the information. Keep in
+   context.
+3. Decide if you want to take an action or not. (we have a list of actions defined in the same way as eliza?)
+
+    - If not, return and ask question again in 1 minute
+    - If yes, continue to Query 2
+
+_Query 2:_
+
+1. Pass context from Query 1 into another lookup to find how to take that action.
+2. consume the cache of available actions
+   (wouldn't this be redundant if we have actions defined in the Eliza way?)
+
+_Execute 3:_
+
+1. Take an action or not.
+
+if success -> save memory
+
+Repeat
+
+### TODO:
+
+1. Create file will all available actions formatted calldata. Don't we already have that in the EternumProvider? Or do
+   you mean Eliza actions?
+
+-   Add TypeDoc to all the Providers. In the example of the typedoc include formatted call data.
+-   Write a script to extract only the typedoc information and save it to a file. This is what we will cache in the db. We
+    need to be able to run this again so making a helper function is the key
+
+2. Get all graphql models in a cache somehow. The entire generated files are too big. We need to compress the
+   information of the available queries just enough that the model will still understand it.
+
+3. Write Action that runs like the above. We just want everything encapsulated within that one ACTION for gameplay.
diff --git a/packages/plugin-eternum/src/actions/dummy.ts b/packages/plugin-eternum/src/actions/dummy.ts
new file mode 100644
index 00000000000..e08cda649da
--- /dev/null
+++ b/packages/plugin-eternum/src/actions/dummy.ts
@@ -0,0 +1,120 @@
+export const GAME_DESCRIPTION = `
+Eternum is a browser-based game where you manage a realm and its resources.
+
+To build a Realm you need the required resources. Which are:
+- 2500 Wood
+- 1800 Stone
+- 3000 Gold
+- 1200 Food
+- 500 Iron
+
+To upgrade your Realm to Level 2 you need:
+- 5000 Wood
+- 3600 Stone
+- 6000 Gold
+- 2400 Food
+- 1000 Iron
+
+To build a Military Academy you need:
+- 3000 Wood
+- 2500 Stone
+- 4000 Gold
+- 1500 Food
+- 800 Iron
+
+To construct a Market you need:
+- 2000 Wood
+- 1500 Stone
+- 5000 Gold
+- 1000 Food
+- 300 Iron
+`;
+
+export const WORLD_STATE = `You have:
+
+Resources:
+- 3000 Wood
+- 1800 Stone
+- 3000 Gold
+- 1200 Food
+- 500 Iron
+
+Standing Army:
+- 150 Swordsmen
+- 75 Archers 
+- 25 Cavalry
+- 10 Siege Weapons
+- 5 War Elephants`;
+
+export const AVAILABLE_QUERIES = `Available GraphQL Queries:
+
+query GetResources {
+  resources(playerId: "123") {
+    wood
+    stone 
+    gold
+    food
+  }
+}
+
+query GetBaseMap {
+  baseMap(playerId: "123") {
+    tiles {
+      x
+      y
+      type
+      building {
+        id
+        type
+        level
+      }
+    }
+  }
+}
+
+query GetMarketPrices {
+  market {
+    resources {
+      name
+      buyPrice
+      sellPrice
+      volume24h
+    }
+  }
+}`;
+export const AVAILABLE_ACTIONS = `Available Actions:
+
+Create Realm:
+- Creates a new realm with the specified ID
+- Example calldata:
+{
+  contractAddress: "realm",
+  entrypoint: "create_realm",
+  calldata: [
+    realm_id,
+    "0x1a3e37c77be7de91a9177c6b57956faa6da25607e567b10a25cf64fea5e533b"
+  ]
+}
+
+Upgrade Realm:
+- Upgrades a realm to the next level
+- Example calldata:
+{
+  contractAddress: "realm",
+  entrypoint: "upgrade_realm", 
+  calldata: [
+    realm_entity_id
+  ]
+}
+
+Set Entity Name:
+- Sets the name for a specific entity
+- Example calldata:
+{
+  contractAddress: "entity",
+  entrypoint: "set_name",
+  calldata: [
+    entity_id,
+    name
+  ]
+}`;
diff --git a/packages/plugin-eternum/src/actions/generative.ts b/packages/plugin-eternum/src/actions/generative.ts
new file mode 100644
index 00000000000..ed39c33403c
--- /dev/null
+++ b/packages/plugin-eternum/src/actions/generative.ts
@@ -0,0 +1,266 @@
+// TODO: Implement this for Starknet.
+// It should just transfer tokens from the agent's wallet to the recipient.
+
+import {
+    ActionExample,
+    elizaLogger,
+    generateObject,
+    HandlerCallback,
+    IAgentRuntime,
+    Memory,
+    ModelClass,
+    type Action,
+} from "@ai16z/eliza";
+import { validateStarknetConfig } from "../enviroment";
+import { EternumState } from "../types";
+import { composeContext } from "../utils";
+import { defineSteps, stepTemplate } from "../utils/execute";
+import {
+    AVAILABLE_ACTIONS,
+    AVAILABLE_QUERIES,
+    GAME_DESCRIPTION,
+    WORLD_STATE,
+} from "./dummy";
+
+interface Step {
+    name: string;
+    reasoning: string;
+}
+
+interface StepsContent {
+    steps: Array<Step>;
+}
+
+export default {
+    name: "GENERATE",
+    similes: ["GAME_ACTION"],
+    validate: async (runtime: IAgentRuntime, _message: Memory) => {
+        elizaLogger.log("Validating Starknet configuration...");
+        await validateStarknetConfig(runtime);
+        elizaLogger.success("Starknet configuration validated successfully");
+        return true;
+    },
+    description: `If a user asks you to do something that is related to this ${GAME_DESCRIPTION}, use this action to generate a plan to do it.`,
+    handler: async (
+        runtime: IAgentRuntime,
+        message: Memory,
+        state: EternumState,
+        _options: { [key: string]: unknown },
+        callback?: HandlerCallback
+    ): Promise<boolean> => {
+        elizaLogger.log("Starting GENERATE handler...");
+        elizaLogger.log(`Processing message ID: ${message.id}`);
+
+        // Initialize or update state
+        elizaLogger.log("Initializing/updating state...");
+        if (!state) {
+            elizaLogger.log("No existing state found, creating new state");
+            state = (await runtime.composeState(message, {
+                gameDescription: GAME_DESCRIPTION,
+                worldState: WORLD_STATE,
+                queriesAvailable: AVAILABLE_QUERIES,
+                availableActions: AVAILABLE_ACTIONS,
+            })) as EternumState;
+            elizaLogger.success("New state created successfully");
+        } else {
+            elizaLogger.log("Updating existing state");
+            state = (await runtime.updateRecentMessageState(state, {
+                gameDescription: GAME_DESCRIPTION,
+                worldState: WORLD_STATE,
+                queriesAvailable: AVAILABLE_QUERIES,
+                availableActions: AVAILABLE_ACTIONS,
+            })) as EternumState;
+            elizaLogger.success("State updated successfully", state);
+        }
+
+        const handleStepError = (step: string) => {
+            elizaLogger.error(`Error generating ${step} content`);
+            elizaLogger.error(`Step execution failed at: ${step}`);
+            if (callback) {
+                elizaLogger.log("Executing error callback");
+                callback({
+                    text: "Unable to process transfer request",
+                    content: {
+                        worldState: state.worldState,
+                        error: `Failed during ${step} step`,
+                    },
+                });
+            }
+            return true;
+        };
+
+        // First, get the steps from the model
+        elizaLogger.log("Generating initial steps...");
+        const context = composeContext({
+            state,
+            template: defineSteps,
+        });
+
+        elizaLogger.log("Context composed, generating content...", context);
+        const stepsContent: StepsContent = await generateObject({
+            runtime,
+            context,
+            modelClass: ModelClass.MEDIUM,
+        });
+
+        const validateStepsContent = (content: any): StepsContent => {
+            if (!content || typeof content !== "object") {
+                throw new Error("Invalid steps content format");
+            }
+
+            if (!Array.isArray(content.steps)) {
+                // Try to handle case where steps are directly returned as array
+                if (Array.isArray(content)) {
+                    return { steps: content };
+                }
+                throw new Error("Steps must be an array");
+            }
+
+            content.steps.forEach((step: any, index: number) => {
+                if (!step.name || !step.reasoning) {
+                    throw new Error(`Invalid step format at index ${index}`);
+                }
+            });
+
+            return content;
+        };
+
+        elizaLogger.log("stepsContent", stepsContent);
+        if (!stepsContent) {
+            elizaLogger.error("Failed to generate steps content");
+            return handleStepError("steps definition");
+        }
+
+        // Parse the steps returned by the model
+        let modelDefinedSteps: Array<{
+            name: string;
+            template: string;
+        }>;
+
+        const generateStep = async (
+            step: Step,
+            state: EternumState
+        ): Promise<
+            | {
+                  actionType: "invoke" | "query";
+                  data: string;
+                  steps: Array<Step>;
+              }
+            | boolean
+        > => {
+            elizaLogger.log(
+                `Generating step with template: ${step.name.substring(0, 100)}...`
+            );
+
+            const context = composeContext({
+                state,
+                template: stepTemplate,
+            });
+
+            // elizaLogger.log("Context composed, generating content...", context);
+            const content = await generateObject({
+                runtime,
+                context,
+                modelClass: ModelClass.MEDIUM,
+            });
+            // elizaLogger.success("Step content generated successfully", content);
+
+            return content;
+        };
+
+        try {
+            if (!Array.isArray(stepsContent)) {
+                throw new Error("Steps must be an array");
+            }
+        } catch (e) {
+            elizaLogger.error("Failed to parse steps:", e);
+            return handleStepError("steps parsing");
+        }
+
+        state = (await runtime.composeState(message, {
+            ...state,
+            allSteps: [...stepsContent],
+            currentStepTitle: stepsContent[0].name,
+            currentStepReasoning: stepsContent[0].reasoning,
+        })) as EternumState;
+
+        // Execute each step
+        let currentSteps = [...stepsContent];
+        let stepIndex = 0;
+
+        // Execute steps dynamically
+        elizaLogger.log("Beginning step execution...");
+        while (stepIndex < currentSteps.length) {
+            const step = currentSteps[stepIndex];
+            elizaLogger.log(`Executing step: ${step.name}`);
+            const content = await generateStep(step, state);
+
+            if (!content) {
+                elizaLogger.error(
+                    `Step ${step.name} failed to generate content`
+                );
+                return handleStepError(step.name);
+            }
+
+            if (typeof content === "object" && "actionType" in content) {
+                // ... existing action handling code ...
+            }
+
+            // Update steps array with any new steps returned from generateStep
+            if (typeof content === "object" && "steps" in content) {
+                currentSteps = content.steps;
+            }
+
+            // Update state with current progress
+            state = (await runtime.composeState(message, {
+                ...state,
+                allSteps: currentSteps,
+                currentStepTitle: currentSteps[stepIndex + 1]?.name,
+                currentStepReasoning: currentSteps[stepIndex + 1]?.reasoning,
+            })) as EternumState;
+
+            elizaLogger.success(`Step ${step.name} completed successfully`);
+            stepIndex++;
+        }
+
+        // TODO: After this happens we need to evaluate how the action went
+        // and if it was successful or not. If it was succesful we should store it in memory as an action to do xyz. This way
+        // we know this action works for the task.
+
+        const handleStepSuccess = () => {
+            elizaLogger.success(
+                `Action completed successfully. Steps executed: ${JSON.stringify(currentSteps, null, 2)}`
+            );
+            if (callback) {
+                elizaLogger.log("Executing success callback");
+                callback({
+                    text: "Action completed successfully",
+                    content: {
+                        worldState: state.worldState,
+                        steps: modelDefinedSteps,
+                    },
+                });
+            }
+            return true;
+        };
+
+        return handleStepSuccess();
+    },
+
+    examples: [
+        [
+            {
+                user: "{{user1}}",
+                content: {
+                    text: "Can you build me a Realm/house/castle/tower?",
+                },
+            },
+            {
+                user: "{{agent}}",
+                content: {
+                    text: "Sure thing! I'll get started on that right away.",
+                },
+            },
+        ],
+    ] as ActionExample[][],
+} as Action;
diff --git a/packages/plugin-eternum/src/actions/generativeActions.ts b/packages/plugin-eternum/src/actions/generativeActions.ts
new file mode 100644
index 00000000000..1b4d911cbba
--- /dev/null
+++ b/packages/plugin-eternum/src/actions/generativeActions.ts
@@ -0,0 +1,21 @@
+export const transferTemplate = `
+
+{{actions}}
+
+Example response:
+\`\`\`json
+{
+    "tokenAddress": "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
+    "recipient": "0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF",
+    "amount": "0.001"
+}
+\`\`\`
+
+{{recentMessages}}
+
+Given the recent messages, extract the following information about the requested token transfer:
+- Token contract address
+- Recipient wallet address
+
+
+Respond with a JSON markdown block containing only the extracted values.`;
diff --git a/packages/plugin-eternum/src/client/dojo.ts b/packages/plugin-eternum/src/client/dojo.ts
new file mode 100644
index 00000000000..d4d5bf9fe83
--- /dev/null
+++ b/packages/plugin-eternum/src/client/dojo.ts
@@ -0,0 +1,47 @@
+// import { IAgentRuntime } from "@ai16z/eliza";
+
+// // The goal of this is to build a generic client that interfaces with the game world
+
+// interface Player {
+//   address: string;
+// }
+
+// export class DojoClient {
+//   private readonly runtime: IAgentRuntime;
+//   private readonly dojoConfig: DojoConfig;
+//   private readonly player: Player;
+
+//   public tick: number = 0; // Agent Tick
+
+//   constructor(runtime: IAgentRuntime, dojoConfig: DojoConfig, player: Player) {
+//     this.runtime = runtime;
+//     this.dojoConfig = dojoConfig;
+//     this.player = player;
+//   }
+
+//   async getWorldState(): Promise<string> {
+//     return ""; // TODO: implement
+//   }
+
+//   async getQueriesAvailable(): Promise<string> {
+//     return ""; // TODO: implement
+//   }
+
+//   async getAvailableActions(): Promise<string> {
+//     return ""; // TODO: implement
+//   }
+
+//   async invokeAction(action: string, data: string): Promise<string> {
+//     return ""; // TODO: implement
+//   }
+
+//   async queryAction(action: string, data: string): Promise<string> {
+//     return ""; // TODO: implement
+//   }
+// }
+
+// export class EternumClient extends DojoClient {
+//   constructor(runtime: IAgentRuntime, dojoConfig: DojoConfig, player: Player) {
+//     super(runtime, dojoConfig, player);
+//   }
+// }
diff --git a/packages/plugin-eternum/src/enviroment.ts b/packages/plugin-eternum/src/enviroment.ts
new file mode 100644
index 00000000000..89a1247de07
--- /dev/null
+++ b/packages/plugin-eternum/src/enviroment.ts
@@ -0,0 +1,40 @@
+import { IAgentRuntime } from "@ai16z/eliza";
+import { z } from "zod";
+
+export const starknetEnvSchema = z.object({
+    STARKNET_ADDRESS: z.string().min(1, "Starknet address is required"),
+    STARKNET_PRIVATE_KEY: z.string().min(1, "Starknet private key is required"),
+    STARKNET_RPC_URL: z.string().min(1, "Starknet RPC URL is required"),
+});
+
+export type StarknetConfig = z.infer<typeof starknetEnvSchema>;
+
+export async function validateStarknetConfig(
+    runtime: IAgentRuntime
+): Promise<StarknetConfig> {
+    try {
+        const config = {
+            STARKNET_ADDRESS:
+                runtime.getSetting("STARKNET_ADDRESS") ||
+                process.env.STARKNET_ADDRESS,
+            STARKNET_PRIVATE_KEY:
+                runtime.getSetting("STARKNET_PRIVATE_KEY") ||
+                process.env.STARKNET_PRIVATE_KEY,
+            STARKNET_RPC_URL:
+                runtime.getSetting("STARKNET_RPC_URL") ||
+                process.env.STARKNET_RPC_URL,
+        };
+
+        return starknetEnvSchema.parse(config);
+    } catch (error) {
+        if (error instanceof z.ZodError) {
+            const errorMessages = error.errors
+                .map((err) => `${err.path.join(".")}: ${err.message}`)
+                .join("\n");
+            throw new Error(
+                `Starknet configuration validation failed:\n${errorMessages}`
+            );
+        }
+        throw error;
+    }
+}
diff --git a/packages/plugin-eternum/src/index.ts b/packages/plugin-eternum/src/index.ts
new file mode 100644
index 00000000000..5774879b0e7
--- /dev/null
+++ b/packages/plugin-eternum/src/index.ts
@@ -0,0 +1,26 @@
+import { Plugin } from "@ai16z/eliza";
+import generate from "./actions/generative";
+export const PROVIDER_CONFIG = {
+    AVNU_API: "https://starknet.impulse.avnu.fi/v1",
+    MAX_RETRIES: 3,
+    RETRY_DELAY: 2000,
+    TOKEN_ADDRESSES: {
+        BTC: "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac",
+        ETH: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
+        STRK: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
+    },
+    TOKEN_SECURITY_ENDPOINT: "/defi/token_security?address=",
+    TOKEN_TRADE_DATA_ENDPOINT: "/defi/v3/token/trade-data/single?address=",
+    DEX_SCREENER_API: "https://api.dexscreener.com/latest/dex/tokens/",
+    MAIN_WALLET: "",
+};
+
+export const eternumPlugin: Plugin = {
+    name: "starknet",
+    description: "Eternum Plugin for Eliza",
+    actions: [generate],
+    evaluators: [],
+    providers: [],
+};
+
+export default eternumPlugin;
diff --git a/packages/plugin-eternum/src/providers/index.ts b/packages/plugin-eternum/src/providers/index.ts
new file mode 100644
index 00000000000..c3fdb9a159f
--- /dev/null
+++ b/packages/plugin-eternum/src/providers/index.ts
@@ -0,0 +1,106 @@
+import { elizaLogger } from "@ai16z/eliza";
+
+interface GraphQLResponse<T> {
+    data?: T;
+    errors?: Array<{
+        message: string;
+        locations?: Array<{
+            line: number;
+            column: number;
+        }>;
+        path?: string[];
+    }>;
+}
+
+async function queryGraphQL<T>(
+    endpoint: string,
+    query: string,
+    variables?: Record<string, unknown>
+): Promise<T> {
+    try {
+        const response = await fetch(endpoint, {
+            method: "POST",
+            headers: {
+                "Content-Type": "application/json",
+            },
+            body: JSON.stringify({
+                query,
+                variables,
+            }),
+        });
+
+        const result = (await response.json()) as GraphQLResponse<T>;
+
+        if (result.errors) {
+            throw new Error(result.errors[0].message);
+        }
+
+        if (!result.data) {
+            throw new Error("No data returned from GraphQL query");
+        }
+
+        return result.data;
+    } catch (error) {
+        elizaLogger.error("GraphQL query failed:", error);
+        throw error;
+    }
+}
+
+// 1.  fetch graphgl schema from game cache.
+
+const message = `
+{{goals}}
+
+{{worldState}} -> general world state
+
+{{query}} - all graphql queries possible. 
+
+Based on the above decide what information you need to fetch from the game. Use the schema examples and format the query accordingly to match the schema.
+
+Return the query and parameters as an object like this:
+
+\`\`\`
+{
+  query: <query>,
+  variables: {
+    // variables go here
+  }
+}
+  \`\`\`
+`;
+
+const fetchData = async (query: string, variables: Record<string, unknown>) => {
+    await queryGraphQL<string>("https://api.eternum.io/graphql", query, {
+        variables,
+    });
+};
+
+const messages = `
+{{goals}}
+
+{{world state}}
+
+{{fetchedQuery}}
+
+{{availableActions}} -> all execution functions agailabble. It will be xample of all the calldata.
+
+Based on the above, decide what action to take. If no action to take return false
+
+Only return the calldata of the action to take. Like this
+
+\`\`\`
+    {
+        contractAddress: contractName,
+        entrypoint: "create_order",
+        calldata: [
+            maker_id,
+            maker_gives_resources.length / 2,
+            ...maker_gives_resources,
+            taker_id,
+            taker_gives_resources.length / 2,
+            ...taker_gives_resources,
+            expires_at,
+        ],
+    }
+\`\`\`
+`;
diff --git a/packages/plugin-eternum/src/types/index.ts b/packages/plugin-eternum/src/types/index.ts
new file mode 100644
index 00000000000..4d4cbc14a17
--- /dev/null
+++ b/packages/plugin-eternum/src/types/index.ts
@@ -0,0 +1,8 @@
+import { State } from "@ai16z/eliza";
+
+export interface EternumState extends State {
+    gameDescription: string;
+    worldState: string;
+    queriesAvailable: string;
+    availableActions: string;
+}
diff --git a/packages/plugin-eternum/src/utils/execute.ts b/packages/plugin-eternum/src/utils/execute.ts
new file mode 100644
index 00000000000..949b4f1fa8d
--- /dev/null
+++ b/packages/plugin-eternum/src/utils/execute.ts
@@ -0,0 +1,85 @@
+const stepTemplate = `
+
+{{allSteps}}
+
+{{currentStepTitle}}
+
+{{currentStepReasoning}}
+
+{{worldState}}
+
+Based on the above decide what information you need to fetch from the game. Use the schema examples and format the query accordingly to match the schema.
+
+Decide to either invoke a query or an action.
+
+These are the available actions you can use:
+{{availableActions}}
+
+Return the action you want to invoke and the parameters as an object like this:
+\`\`\`json
+{
+  actionType: "invoke",
+  data: {
+    "tokenAddress": "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
+    "recipient": "0x1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF",
+    "amount": "0.001"
+  },
+  "steps": [
+  { name: "1", reasoning: "this is an example" },
+    {{allSteps}}
+    + add any other steps or change the steps if you think it's necessary, only change steps after the current step
+  ]
+}
+\`\`\`
+
+Or if you want to query the game, these are the available queries you can use:
+{{queriesAvailable}}
+
+Return the query you want to invoke and the variables as an object like this:
+
+\`\`\`json
+{
+  "actionType": "query",
+  "data": {
+    "query": "<query>",
+    "variables": {
+      // variables go here
+    }
+  },
+  "steps": [
+  { name: "1", reasoning: "this is an example" },
+    {{allSteps}}
+    + add any other steps or change the steps if you think it's necessary, only change steps after the current step
+  ]
+}
+\`\`\`
+
+Make sure to only return one object like above depending on the action you want to take.
+
+`;
+
+const defineSteps = `
+
+Based on the goals and world state, decide what steps you need to execute to achieve the goals.
+
+{{worldState}}
+
+{{queriesAvailable}}
+
+{{availableActions}}
+
+You should return an array of steps to execute. 
+
+Return something like this:
+
+\`\`\`json
+[
+  { name: "1", reasoning: "Need to check for resources" },
+  { name: "2", reasoning: "Need to check the base" },
+  { name: "3", reasoning: "Need to build calldata" },
+]
+\`\`\`
+
+`;
+
+export { defineSteps, stepTemplate };
diff --git a/packages/plugin-eternum/src/utils/index.ts b/packages/plugin-eternum/src/utils/index.ts
new file mode 100644
index 00000000000..1ad0c499382
--- /dev/null
+++ b/packages/plugin-eternum/src/utils/index.ts
@@ -0,0 +1,14 @@
+import { EternumState } from "../types";
+
+export const composeContext = ({
+    state,
+    template,
+}: {
+    state: EternumState;
+    template: string;
+}) => {
+    // @ts-expect-error match isn't working as expected
+    return template.replace(/{{\w+}}/g, (match) => {
+        return state[match.replace(/{{|}}/g, "")] ?? "";
+    });
+};
diff --git a/packages/plugin-eternum/tsconfig.json b/packages/plugin-eternum/tsconfig.json
new file mode 100644
index 00000000000..7c90f8c0f6d
--- /dev/null
+++ b/packages/plugin-eternum/tsconfig.json
@@ -0,0 +1,21 @@
+{
+    "compilerOptions": {
+        "target": "esnext",
+        "module": "ESNext",
+        "declaration": true,
+        "outDir": "./dist",
+        "sourceMap": true,
+        "noImplicitAny": true,
+        "noUnusedLocals": true,
+        "moduleResolution": "node",
+        // "noUnusedParameters": true,
+        "resolveJsonModule": true,
+        "skipLibCheck": true,
+        "strict": true,
+        "strictNullChecks": true,
+        "esModuleInterop": true
+    },
+    "include": ["src/**/*.ts"],
+    "skipLibCheck": true,
+    "exclude": ["node_modules", "dist", "**/*.test.ts"]
+}
diff --git a/packages/plugin-eternum/tsup.config.ts b/packages/plugin-eternum/tsup.config.ts
new file mode 100644
index 00000000000..b5e4388b214
--- /dev/null
+++ b/packages/plugin-eternum/tsup.config.ts
@@ -0,0 +1,21 @@
+import { defineConfig } from "tsup";
+
+export default defineConfig({
+    entry: ["src/index.ts"],
+    outDir: "dist",
+    sourcemap: true,
+    clean: true,
+    format: ["esm"], // Ensure you're targeting CommonJS
+    external: [
+        "dotenv", // Externalize dotenv to prevent bundling
+        "fs", // Externalize fs to use Node.js built-in module
+        "path", // Externalize other built-ins if necessary
+        "@reflink/reflink",
+        "@node-llama-cpp",
+        "https",
+        "http",
+        "agentkeepalive",
+        "zod",
+        // Add other modules you want to externalize
+    ],
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index dc2b4bafbc2..2e65d542e91 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -90,6 +90,9 @@ importers:
       '@ai16z/plugin-coinbase':
         specifier: workspace:*
         version: link:../packages/plugin-coinbase
+      '@ai16z/plugin-eternum':
+        specifier: workspace:*
+        version: link:../packages/plugin-eternum
       '@ai16z/plugin-image-generation':
         specifier: workspace:*
         version: link:../packages/plugin-image-generation
@@ -897,6 +900,42 @@ importers:
         specifier: ^8.3.5
         version: 8.3.5(@swc/core@1.9.3(@swc/helpers@0.5.15))(jiti@2.4.0)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.6.1)
 
+  packages/plugin-eternum:
+    dependencies:
+      '@ai16z/eliza':
+        specifier: 0.1.4-alpha.3
+        version: 0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)
+      '@ai16z/plugin-trustdb':
+        specifier: 0.1.4-alpha.3
+        version: 0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(@swc/core@1.9.3(@swc/helpers@0.5.15))(@types/node@22.8.4)(axios@1.7.7)(encoding@0.1.13)(handlebars@4.7.8)(jiti@2.4.0)(jsdom@25.0.1(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(postcss@8.4.49)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(terser@5.36.0)(typescript@5.6.3)(whatwg-url@7.1.0)(yaml@2.6.1)
+      '@avnu/avnu-sdk':
+        specifier: ^2.1.1
+        version: 2.1.1(ethers@6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(qs@6.13.0)(starknet@6.18.0(encoding@0.1.13))
+      '@uniswap/sdk-core':
+        specifier: ^6.0.0
+        version: 6.0.0
+      '@unruggable_starknet/core':
+        specifier: ^0.1.0
+        version: 0.1.0(starknet@6.18.0(encoding@0.1.13))
+      langsmith:
+        specifier: ^0.2.7
+        version: 0.2.7(openai@4.73.0(encoding@0.1.13)(zod@3.23.8))
+      openai:
+        specifier: ^4.73.0
+        version: 4.73.0(encoding@0.1.13)(zod@3.23.8)
+      starknet:
+        specifier: ^6.17.0
+        version: 6.18.0(encoding@0.1.13)
+      tsup:
+        specifier: ^8.3.5
+        version: 8.3.5(@swc/core@1.9.3(@swc/helpers@0.5.15))(jiti@2.4.0)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.6.1)
+      vitest:
+        specifier: ^2.1.4
+        version: 2.1.5(@types/node@22.8.4)(jsdom@25.0.1(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.36.0)
+      whatwg-url:
+        specifier: 7.1.0
+        version: 7.1.0
+
   packages/plugin-image-generation:
     dependencies:
       '@ai16z/eliza':
@@ -971,7 +1010,7 @@ importers:
         version: 1.6.0
       echogarden:
         specifier: ^2.0.5
-        version: 2.0.6(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(encoding@0.1.13)(utf-8-validate@5.0.10)(zod@3.23.8)
+        version: 2.0.7(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(encoding@0.1.13)(utf-8-validate@5.0.10)(zod@3.23.8)
       espeak-ng:
         specifier: 1.0.2
         version: 1.0.2
@@ -1414,9 +1453,32 @@ packages:
       vue:
         optional: true
 
+  '@ai16z/adapter-sqlite@0.1.4-alpha.3':
+    resolution: {integrity: sha512-EIz2lJtueAmDpUAZh+Pz+bEABkqLzch8TwtjWIyaVB80rNX0vjZZ6+AZz90sLKMpLgZs6ZpGHQxwsQa56YVEpw==}
+    peerDependencies:
+      whatwg-url: 7.1.0
+
+  '@ai16z/adapter-sqljs@0.1.4-alpha.3':
+    resolution: {integrity: sha512-qz9uqR2SWZ/rXKM6uw+ur7e7mFr6JG00ciXe1oVCX9szFVUksw6couq3q0mrC/SEHZN4LqimDl8m88/49bvcOA==}
+    peerDependencies:
+      whatwg-url: 7.1.0
+
+  '@ai16z/adapter-supabase@0.1.4-alpha.3':
+    resolution: {integrity: sha512-/RM6RQfMpnokz39RDCMpTC7aELB31u45jWWRXWcAY6H8FjGQ7miL6f7ejavaVtW96mAz3oyMP69JP+nYBdlu2g==}
+    peerDependencies:
+      whatwg-url: 7.1.0
+
   '@ai16z/eliza@0.1.3':
     resolution: {integrity: sha512-MeHvD44YKeYdnmI0k03RpS0COGPUsM5/nYoo7ih9vi7iXWFUWANEa3FReWr8rT51AOdHFXGdaLvTzngI527WjA==}
 
+  '@ai16z/eliza@0.1.4-alpha.3':
+    resolution: {integrity: sha512-op3uFF1SPiQAbhKg9urDPm9HJMTc07K4AOx/lF+81wJZujja2cdB28YCEk20QX1ub/NkRPa0/dhdEsQJYAGZyw==}
+
+  '@ai16z/plugin-trustdb@0.1.4-alpha.3':
+    resolution: {integrity: sha512-sgQH5lYHsenbb4Zzgt5mpk0AwvrJz+TjPTveUFDI3Z0scwzP8U1PVGUP7OewOphSVwqsWIZWZDN4aVkWvsx4pg==}
+    peerDependencies:
+      whatwg-url: 7.1.0
+
   '@algolia/autocomplete-core@1.17.7':
     resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==}
 
@@ -4535,56 +4597,50 @@ packages:
       '@types/react':
         optional: true
 
-  '@reflink/reflink-darwin-arm64@0.1.16':
-    resolution: {integrity: sha512-s61AeZ0br2LtqOl2Rbq0k833hQ00sXJ+l9LGJmjM53dupWft3HEX9C5WUIMDDiU2Scx7f7UKAE4DvIvv7XjBWQ==}
+  '@reflink/reflink-darwin-arm64@0.1.17':
+    resolution: {integrity: sha512-ATlkKHl/9wFwZNezuUfHBGR5O4OpXs5ikD+3OKNOl+CzSPUY95ZSm3DyVUaTfGM5O7rCC1N7PXv/Q73uEdy/wA==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [darwin]
 
-  '@reflink/reflink-darwin-x64@0.1.16':
-    resolution: {integrity: sha512-ssrJj3K0Euua2LAkA4ff5y693wGKUHfznrGeWWtMw2aoLZRAH+C9Ne5oQvmcPPEK6wa929nRhA0ABrvhUa9mvA==}
-    engines: {node: '>= 10'}
-    cpu: [x64]
-    os: [darwin]
-
-  '@reflink/reflink-linux-arm64-gnu@0.1.16':
-    resolution: {integrity: sha512-I4PCAcsAKFRSfOSHdz+rck6ARg4jzo4PvVqcnS2odcXy1Inbehxk3IcKBpHnuuDbXRCUoWV6NP7wSx1wG7ZBuA==}
+  '@reflink/reflink-linux-arm64-gnu@0.1.17':
+    resolution: {integrity: sha512-RTKNhBHn7rvQyqciqY5yylcsdOcxuFbTbg81fRR/b8WOTNJ6WXDQpdX0AcL38IC7VYSe4fRhqwvyEuVg/d5wbg==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
 
-  '@reflink/reflink-linux-arm64-musl@0.1.16':
-    resolution: {integrity: sha512-xzcdtfwTXWUzN5yHdJgCdyAZSBO0faSgTqGdT4QKDxGHmiokf7+tgVBd6bU2nT4sL26AiIFyIBwp8buXGQYyaw==}
+  '@reflink/reflink-linux-arm64-musl@0.1.17':
+    resolution: {integrity: sha512-dCKQLGkDiAjlwWGZvkzeRwY51RZWPFgNx9QDJ1SKR1nGxC3/tNukyC8doG1ssFfBYTlo/YMfEk6SZXnxbbFaUg==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
 
-  '@reflink/reflink-linux-x64-gnu@0.1.16':
-    resolution: {integrity: sha512-4/jscn1A/hx6maOowUjcvIs7YBs0fj//1vxB16TdMYk3tH9FHNmMBv5Pvw8eeRDimAzHP9fQJ9/t4dR6HCf32w==}
+  '@reflink/reflink-linux-x64-gnu@0.1.17':
+    resolution: {integrity: sha512-RIfW4D5gQCS9svw9XvKQSO368/FQSs4Yo+t2buAdgg9KhWxBWcjmtm7z4jlaMRGBimQE4I86PFhtPrXHrcbufg==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
 
-  '@reflink/reflink-linux-x64-musl@0.1.16':
-    resolution: {integrity: sha512-03kRXoAXhS/ZKxU2TKax59mLyKP7mev0EoIs+yXejUQo6D4uU46j+Sc243xMp72jRTgbWV4hQykcov98KtXEKQ==}
+  '@reflink/reflink-linux-x64-musl@0.1.17':
+    resolution: {integrity: sha512-Ha6NXgIH0qbEpTWDIdqO94NMmcz3lxVzH17VeWjRQe05M7wxtB2ODpxEY6uTkJrNrP3T5fiX4DT0X1t0PJegfg==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
 
-  '@reflink/reflink-win32-arm64-msvc@0.1.16':
-    resolution: {integrity: sha512-N7r+6YB3vXijs7PF3eg306B5s82hGS2TzsMM4+B9DNN9sbvN2yV5HQw29zyCXHY9c9SLe5kEzERp0rsDtN+6TA==}
+  '@reflink/reflink-win32-arm64-msvc@0.1.17':
+    resolution: {integrity: sha512-eB2+v47QmAH2XKVykWLevCLSnJG+xSszcUiqc3LReMI4oa1yyR1L5Pa/WHs18ac8mPq6lsgSovdVO2voZVLb8g==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [win32]
 
-  '@reflink/reflink-win32-x64-msvc@0.1.16':
-    resolution: {integrity: sha512-CaslGjfhpvtjHqr8Cw1MhkYZAkcLWFiL1pMXOPv4fwngtLC5/OlcL/Y4Rw2QEZwDvPG3gaeY7pjF1NYEGnDrZA==}
+  '@reflink/reflink-win32-x64-msvc@0.1.17':
+    resolution: {integrity: sha512-S9mUCeVcty9vnekD9x5eSUvUqcTCha1Iscd0fN0PoXusj/IqaYcyMO9Vke9Qr/UFcKPQBFLj6F52b8PP3EaNsQ==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [win32]
 
-  '@reflink/reflink@0.1.16':
-    resolution: {integrity: sha512-i2zYt2FH1CE/1HUwK96HcwiahGhaS4wSCgaUnlIrl/4bxTnaZ0T/sYcLJ5VNSrbuczWjtyJ4WUROB+qMcRI9jA==}
+  '@reflink/reflink@0.1.17':
+    resolution: {integrity: sha512-+drVOToSW+6HouIpY0kFGC9DwETJJTBIS3UqUUxrR8C0bHRX93XYOgG6BEYqguOJA6+Qz0VfPt+CL3zSrsdjfg==}
     engines: {node: '>= 10'}
 
   '@remix-run/router@1.15.1':
@@ -6489,8 +6545,8 @@ packages:
   bare-path@2.1.3:
     resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==}
 
-  bare-stream@2.4.0:
-    resolution: {integrity: sha512-sd96/aZ8LjF1uJbEHzIo1LrERPKRFPEy1nZ1eOILftBxrVsFDAQkimHIIq87xrHcubzjNeETsD9PwN0wp+vLiQ==}
+  bare-stream@2.4.2:
+    resolution: {integrity: sha512-XZ4ln/KV4KT+PXdIWTKjsLY+quqCaEtqqtgGJVPw9AoM73By03ij64YjepK0aQvHSWDb6AfAZwqKaFu68qkrdA==}
 
   base-x@3.0.10:
     resolution: {integrity: sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==}
@@ -6748,8 +6804,8 @@ packages:
   caniuse-api@3.0.0:
     resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
 
-  caniuse-lite@1.0.30001683:
-    resolution: {integrity: sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q==}
+  caniuse-lite@1.0.30001684:
+    resolution: {integrity: sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==}
 
   canvas@2.11.2:
     resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==}
@@ -7964,8 +8020,8 @@ packages:
   ecdsa-sig-formatter@1.0.11:
     resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
 
-  echogarden@2.0.6:
-    resolution: {integrity: sha512-mpSC03TiMsQEX2jg9+/7YHBDhc+bITemHiUI/MmW3T0GQOE5hqXFc0vBQJ/9Fei7skei4hQVgGpvpxGMQLoEEw==}
+  echogarden@2.0.7:
+    resolution: {integrity: sha512-/yggoJ2NEy5VZPcAtk4DoGNGgHIRicSS0uKfk06gT+GmRPJ28kKD3MgyjK3agtQ8yIc46si09nB+hWPYiruzXw==}
     engines: {node: '>=18'}
     os: [win32, darwin, linux]
     hasBin: true
@@ -13843,14 +13899,14 @@ packages:
     resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
     engines: {node: '>=14.0.0'}
 
-  tldts-core@6.1.63:
-    resolution: {integrity: sha512-H1XCt54xY+QPbwhTgmxLkepX0MVHu3USfMmejiCOdkMbRcP22Pn2FVF127r/GWXVDmXTRezyF3Ckvhn4Fs6j7Q==}
+  tldts-core@6.1.64:
+    resolution: {integrity: sha512-uqnl8vGV16KsyflHOzqrYjjArjfXaU6rMPXYy2/ZWoRKCkXtghgB4VwTDXUG+t0OTGeSewNAG31/x1gCTfLt+Q==}
 
-  tldts-experimental@6.1.63:
-    resolution: {integrity: sha512-Xqxv4UvuTwC/sslspSbkw/52vvYCeZdEJwnv7VFlQEfYvK8fNuIpz5hoOvO7XuzfjqexMRRnVDYUyjqesTYESg==}
+  tldts-experimental@6.1.64:
+    resolution: {integrity: sha512-Lm6dwThCCmUecyvOJwTfZgYRP9JB6UDam//96OSvZffBtBA3GuwucIiycLT5yO5nz0ZAGV37FF1hef2HE0K8BQ==}
 
-  tldts@6.1.63:
-    resolution: {integrity: sha512-YWwhsjyn9sB/1rOkSRYxvkN/wl5LFM1QDv6F2pVR+pb/jFne4EOBxHfkKVWvDIBEAw9iGOwwubHtQTm0WRT5sQ==}
+  tldts@6.1.64:
+    resolution: {integrity: sha512-ph4AE5BXWIOsSy9stpoeo7bYe/Cy7VfpciIH4RhVZUPItCJmhqWCN0EVzxd8BOHiyNb42vuJc6NWTjJkg91Tuw==}
     hasBin: true
 
   tmp@0.0.33:
@@ -15032,6 +15088,103 @@ snapshots:
     transitivePeerDependencies:
       - zod
 
+  '@ai16z/adapter-sqlite@0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)':
+    dependencies:
+      '@ai16z/eliza': 0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)
+      '@types/better-sqlite3': 7.6.11
+      better-sqlite3: 11.5.0
+      sqlite-vec: 0.1.4-alpha.2
+      whatwg-url: 7.1.0
+    transitivePeerDependencies:
+      - '@google-cloud/vertexai'
+      - '@langchain/anthropic'
+      - '@langchain/aws'
+      - '@langchain/cohere'
+      - '@langchain/core'
+      - '@langchain/google-genai'
+      - '@langchain/google-vertexai'
+      - '@langchain/groq'
+      - '@langchain/mistralai'
+      - '@langchain/ollama'
+      - axios
+      - bufferutil
+      - cheerio
+      - encoding
+      - handlebars
+      - peggy
+      - react
+      - solid-js
+      - sswr
+      - supports-color
+      - svelte
+      - typeorm
+      - utf-8-validate
+      - vue
+
+  '@ai16z/adapter-sqljs@0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)':
+    dependencies:
+      '@ai16z/eliza': 0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)
+      '@types/sql.js': 1.4.9
+      sql.js: 1.12.0
+      uuid: 11.0.2
+      whatwg-url: 7.1.0
+    transitivePeerDependencies:
+      - '@google-cloud/vertexai'
+      - '@langchain/anthropic'
+      - '@langchain/aws'
+      - '@langchain/cohere'
+      - '@langchain/core'
+      - '@langchain/google-genai'
+      - '@langchain/google-vertexai'
+      - '@langchain/groq'
+      - '@langchain/mistralai'
+      - '@langchain/ollama'
+      - axios
+      - bufferutil
+      - cheerio
+      - encoding
+      - handlebars
+      - peggy
+      - react
+      - solid-js
+      - sswr
+      - supports-color
+      - svelte
+      - typeorm
+      - utf-8-validate
+      - vue
+
+  '@ai16z/adapter-supabase@0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)':
+    dependencies:
+      '@ai16z/eliza': 0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)
+      '@supabase/supabase-js': 2.46.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+      whatwg-url: 7.1.0
+    transitivePeerDependencies:
+      - '@google-cloud/vertexai'
+      - '@langchain/anthropic'
+      - '@langchain/aws'
+      - '@langchain/cohere'
+      - '@langchain/core'
+      - '@langchain/google-genai'
+      - '@langchain/google-vertexai'
+      - '@langchain/groq'
+      - '@langchain/mistralai'
+      - '@langchain/ollama'
+      - axios
+      - bufferutil
+      - cheerio
+      - encoding
+      - handlebars
+      - peggy
+      - react
+      - solid-js
+      - sswr
+      - supports-color
+      - svelte
+      - typeorm
+      - utf-8-validate
+      - vue
+
   '@ai16z/eliza@0.1.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(encoding@0.1.13)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)':
     dependencies:
       '@ai-sdk/anthropic': 0.0.53(zod@3.23.8)
@@ -15065,6 +15218,114 @@ snapshots:
       - svelte
       - vue
 
+  '@ai16z/eliza@0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)':
+    dependencies:
+      '@ai-sdk/anthropic': 0.0.53(zod@3.23.8)
+      '@ai-sdk/google': 0.0.55(zod@3.23.8)
+      '@ai-sdk/google-vertex': 0.0.42(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(zod@3.23.8)
+      '@ai-sdk/groq': 0.0.3(zod@3.23.8)
+      '@ai-sdk/openai': 1.0.0-canary.3(zod@3.23.8)
+      '@ai16z/adapter-sqlite': 0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)
+      '@ai16z/adapter-sqljs': 0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)
+      '@ai16z/adapter-supabase': 0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)
+      '@anthropic-ai/sdk': 0.30.1(encoding@0.1.13)
+      '@types/uuid': 10.0.0
+      ai: 3.4.33(openai@4.69.0(encoding@0.1.13)(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(zod@3.23.8)
+      anthropic-vertex-ai: 1.0.2(encoding@0.1.13)(zod@3.23.8)
+      fastembed: 1.14.1
+      fastestsmallesttextencoderdecoder: 1.0.22
+      gaxios: 6.7.1(encoding@0.1.13)
+      glob: 11.0.0
+      js-sha1: 0.7.0
+      langchain: 0.3.6(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.69.0(encoding@0.1.13)(zod@3.23.8))
+      ollama-ai-provider: 0.16.1(zod@3.23.8)
+      openai: 4.69.0(encoding@0.1.13)(zod@3.23.8)
+      tiktoken: 1.0.17
+      tinyld: 1.3.4
+      together-ai: 0.7.0(encoding@0.1.13)
+      unique-names-generator: 4.7.1
+      uuid: 11.0.2
+      zod: 3.23.8
+    transitivePeerDependencies:
+      - '@google-cloud/vertexai'
+      - '@langchain/anthropic'
+      - '@langchain/aws'
+      - '@langchain/cohere'
+      - '@langchain/core'
+      - '@langchain/google-genai'
+      - '@langchain/google-vertexai'
+      - '@langchain/groq'
+      - '@langchain/mistralai'
+      - '@langchain/ollama'
+      - axios
+      - bufferutil
+      - cheerio
+      - encoding
+      - handlebars
+      - peggy
+      - react
+      - solid-js
+      - sswr
+      - supports-color
+      - svelte
+      - typeorm
+      - utf-8-validate
+      - vue
+      - whatwg-url
+
+  '@ai16z/plugin-trustdb@0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(@swc/core@1.9.3(@swc/helpers@0.5.15))(@types/node@22.8.4)(axios@1.7.7)(encoding@0.1.13)(handlebars@4.7.8)(jiti@2.4.0)(jsdom@25.0.1(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(postcss@8.4.49)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(terser@5.36.0)(typescript@5.6.3)(whatwg-url@7.1.0)(yaml@2.6.1)':
+    dependencies:
+      '@ai16z/eliza': 0.1.4-alpha.3(@google-cloud/vertexai@1.9.0(encoding@0.1.13))(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(bufferutil@4.0.8)(encoding@0.1.13)(handlebars@4.7.8)(react@18.3.1)(sswr@2.1.0(svelte@5.2.7))(svelte@5.2.7)(utf-8-validate@5.0.10)(whatwg-url@7.1.0)
+      dompurify: 3.2.0
+      tsup: 8.3.5(@swc/core@1.9.3(@swc/helpers@0.5.15))(jiti@2.4.0)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.6.1)
+      uuid: 11.0.2
+      vitest: 2.1.5(@types/node@22.8.4)(jsdom@25.0.1(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.36.0)
+      whatwg-url: 7.1.0
+    transitivePeerDependencies:
+      - '@edge-runtime/vm'
+      - '@google-cloud/vertexai'
+      - '@langchain/anthropic'
+      - '@langchain/aws'
+      - '@langchain/cohere'
+      - '@langchain/core'
+      - '@langchain/google-genai'
+      - '@langchain/google-vertexai'
+      - '@langchain/groq'
+      - '@langchain/mistralai'
+      - '@langchain/ollama'
+      - '@microsoft/api-extractor'
+      - '@swc/core'
+      - '@types/node'
+      - '@vitest/browser'
+      - '@vitest/ui'
+      - axios
+      - cheerio
+      - encoding
+      - handlebars
+      - happy-dom
+      - jiti
+      - jsdom
+      - less
+      - lightningcss
+      - msw
+      - peggy
+      - postcss
+      - react
+      - sass
+      - sass-embedded
+      - solid-js
+      - sswr
+      - stylus
+      - sugarss
+      - supports-color
+      - svelte
+      - terser
+      - tsx
+      - typeorm
+      - typescript
+      - vue
+      - yaml
+
   '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.15.0)(algoliasearch@5.15.0)(search-insights@2.17.3)':
     dependencies:
       '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.15.0)(algoliasearch@5.15.0)(search-insights@2.17.3)
@@ -16671,7 +16932,7 @@ snapshots:
       '@cliqz/adblocker': 1.34.0
       '@cliqz/adblocker-content': 1.34.0
       playwright: 1.48.2
-      tldts-experimental: 6.1.63
+      tldts-experimental: 6.1.64
 
   '@cliqz/adblocker@1.34.0':
     dependencies:
@@ -16682,7 +16943,7 @@ snapshots:
       '@remusao/smaz': 1.10.0
       '@types/chrome': 0.0.278
       '@types/firefox-webext-browser': 120.0.4
-      tldts-experimental: 6.1.63
+      tldts-experimental: 6.1.64
 
   '@colors/colors@1.5.0':
     optional: true
@@ -19655,40 +19916,36 @@ snapshots:
     optionalDependencies:
       '@types/react': 18.3.12
 
-  '@reflink/reflink-darwin-arm64@0.1.16':
-    optional: true
-
-  '@reflink/reflink-darwin-x64@0.1.16':
+  '@reflink/reflink-darwin-arm64@0.1.17':
     optional: true
 
-  '@reflink/reflink-linux-arm64-gnu@0.1.16':
+  '@reflink/reflink-linux-arm64-gnu@0.1.17':
     optional: true
 
-  '@reflink/reflink-linux-arm64-musl@0.1.16':
+  '@reflink/reflink-linux-arm64-musl@0.1.17':
     optional: true
 
-  '@reflink/reflink-linux-x64-gnu@0.1.16':
+  '@reflink/reflink-linux-x64-gnu@0.1.17':
     optional: true
 
-  '@reflink/reflink-linux-x64-musl@0.1.16':
+  '@reflink/reflink-linux-x64-musl@0.1.17':
     optional: true
 
-  '@reflink/reflink-win32-arm64-msvc@0.1.16':
+  '@reflink/reflink-win32-arm64-msvc@0.1.17':
     optional: true
 
-  '@reflink/reflink-win32-x64-msvc@0.1.16':
+  '@reflink/reflink-win32-x64-msvc@0.1.17':
     optional: true
 
-  '@reflink/reflink@0.1.16':
+  '@reflink/reflink@0.1.17':
     optionalDependencies:
-      '@reflink/reflink-darwin-arm64': 0.1.16
-      '@reflink/reflink-darwin-x64': 0.1.16
-      '@reflink/reflink-linux-arm64-gnu': 0.1.16
-      '@reflink/reflink-linux-arm64-musl': 0.1.16
-      '@reflink/reflink-linux-x64-gnu': 0.1.16
-      '@reflink/reflink-linux-x64-musl': 0.1.16
-      '@reflink/reflink-win32-arm64-msvc': 0.1.16
-      '@reflink/reflink-win32-x64-msvc': 0.1.16
+      '@reflink/reflink-darwin-arm64': 0.1.17
+      '@reflink/reflink-linux-arm64-gnu': 0.1.17
+      '@reflink/reflink-linux-arm64-musl': 0.1.17
+      '@reflink/reflink-linux-x64-gnu': 0.1.17
+      '@reflink/reflink-linux-x64-musl': 0.1.17
+      '@reflink/reflink-win32-arm64-msvc': 0.1.17
+      '@reflink/reflink-win32-x64-msvc': 0.1.17
     optional: true
 
   '@remix-run/router@1.15.1': {}
@@ -21964,7 +22221,7 @@ snapshots:
   autoprefixer@10.4.20(postcss@8.4.49):
     dependencies:
       browserslist: 4.24.2
-      caniuse-lite: 1.0.30001683
+      caniuse-lite: 1.0.30001684
       fraction.js: 4.3.7
       normalize-range: 0.1.2
       picocolors: 1.1.1
@@ -22097,7 +22354,7 @@ snapshots:
     dependencies:
       bare-events: 2.5.0
       bare-path: 2.1.3
-      bare-stream: 2.4.0
+      bare-stream: 2.4.2
     optional: true
 
   bare-os@2.4.4:
@@ -22108,7 +22365,7 @@ snapshots:
       bare-os: 2.4.4
     optional: true
 
-  bare-stream@2.4.0:
+  bare-stream@2.4.2:
     dependencies:
       streamx: 2.20.2
     optional: true
@@ -22263,7 +22520,7 @@ snapshots:
 
   browserslist@4.24.2:
     dependencies:
-      caniuse-lite: 1.0.30001683
+      caniuse-lite: 1.0.30001684
       electron-to-chromium: 1.5.64
       node-releases: 2.0.18
       update-browserslist-db: 1.1.1(browserslist@4.24.2)
@@ -22416,11 +22673,11 @@ snapshots:
   caniuse-api@3.0.0:
     dependencies:
       browserslist: 4.24.2
-      caniuse-lite: 1.0.30001683
+      caniuse-lite: 1.0.30001684
       lodash.memoize: 4.1.2
       lodash.uniq: 4.5.0
 
-  caniuse-lite@1.0.30001683: {}
+  caniuse-lite@1.0.30001684: {}
 
   canvas@2.11.2(encoding@0.1.13):
     dependencies:
@@ -23751,7 +24008,7 @@ snapshots:
     dependencies:
       safe-buffer: 5.2.1
 
-  echogarden@2.0.6(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(encoding@0.1.13)(utf-8-validate@5.0.10)(zod@3.23.8):
+  echogarden@2.0.7(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(encoding@0.1.13)(utf-8-validate@5.0.10)(zod@3.23.8):
     dependencies:
       '@aws-sdk/client-polly': 3.699.0
       '@aws-sdk/client-transcribe-streaming': 3.699.0
@@ -25542,7 +25799,7 @@ snapshots:
       stdout-update: 4.0.1
       strip-ansi: 7.1.0
     optionalDependencies:
-      '@reflink/reflink': 0.1.16
+      '@reflink/reflink': 0.1.17
 
   is-alphabetical@2.0.1: {}
 
@@ -26318,6 +26575,28 @@ snapshots:
       doublearray: 0.0.2
       zlibjs: 0.3.1
 
+  langchain@0.3.6(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.69.0(encoding@0.1.13)(zod@3.23.8)):
+    dependencies:
+      '@langchain/core': 0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8))
+      '@langchain/openai': 0.3.14(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(encoding@0.1.13)
+      '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))
+      js-tiktoken: 1.0.15
+      js-yaml: 4.1.0
+      jsonpointer: 5.0.1
+      langsmith: 0.2.7(openai@4.69.0(encoding@0.1.13)(zod@3.23.8))
+      openapi-types: 12.1.3
+      p-retry: 4.6.2
+      uuid: 10.0.0
+      yaml: 2.6.1
+      zod: 3.23.8
+      zod-to-json-schema: 3.23.5(zod@3.23.8)
+    optionalDependencies:
+      axios: 1.7.7(debug@4.3.7)
+      handlebars: 4.7.8
+    transitivePeerDependencies:
+      - encoding
+      - openai
+
   langchain@0.3.6(@langchain/core@0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)))(axios@1.7.7)(encoding@0.1.13)(handlebars@4.7.8)(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)):
     dependencies:
       '@langchain/core': 0.3.18(openai@4.73.0(encoding@0.1.13)(zod@3.23.8))
@@ -26348,6 +26627,17 @@ snapshots:
       vscode-languageserver-textdocument: 1.0.12
       vscode-uri: 3.0.8
 
+  langsmith@0.2.7(openai@4.69.0(encoding@0.1.13)(zod@3.23.8)):
+    dependencies:
+      '@types/uuid': 10.0.0
+      commander: 10.0.1
+      p-queue: 6.6.2
+      p-retry: 4.6.2
+      semver: 7.6.3
+      uuid: 10.0.0
+    optionalDependencies:
+      openai: 4.69.0(encoding@0.1.13)(zod@3.23.8)
+
   langsmith@0.2.7(openai@4.73.0(encoding@0.1.13)(zod@3.23.8)):
     dependencies:
       '@types/uuid': 10.0.0
@@ -31123,15 +31413,15 @@ snapshots:
 
   tinyspy@3.0.2: {}
 
-  tldts-core@6.1.63: {}
+  tldts-core@6.1.64: {}
 
-  tldts-experimental@6.1.63:
+  tldts-experimental@6.1.64:
     dependencies:
-      tldts-core: 6.1.63
+      tldts-core: 6.1.64
 
-  tldts@6.1.63:
+  tldts@6.1.64:
     dependencies:
-      tldts-core: 6.1.63
+      tldts-core: 6.1.64
 
   tmp@0.0.33:
     dependencies:
@@ -31188,7 +31478,7 @@ snapshots:
 
   tough-cookie@5.0.0:
     dependencies:
-      tldts: 6.1.63
+      tldts: 6.1.64
 
   tr46@0.0.3: {}
 
diff --git a/scripts/build.sh b/scripts/build.sh
index 9fbbe5f2fb0..2240dd4f5d9 100644
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -37,6 +37,7 @@ PACKAGES=(
     "plugin-bootstrap"
     "plugin-image-generation"
     "plugin-coinbase"
+    "plugin-eternum"
 )
 
 # Build packages in specified order