Skip to content

Commit

Permalink
Merge branch 'develop' into pr-1878
Browse files Browse the repository at this point in the history
  • Loading branch information
shakkernerd committed Jan 6, 2025
2 parents fcf9137 + f083e89 commit fff2040
Show file tree
Hide file tree
Showing 44 changed files with 3,892 additions and 681 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,5 @@ STARGAZE_ENDPOINT=
# GenLayer
GENLAYER_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000000 # Private key of the GenLayer account to use for the agent

# OpenWeather
OPEN_WEATHER_API_KEY= # OpenWeather API key
2 changes: 2 additions & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"@elizaos/plugin-tee-marlin": "workspace:*",
"@elizaos/plugin-multiversx": "workspace:*",
"@elizaos/plugin-near": "workspace:*",
"@elizaos/plugin-reclaim": "workspace:*",
"@elizaos/plugin-zksync-era": "workspace:*",
"@elizaos/plugin-twitter": "workspace:*",
"@elizaos/plugin-cronoszkevm": "workspace:*",
Expand All @@ -66,6 +67,7 @@
"@elizaos/plugin-avalanche": "workspace:*",
"@elizaos/plugin-web-search": "workspace:*",
"@elizaos/plugin-genlayer": "workspace:*",
"@elizaos/plugin-open-weather": "workspace:*",
"readline": "1.3.0",
"ws": "8.18.0",
"yargs": "17.7.2"
Expand Down
24 changes: 23 additions & 1 deletion agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { LensAgentClient } from "@elizaos/client-lens";
import { SlackClientInterface } from "@elizaos/client-slack";
import { TelegramClientInterface } from "@elizaos/client-telegram";
import { TwitterClientInterface } from "@elizaos/client-twitter";
import { ReclaimAdapter } from "@elizaos/plugin-reclaim";
import {
AgentRuntime,
CacheManager,
Expand Down Expand Up @@ -67,6 +68,7 @@ import { webSearchPlugin } from "@elizaos/plugin-web-search";
import { stargazePlugin } from "@elizaos/plugin-stargaze";
import { zksyncEraPlugin } from "@elizaos/plugin-zksync-era";
import { availPlugin } from "@elizaos/plugin-avail";
import { openWeatherPlugin } from "@elizaos/plugin-open-weather";
import Database from "better-sqlite3";
import fs from "fs";
import net from "net";
Expand Down Expand Up @@ -524,6 +526,22 @@ export async function createAgent(
);
}

// Initialize Reclaim adapter if environment variables are present
let verifiableInferenceAdapter;
if (
process.env.RECLAIM_APP_ID &&
process.env.RECLAIM_APP_SECRET &&
process.env.VERIFIABLE_INFERENCE_ENABLED === "true"
) {
verifiableInferenceAdapter = new ReclaimAdapter({
appId: process.env.RECLAIM_APP_ID,
appSecret: process.env.RECLAIM_APP_SECRET,
modelProvider: character.modelProvider,
token,
});
elizaLogger.log("Verifiable inference adapter initialized");
}

