Skip to content

Commit 6a9cbe3

Browse files
authored
Merge branch 'develop' into patch-3
2 parents 1c1072a + c7779eb commit 6a9cbe3

File tree

11 files changed

+194
-49
lines changed

11 files changed

+194
-49
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ git clone https://github.com/elizaos/eliza.git
8080
# Checkout the latest release
8181
# This project iterates fast, so we recommend checking out the latest release
8282
git checkout $(git describe --tags --abbrev=0)
83+
# If the above doesn't checkout the latest release, this should work:
84+
# git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
8385
```
8486

8587
### Start Eliza with Gitpod

agent/src/index.ts

+56-26
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,61 @@ function tryLoadFile(filePath: string): string | null {
149149
return null;
150150
}
151151
}
152+
function mergeCharacters(base: Character, child: Character): Character {
153+
const mergeObjects = (baseObj: any, childObj: any) => {
154+
const result: any = {};
155+
const keys = new Set([...Object.keys(baseObj || {}), ...Object.keys(childObj || {})]);
156+
keys.forEach(key => {
157+
if (typeof baseObj[key] === 'object' && typeof childObj[key] === 'object' && !Array.isArray(baseObj[key]) && !Array.isArray(childObj[key])) {
158+
result[key] = mergeObjects(baseObj[key], childObj[key]);
159+
} else if (Array.isArray(baseObj[key]) || Array.isArray(childObj[key])) {
160+
result[key] = [...(baseObj[key] || []), ...(childObj[key] || [])];
161+
} else {
162+
result[key] = childObj[key] !== undefined ? childObj[key] : baseObj[key];
163+
}
164+
});
165+
return result;
166+
};
167+
return mergeObjects(base, child);
168+
}
169+
async function loadCharacter(filePath: string): Promise<Character> {
170+
const content = tryLoadFile(filePath);
171+
if (!content) {
172+
throw new Error(`Character file not found: ${filePath}`);
173+
}
174+
let character = JSON.parse(content);
175+
validateCharacterConfig(character);
176+
177+
// .id isn't really valid
178+
const characterId = character.id || character.name;
179+
const characterPrefix = `CHARACTER.${characterId.toUpperCase().replace(/ /g, "_")}.`;
180+
const characterSettings = Object.entries(process.env)
181+
.filter(([key]) => key.startsWith(characterPrefix))
182+
.reduce((settings, [key, value]) => {
183+
const settingKey = key.slice(characterPrefix.length);
184+
return { ...settings, [settingKey]: value };
185+
}, {});
186+
if (Object.keys(characterSettings).length > 0) {
187+
character.settings = character.settings || {};
188+
character.settings.secrets = {
189+
...characterSettings,
190+
...character.settings.secrets,
191+
};
192+
}
193+
// Handle plugins
194+
character.plugins = await handlePluginImporting(
195+
character.plugins
196+
);
197+
if (character.extends) {
198+
elizaLogger.info(`Merging ${character.name} character with parent characters`);
199+
for (const extendPath of character.extends) {
200+
const baseCharacter = await loadCharacter(path.resolve(path.dirname(filePath), extendPath));
201+
character = mergeCharacters(baseCharacter, character);
202+
elizaLogger.info(`Merged ${character.name} with ${baseCharacter.name}`);
203+
}
204+
}
205+
return character;
206+
}
152207

153208
export async function loadCharacters(
154209
charactersArg: string
@@ -212,32 +267,7 @@ export async function loadCharacters(
212267
}
213268

214269
try {
215-
const character = JSON.parse(content);
216-
validateCharacterConfig(character);
217-
218-
// .id isn't really valid
219-
const characterId = character.id || character.name;
220-
const characterPrefix = `CHARACTER.${characterId.toUpperCase().replace(/ /g, "_")}.`;
221-
222-
const characterSettings = Object.entries(process.env)
223-
.filter(([key]) => key.startsWith(characterPrefix))
224-
.reduce((settings, [key, value]) => {
225-
const settingKey = key.slice(characterPrefix.length);
226-
return { ...settings, [settingKey]: value };
227-
}, {});
228-
229-
if (Object.keys(characterSettings).length > 0) {
230-
character.settings = character.settings || {};
231-
character.settings.secrets = {
232-
...characterSettings,
233-
...character.settings.secrets,
234-
};
235-
}
236-
237-
// Handle plugins
238-
character.plugins = await handlePluginImporting(
239-
character.plugins
240-
);
270+
const character: Character = await loadCharacter(resolvedPath);
241271

242272
loadedCharacters.push(character);
243273
elizaLogger.info(

packages/client-discord/src/actions/joinvoice.ts

+16-16
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
IAgentRuntime,
99
Memory,
1010
State,
11+
generateText,
12+
ModelClass,
1113
} from "@elizaos/core";
1214
import {
1315
Channel,
@@ -17,6 +19,7 @@ import {
1719
Guild,
1820
GuildMember,
1921
} from "discord.js";
22+
import { joinVoiceChannel } from "@discordjs/voice";
2023

2124
export default {
2225
name: "JOIN_VOICE",
@@ -66,12 +69,7 @@ export default {
6669
return false;
6770
}
6871

69-
const client = state.discordClient as Client;
70-
71-
// Check if the client is connected to any voice channel
72-
const isConnectedToVoice = client.voice.adapters.size === 0;
73-
74-
return isConnectedToVoice;
72+
return true;
7573
},
7674
description: "Join a voice channel to participate in voice chat.",
7775
handler: async (
@@ -115,31 +113,30 @@ export default {
115113
);
116114
});
117115

118-
if (!state.voiceManager) {
119-
state.voiceManager = new VoiceManager({
120-
client: state.discordClient,
121-
runtime: runtime,
122-
});
123-
}
124-
125116
if (targetChannel) {
126-
state.voiceManager.joinVoiceChannel({
117+
joinVoiceChannel({
127118
channelId: targetChannel.id,
128119
guildId: (discordMessage as DiscordMessage).guild?.id as string,
129120
adapterCreator: (client.guilds.cache.get(id) as Guild)
130121
.voiceAdapterCreator,
122+
selfDeaf: false,
123+
selfMute: false,
124+
group: client.user.id,
131125
});
132126
return true;
133127
} else {
134128
const member = (discordMessage as DiscordMessage)
135129
.member as GuildMember;
136130
if (member?.voice?.channel) {
137-
state.voiceManager.joinVoiceChannel({
131+
joinVoiceChannel({
138132
channelId: member.voice.channel.id,
139133
guildId: (discordMessage as DiscordMessage).guild
140134
?.id as string,
141135
adapterCreator: (client.guilds.cache.get(id) as Guild)
142136
.voiceAdapterCreator,
137+
selfDeaf: false,
138+
selfMute: false,
139+
group: client.user.id,
143140
});
144141
return true;
145142
}
@@ -204,12 +201,15 @@ You should only respond with the name of the voice channel or none, no commentar
204201
});
205202

206203
if (targetChannel) {
207-
state.voiceManager.joinVoiceChannel({
204+
joinVoiceChannel({
208205
channelId: targetChannel.id,
209206
guildId: (discordMessage as DiscordMessage).guild
210207
?.id as string,
211208
adapterCreator: (client.guilds.cache.get(id) as Guild)
212209
.voiceAdapterCreator,
210+
selfDeaf: false,
211+
selfMute: false,
212+
group: client.user.id,
213213
});
214214
return true;
215215
}

packages/core/src/defaultCharacter.ts

+1
Original file line numberDiff line numberDiff line change
@@ -527,4 +527,5 @@ export const defaultCharacter: Character = {
527527
"meticulous",
528528
"provocative",
529529
],
530+
extends: [],
530531
};

packages/core/src/environment.ts

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export const CharacterSchema = z.object({
135135
prompt: z.string().optional(),
136136
})
137137
.optional(),
138+
extends: z.array(z.string()).optional(),
138139
});
139140

140141
// Type inference

packages/core/src/tests/uuid.test.ts

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { beforeEach, describe, expect, it } from "vitest";
2+
import { stringToUuid } from "../uuid";
3+
import type { UUID } from "../types";
4+
5+
describe("UUID Module", () => {
6+
// Helper function to generate test strings
7+
const generateTestString = (): string =>
8+
Math.random().toString(36).substring(7);
9+
10+
// Test data setup
11+
let testString: string;
12+
let testNumber: number;
13+
14+
beforeEach(() => {
15+
testString = generateTestString();
16+
testNumber = Math.floor(Math.random() * 1000);
17+
});
18+
19+
describe("stringToUuid", () => {
20+
it("should generate a valid UUID matching the standard format", () => {
21+
const result = stringToUuid(testString) as UUID;
22+
expect(result).toMatch(
23+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
24+
);
25+
});
26+
27+
it("should generate consistent UUIDs for identical inputs", () => {
28+
const input = testString;
29+
const uuid1 = stringToUuid(input) as UUID;
30+
const uuid2 = stringToUuid(input) as UUID;
31+
expect(uuid1).toBe(uuid2);
32+
});
33+
34+
it("should generate unique UUIDs for different inputs", () => {
35+
const input1 = testString;
36+
const input2 = generateTestString();
37+
const uuid1 = stringToUuid(input1) as UUID;
38+
const uuid2 = stringToUuid(input2) as UUID;
39+
expect(uuid1).not.toBe(uuid2);
40+
});
41+
42+
describe("input handling", () => {
43+
it("should convert number inputs to strings correctly", () => {
44+
const numberUuid = stringToUuid(testNumber) as UUID;
45+
const stringUuid = stringToUuid(testNumber.toString()) as UUID;
46+
expect(numberUuid).toBe(stringUuid);
47+
});
48+
49+
it("should throw TypeError for invalid input types", () => {
50+
expect(() => stringToUuid(undefined as any)).toThrow(TypeError);
51+
expect(() => stringToUuid(null as any)).toThrow(TypeError);
52+
expect(() => stringToUuid({} as any)).toThrow(TypeError);
53+
});
54+
55+
it("should handle empty string input", () => {
56+
const result = stringToUuid("") as UUID;
57+
expect(result).toMatch(
58+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
59+
);
60+
});
61+
62+
it("should handle Unicode characters and emojis consistently", () => {
63+
const unicodeInput = "Hello 世界! 🌍";
64+
const result1 = stringToUuid(unicodeInput) as UUID;
65+
const result2 = stringToUuid(unicodeInput) as UUID;
66+
expect(result1).toBe(result2);
67+
expect(result1).toMatch(
68+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
69+
);
70+
});
71+
});
72+
73+
describe("UUID version and variant bits", () => {
74+
it("should set correct version bits (version 5)", () => {
75+
const uuid = stringToUuid(testString) as UUID;
76+
const versionChar = uuid.split("-")[2][0];
77+
expect(versionChar).toBe("5");
78+
});
79+
80+
it("should set correct variant bits (RFC4122)", () => {
81+
const uuid = stringToUuid(testString) as UUID;
82+
const variantByte = parseInt(
83+
uuid.split("-")[3].slice(0, 2),
84+
16
85+
);
86+
expect(variantByte >= 0x80 && variantByte <= 0xbf).toBe(true);
87+
});
88+
});
89+
90+
describe("encoding handling", () => {
91+
it("should handle URL-unsafe characters", () => {
92+
const urlUnsafeInput = "test?query=value&param=123";
93+
const result = stringToUuid(urlUnsafeInput) as UUID;
94+
expect(result).toMatch(
95+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
96+
);
97+
});
98+
99+
it("should handle very long inputs", () => {
100+
const longInput = "a".repeat(1000);
101+
const result = stringToUuid(longInput) as UUID;
102+
expect(result).toMatch(
103+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
104+
);
105+
});
106+
});
107+
});
108+
});

packages/core/src/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,7 @@ export enum Clients {
647647
LENS = "lens",
648648
AUTO = "auto",
649649
SLACK = "slack",
650+
GITHUB = "github",
650651
}
651652

652653
export interface IAgentConfig {
@@ -871,6 +872,8 @@ export type Character = {
871872
nft?: {
872873
prompt: string;
873874
};
875+
/**Optinal Parent characters to inherit information from */
876+
extends?: string[];
874877
};
875878

876879
/**

packages/plugin-solana/src/actions/fomo.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export const createAndBuyToken = async ({
6666
priorityFee,
6767
requiredLiquidity = 85,
6868
allowOffCurve,
69-
commitment = "finalized",
69+
commitment = "confirmed",
7070
fomo,
7171
connection,
7272
}: {
@@ -182,7 +182,7 @@ export const buyToken = async ({
182182
slippage,
183183
connection,
184184
currency = "sol",
185-
commitment = "finalized",
185+
commitment = "confirmed",
186186
}: {
187187
fomo: Fomo;
188188
buyer: Keypair;
@@ -281,7 +281,7 @@ export const sellToken = async ({
281281
slippage,
282282
connection,
283283
currency = "token",
284-
commitment = "finalized",
284+
commitment = "confirmed",
285285
}: {
286286
fomo: Fomo;
287287
seller: Keypair;

packages/plugin-solana/src/actions/pumpfun.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export const createAndBuyToken = async ({
5656
buyAmountSol,
5757
priorityFee,
5858
allowOffCurve,
59-
commitment = "finalized",
59+
commitment = "confirmed",
6060
sdk,
6161
connection,
6262
slippage,
@@ -416,7 +416,7 @@ export default {
416416

417417
const wallet = new Wallet(deployerKeypair);
418418
const provider = new AnchorProvider(connection, wallet, {
419-
commitment: "finalized",
419+
commitment: "confirmed",
420420
});
421421
const sdk = new PumpFunSDK(provider);
422422
// const slippage = runtime.getSetting("SLIPPAGE");

packages/plugin-solana/src/actions/swapUtils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export const executeSwap = async (
8282
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
8383
blockhash: latestBlockhash.blockhash,
8484
},
85-
"finalized"
85+
"confirmed"
8686
);
8787
if (confirmation.value.err) {
8888
elizaLogger.log("Confirmation error", confirmation.value.err);

packages/plugin-sui/src/providers/wallet.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export class WalletProvider {
181181
}
182182
);
183183
const prices: Prices = {
184-
sui: { usd: suiPriceData.pair.priceUsd },
184+
sui: { usd: (1 / suiPriceData.pair.priceNative).toString() },
185185
};
186186
this.setCachedData(cacheKey, prices);
187187
return prices;

0 commit comments

Comments
 (0)