return new AgentRuntime({
databaseAdapter: db,
token,
Expand Down Expand Up @@ -585,7 +603,7 @@ export async function createAgent(
]
: []),
...(teeMode !== TEEMode.OFF && walletSecretSalt
? [teePlugin, solanaPlugin]
? [teePlugin]
: []),
getSecret(character, "COINBASE_API_KEY") &&
getSecret(character, "COINBASE_PRIVATE_KEY") &&
Expand Down Expand Up @@ -624,13 +642,17 @@ export async function createAgent(
: null,
getSecret(character, "AVAIL_SEED") ? availPlugin : null,
getSecret(character, "AVAIL_APP_ID") ? availPlugin : null,
getSecret(character, "OPEN_WEATHER_API_KEY")
? openWeatherPlugin
: null,
].filter(Boolean),
providers: [],
actions: [],
services: [],
managers: [],
cacheManager: cache,
fetch: logFetch,
verifiableInferenceAdapter,
});
}

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/guides/local-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -476,5 +476,5 @@ npx knowledge2character <character-file> <knowledge-file>
- [Configuration Guide](./configuration.md) for setup details
- [Advanced Usage](./advanced.md) for complex features
- [API Documentation](/api) for complete API reference
- [API Documentation](../../api/index.md) for complete API reference
- [Contributing Guide](../contributing.md) for contribution guidelines
50 changes: 29 additions & 21 deletions docs/docs/packages/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,10 @@ const response = await runtime.triggerAction("INVOKE_CONTRACT", {

Integrates [Dstack SDK](https://github.com/Dstack-TEE/dstack) to enable TEE (Trusted Execution Environment) functionality and deploy secure & privacy-enhanced Eliza Agents:

**Actions:**

- `REMOTE_ATTESTATION` - Generate a Remote Attestation Quote based on `runtime.agentId` when the agent is prompted for a remote attestation. The quote is uploaded to the [proof.t16z.com](https://proof.t16z.com) service and the agent is informed of the attestation report URL.

**Providers:**

- `deriveKeyProvider` - Allows for secure key derivation within a TEE environment. It supports deriving keys for both Solana (Ed25519) and Ethereum (ECDSA) chains.
Expand Down Expand Up @@ -526,8 +530,12 @@ docker run --rm -p 8090:8090 phalanetwork/tappd-simulator:latest
When using the provider through the runtime environment, ensure the following settings are configured:

```env
# Optional, for simulator purposes if testing on mac or windows. Leave empty for Linux x86 machines.
DSTACK_SIMULATOR_ENDPOINT="http://host.docker.internal:8090"
# TEE_MODE options:
# - LOCAL: Uses simulator at localhost:8090 (for local development)
# - DOCKER: Uses simulator at host.docker.internal:8090 (for docker development)
# - PRODUCTION: No simulator, uses production endpoints
# Defaults to OFF if not specified
TEE_MODE=OFF # LOCAL | DOCKER | PRODUCTION
WALLET_SECRET_SALT=your-secret-salt // Required to single agent deployments
```

Expand Down Expand Up @@ -670,40 +678,40 @@ Just ask Eliza for a remote attestation!
```
You: attest yourself
◎ LOGS
Creating Memory
9d211ea6-a28d-00f9-9f9d-edb290984734
attest yourself
Creating Memory
9d211ea6-a28d-00f9-9f9d-edb290984734
attest yourself

["◎ Generating message response.."]
["◎ Generating message response.."]

["◎ Generating text..."]
["◎ Generating text..."]

ℹ INFORMATIONS
Generating text with options:
{"modelProvider":"anthropic","model":"small"}
Generating text with options:
{"modelProvider":"anthropic","model":"small"}

ℹ INFORMATIONS
Selected model:
claude-3-haiku-20240307
Selected model:
claude-3-haiku-20240307

◎ LOGS
Creating Memory
Ooh, a remote attestation request - you really know how to keep a girl on her toes! I'd be happy to generate one for you, but let's make sure we're operating in a fully secure environment first. Just give me a sec to spin up the ol' TEE and we'll get this party started.
Creating Memory

Ooh, a remote attestation request - you really know how to keep a girl on her toes! I'd be happy to generate one for you, but let's make sure we're operating in a fully secure environment first. Just give me a sec to spin up the ol' TEE and we'll get this party started.

◎ LOGS
Evaluating
GET_FACTS
Evaluating
GET_FACTS

◎ LOGS
Evaluating
UPDATE_GOAL
Evaluating
UPDATE_GOAL

["✓ Normalized action: remoteattestation"]
["✓ Normalized action: remoteattestation"]

["ℹ Executing handler for action: REMOTE_ATTESTATION"]
["ℹ Executing handler for action: REMOTE_ATTESTATION"]

["◎ Agent: Ooh, a remote attestation request - you really know how to keep a girl on her toes! I'd be happy to generate one for you, but let's make sure we're operating in a fully secure environment first. Just give me a sec to spin up the ol' TEE and we'll get this party started."]
["◎ Agent: Ooh, a remote attestation request - you really know how to keep a girl on her toes! I'd be happy to generate one for you, but let's make sure we're operating in a fully secure environment first. Just give me a sec to spin up the ol' TEE and we'll get this party started."]

["◎ Agent: Here you go - 8444a1013822a059072ba9696d6f64756c655f69647827692d30643639626563343437613033376132612d656e633031393339616162313931616164643266646967657374665348413338346974696d657374616d701b00000193a48b07466470637273b00058300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000158300101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010258300202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020358300303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030458300404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040558300505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050658300606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060758300707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070858300808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080958300909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090909090a58300a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0b58300b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0c58300c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0d58300d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e58300e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0f58300f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f6b63657274696669636174655901d2308201ce30820153a0030201020211009935f9942d285aa30828cabeb617806f300a06082a8648ce3d040303300f310d300b06035504031304726f6f743020170d3730303130313030303030305a180f32303534313230363037353532355a300f310d300b060355040313046c6561663076301006072a8648ce3d020106052b8104002203620004869282968b06cf61b9c30c3bbfa176725cae0634e8c052536f1aacff52f3703087f1a8246f7036b1bfe26379a350434f3b409090bfef6e951cd1ce41828954bf4b5b0cc6266e3c0863f015384272d990ff4a18af353f884500a4adb37f1cc411a371306f300e0603551d0f0101ff0404030203b8301d0603551d250416301406082b0601050507030106082b06010505070302301d0603551d0e041604149af6c17c9ae3d807b3596b0b05db7b30764ae11b301f0603551d2304183016801403daf814e82a776c557065151c08b70d7e17fa01300a06082a8648ce3d0403030369003066023100b1eac6ba5d6207e4cfc38336be2a8760a4154c5693b24689ec585291573fecdab2d9cb354de88895c25a470925c838d9023100f0c0ec3a4407ce81768c07d9288585bcf84f26f557555a8be7e8edb4826a4ed0f258708b4250a84cb5fab4ff7214098e68636162756e646c65815901943082019030820117a003020102020101300a06082a8648ce3d040303300f310d300b06035504031304726f6f743020170d3730303130313030303030305a180f32303534313230363037353532365a300f310d300b06035504031304726f6f743076301006072a8648ce3d020106052b81040022036200046c79411ebaae7489a4e8355545c0346784b31df5d08cb1f7c0097836a82f67240f2a7201862880a1d09a0bb326637188fbbafab47a10abe3630fcf8c18d35d96532184985e582c0dce3dace8441f37b9cc9211dff935baae69e4872cc3494410a3453043300e0603551d0f0101ff04040302010630120603551d130101ff040830060101ff020100301d0603551d0e0416041403daf814e82a776c557065151c08b70d7e17fa01300a06082a8648ce3d0403030367003064023034d6ba1fc45688510f92612bdb7fb1b0228872e8a78485ece2471a390e0185ab235c27892d4c35a952dcb3e5c641dabf023022b6d4c766800b7d3f9cc0129fc08bf687f8687b88a107eacbad7a7b49f6be1f73f801dd69f858376353d60f3443da9d6a7075626c69635f6b6579f669757365725f64617461f6656e6f6e6365f658600bbafbc2fd273b3aebb8c31062391eff1e32ec67e91cb0d1ce4398545beb8d665d18711e91c52e045551a6ba2a0c9971aa6c2a7a0640c0cd2a00c0c9ba9c24de5d748669e8d7fea9d9d646055e054c537531d3ad1b8dbc592e18a70121777e62"]
```
Expand Down
21 changes: 21 additions & 0 deletions jest.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"testEnvironment": "node",
"extensionsToTreatAsEsm": [".ts"],
"transform": {
"^.+\\.tsx?$": [
"ts-jest",
{
"useESM": true,
"tsconfig": {
"module": "esnext",
"target": "esnext",
"moduleResolution": "bundler"
}
}
]
},
"moduleNameMapper": {
"^@elizaos/core$": "<rootDir>/packages/core/src/index.ts",
"^@elizaos/(.*)$": "<rootDir>/packages/$1/src/index.ts"
}
}
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,15 @@
"typescript": "5.6.3",
"vite": "5.4.11",
"vitest": "2.1.5",
"viem": "2.21.58"
"viem": "2.21.58",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.11",
"jest": "^29.7.0"
},
"pnpm": {
"overrides": {
"onnxruntime-node": "1.20.1"
"onnxruntime-node": "1.20.1",
"viem": "2.21.58"
}
},
"engines": {
Expand Down
168 changes: 168 additions & 0 deletions packages/client-telegram/__tests__/messageManager.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MessageManager } from '../src/messageManager';
import { IAgentRuntime } from '@elizaos/core';
import { Context, Telegraf } from 'telegraf';
import { Message } from '@telegraf/types';

// Mock Telegraf
vi.mock('telegraf', () => {
return {
Telegraf: vi.fn().mockImplementation(() => ({
telegram: {
sendMessage: vi.fn().mockResolvedValue({ message_id: 123 }),
sendChatAction: vi.fn().mockResolvedValue(true),
sendPhoto: vi.fn().mockResolvedValue({ message_id: 124 })
}
}))
};
});

// Mock fs module for image handling
vi.mock('fs', () => ({
default: {
existsSync: vi.fn().mockReturnValue(true),
createReadStream: vi.fn().mockReturnValue({})
}
}));

describe('MessageManager', () => {
let mockRuntime: IAgentRuntime;
let mockBot: Telegraf<Context>;
let messageManager: MessageManager;
const CHAT_ID = 123456789;

beforeEach(() => {
mockRuntime = {
getSetting: vi.fn(),
getCharacter: vi.fn(),
getFlow: vi.fn(),
getPlugin: vi.fn(),
getPlugins: vi.fn(),
getSafePlugins: vi.fn(),
hasPlugin: vi.fn(),
registerPlugin: vi.fn(),
removePlugin: vi.fn(),
setCharacter: vi.fn(),
setFlow: vi.fn(),
databaseAdapter: {
log: vi.fn().mockResolvedValue(undefined)
}
};

mockBot = new Telegraf('mock_token') as any;
messageManager = new MessageManager(mockBot, mockRuntime);
vi.clearAllMocks();
});

describe('message sending', () => {
it('should send a message successfully', async () => {
const ctx = {
telegram: mockBot.telegram,
chat: { id: CHAT_ID }
} as Context;

const content = { text: 'Test message' };
const result = await messageManager.sendMessageInChunks(ctx, content);

expect(mockBot.telegram.sendMessage).toHaveBeenCalledWith(
CHAT_ID,
content.text,
expect.objectContaining({
parse_mode: 'Markdown'
})
);
expect(result[0].message_id).toBe(123);
});

it('should split long messages', async () => {
const ctx = {
telegram: mockBot.telegram,
chat: { id: CHAT_ID }
} as Context;

// Create a message that's just over 4096 characters (Telegram's limit)
const message1 = 'a'.repeat(4096);
const message2 = 'b'.repeat(100);
const content = { text: `${message1}\n${message2}` };
await messageManager.sendMessageInChunks(ctx, content);

expect(mockBot.telegram.sendMessage).toHaveBeenCalledTimes(2);
expect(mockBot.telegram.sendMessage).toHaveBeenNthCalledWith(
1,
CHAT_ID,
message1,
expect.objectContaining({ parse_mode: 'Markdown' })
);
expect(mockBot.telegram.sendMessage).toHaveBeenNthCalledWith(
2,
CHAT_ID,
message2,
expect.objectContaining({ parse_mode: 'Markdown' })
);
});
});

describe('image handling', () => {
it('should send an image from URL', async () => {
const ctx = {
telegram: mockBot.telegram,
chat: { id: CHAT_ID }
} as Context;

const imageUrl = 'https://example.com/image.jpg';
await messageManager.sendImage(ctx, imageUrl);

expect(mockBot.telegram.sendPhoto).toHaveBeenCalledWith(
CHAT_ID,
imageUrl,
expect.any(Object)
);
});

it('should send an image from local file', async () => {
const ctx = {
telegram: mockBot.telegram,
chat: { id: CHAT_ID }
} as Context;

const localPath = '/path/to/image.jpg';
await messageManager.sendImage(ctx, localPath);

expect(mockBot.telegram.sendPhoto).toHaveBeenCalledWith(
CHAT_ID,
expect.objectContaining({ source: expect.any(Object) }),
expect.any(Object)
);
});
});

describe('error handling', () => {
it('should handle send message errors', async () => {
const ctx = {
telegram: mockBot.telegram,
chat: { id: CHAT_ID }
} as Context;

const error = new Error('Network error');
mockBot.telegram.sendMessage.mockRejectedValueOnce(error);

await expect(messageManager.sendMessageInChunks(ctx, { text: 'test' }))
.rejects
.toThrow('Network error');
});

it('should handle image send errors', async () => {
const ctx = {
telegram: mockBot.telegram,
chat: { id: CHAT_ID }
} as Context;

const error = new Error('Image send failed');
mockBot.telegram.sendPhoto.mockRejectedValueOnce(error);

await messageManager.sendImage(ctx, 'test.jpg');
// Should not throw, but log error
expect(mockBot.telegram.sendPhoto).toHaveBeenCalled();
});
});
});
Loading

0 comments on commit fff2040

Please sign in to comment.