diff --git a/.env.example b/.env.example index 0ab24f6b05..cc530b5198 100644 --- a/.env.example +++ b/.env.example @@ -214,12 +214,6 @@ SMALL_ANTHROPIC_MODEL= # Default: claude-3-haiku-20240307 MEDIUM_ANTHROPIC_MODEL= # Default: claude-3-5-sonnet-20241022 LARGE_ANTHROPIC_MODEL= # Default: claude-3-5-sonnet-20241022 -# VoyageAI Configuration -VOYAGEAI_API_KEY= -USE_VOYAGEAI_EMBEDDING= # Set to TRUE for VoyageAI, leave blank for local -VOYAGEAI_EMBEDDING_MODEL= # Default: voyage-3-lite -VOYAGEAI_EMBEDDING_DIMENSIONS= # Default: 512 - # Heurist Configuration HEURIST_API_KEY= # Get from https://heurist.ai/dev-access SMALL_HEURIST_MODEL= # Default: meta-llama/llama-3-70b-instruct @@ -514,7 +508,9 @@ GIPHY_API_KEY= # OpenWeather OPEN_WEATHER_API_KEY= # OpenWeather API key - +#GITCOIN Passport +PASSPORT_API_KEY= #Gitcoin Passport key +PASSPORT_SCORER= #Scorer number # EchoChambers Configuration ECHOCHAMBERS_API_URL=http://127.0.0.1:3333 diff --git a/agent/package.json b/agent/package.json index 1e6e261939..73cd235caa 100644 --- a/agent/package.json +++ b/agent/package.json @@ -50,6 +50,7 @@ "@elizaos/plugin-flow": "workspace:*", "@elizaos/plugin-gitbook": "workspace:*", "@elizaos/plugin-story": "workspace:*", + "@elizaos/plugin-gitcoin-passport": "workspace:*", "@elizaos/plugin-goat": "workspace:*", "@elizaos/plugin-lensNetwork": "workspace:*", "@elizaos/plugin-icp": "workspace:*", diff --git a/agent/src/index.ts b/agent/src/index.ts index 1911cc795a..36ebce199b 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -32,6 +32,7 @@ import { settings, stringToUuid, validateCharacterConfig, + parseBooleanFromText, } from "@elizaos/core"; import { zgPlugin } from "@elizaos/plugin-0g"; @@ -70,6 +71,7 @@ import { flowPlugin } from "@elizaos/plugin-flow"; import { fuelPlugin } from "@elizaos/plugin-fuel"; import { genLayerPlugin } from "@elizaos/plugin-genlayer"; import { giphyPlugin } from "@elizaos/plugin-giphy"; +import { gitcoinPassportPlugin } from "@elizaos/plugin-gitcoin-passport"; import { hyperliquidPlugin } from "@elizaos/plugin-hyperliquid"; import { imageGenerationPlugin } from "@elizaos/plugin-image-generation"; import { lensPlugin } from "@elizaos/plugin-lensNetwork"; @@ -830,7 +832,7 @@ export async function createAgent( getSecret(character, "ABSTRACT_PRIVATE_KEY") ? abstractPlugin : null, - getSecret(character, "B2_PRIVATE_KEY") ? b2Plugin: null, + getSecret(character, "B2_PRIVATE_KEY") ? b2Plugin : null, getSecret(character, "BINANCE_API_KEY") && getSecret(character, "BINANCE_SECRET_KEY") ? binancePlugin @@ -873,6 +875,9 @@ export async function createAgent( getSecret(character, "LETZAI_API_KEY") ? letzAIPlugin : null, getSecret(character, "STARGAZE_ENDPOINT") ? stargazePlugin : null, getSecret(character, "GIPHY_API_KEY") ? giphyPlugin : null, + getSecret(character, "PASSPORT_API_KEY") + ? gitcoinPassportPlugin + : null, getSecret(character, "GENLAYER_PRIVATE_KEY") ? genLayerPlugin : null, @@ -1114,3 +1119,16 @@ startAgents().catch((error) => { elizaLogger.error("Unhandled error in startAgents:", error); process.exit(1); }); + +// Prevent unhandled exceptions from crashing the process if desired +if (process.env.PREVENT_UNHANDLED_EXIT && parseBooleanFromText(process.env.PREVENT_UNHANDLED_EXIT)) { + // Handle uncaught exceptions to prevent the process from crashing + process.on('uncaughtException', function(err) { + console.error("uncaughtException", err); + }); + + // Handle unhandled rejections to prevent the process from crashing + process.on('unhandledRejection', function(err) { + console.error("unhandledRejection", err); + }); +} diff --git a/client/src/components/chat.tsx b/client/src/components/chat.tsx index b54521de53..8173e99da5 100644 --- a/client/src/components/chat.tsx +++ b/client/src/components/chat.tsx @@ -61,10 +61,13 @@ export default function Page({ agentId }: { agentId: UUID }) { const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + if (e.nativeEvent.isComposing) return; handleSendMessage(e as unknown as React.FormEvent); } }; + const handleSendMessage = (e: React.FormEvent) => { e.preventDefault(); if (!input) return; diff --git a/docs/community/Contributors/weekly-contributor-meeting/2024-12-10.md b/docs/community/Contributors/weekly-contributor-meeting/2024-12-10.md new file mode 100644 index 0000000000..7dcd69a675 --- /dev/null +++ b/docs/community/Contributors/weekly-contributor-meeting/2024-12-10.md @@ -0,0 +1,69 @@ +--- +title: "Weekly Contributor Meeting Notes" +date: 2024-12-10 +description: "Notes from the first ai16z Eliza contributor meeting, addressing key challenges in stability, plugins, and codebase organization, with proposed solutions for improvement." +--- + +# Weekly Contributor Meeting Notes + +(December 10, 2024 4:00 PM PST) + +**Taming the Beast, The Quest for AI Agent Stability** + + +## Summary + +This first weekly meeting of maintainers and contributors for the ai16z Eliza repo, organized by Odilitime. The discussion revolved around improving the stability, maintainability, and organization of the rapidly growing project. + +**Key Issues and Concerns:** + +* **Instability and Breakages:** Frequent crashes and bugs were reported, especially after merging new code. Main was breaking repeatedly despite fixes. +* **Plugin Management:** There was a hold on merging new plugins due to concerns about quality, maintainability, and the need for a more robust process. +* **Code Quality and Modularity:** Concerns were raised about the overall quality and modularity of the codebase, particularly regarding how plugins were integrated. The lack of clear separation between core logic and plugins was causing issues. +* **Testing:** The absence of comprehensive testing, especially for plugins and adapters, was identified as a major problem contributing to instability. +* **Documentation:** The documentation was incomplete and outdated, making it difficult for new contributors to understand the project and how to use plugins. +* **Organization and Planning:** The need for better organization, planning, and communication was highlighted, particularly for coordinating contributions and defining a clear roadmap. +* **Contributor Incentives:** The discussion touched on how to properly incentivize and reward contributors, with considerations for retroactive rewards and avoiding a "mercenary" culture. + +**Proposed Solutions and Action Items:** + +* **Branching Strategy:** Enforce pushing new features to the `develop` branch instead of `main` to prevent constant breakages. Close PRs targeting `main` that are not bug fixes. +* **Plugin V2 API:** Consider creating a new V2 API for plugins that better distinguishes between clients and adapters, allowing for a cleaner separation of concerns. +* **Monorepo Tools:** Explore using monorepo tools like Turbo or NX to better manage dependencies and potentially break down the project into smaller, more manageable modules. +* **Testing Framework:** Develop a testing framework to make it easier to write tests for plugins, adapters, and other components. Implement end-to-end tests to ensure basic functionality. +* **Plugin Requirements:** Draft new requirements for plugins, including contact information for maintainers and possibly a migration path to a V2 API. +* **JSON Configuration:** Allow specifying plugins in the character JSON configuration file to enable easier multi-tenant agent setup. +* **Documentation Improvement:** Prioritize improving the documentation, potentially with the help of an AI agent to keep it up-to-date. +* **Organization and Communication:** Use GitHub issues more effectively for tracking tasks and discussions. Consider using tools like Gorse to visualize code contributions. +* **Contributor Roles:** Clarify roles and responsibilities, particularly regarding release engineering, architecture, and DevOps. +* **Contributor Rewards:** Develop a system for rewarding contributors, potentially using quadratic funding and considering retroactive rewards. +* **Regular Meetings:** Establish regular meetings (weekly, as suggested) to improve communication and coordination among maintainers and contributors. +* **AI Agents:** Utilize AI agents to assist with documentation, onboarding, and other tasks. + +**Participants:** + +The meeting involved several key participants, including Odilitime (the organizer), Jin, 0x8664,and several others who actively contributed to the discussion. + +**Overall Tone:** + +The tone of the meeting was constructive and collaborative, with participants expressing a shared desire to improve the Eliza Repo and address the challenges of its rapid growth. There was a sense of urgency to implement solutions and establish better processes to ensure the project's long-term success. + + +## Quotables + +1. **"We kind of put a hold on merging new plugins and I started going through the repo last night and actually flagging which PRs are just straight up new plugins." (00:01:55)** + * This is a hot take because it signals a significant shift in the project's approach to handling new plugins. It implies that the previous process was not sustainable, and the decision to halt new plugin merges could be controversial among contributors eager to add their work. It raises questions about the criteria for accepting plugins and the long-term vision for plugin integration. + +2. **"Yeah, what I would love if we can manage- it is to get people pushing to develop and not main because main keeps like- we fix main and then it re breaks and this is the same." (00:02:19)** + * This highlights a major pain point in the development process and suggests a stricter control over the `main` branch. This could be controversial as it restricts contributors' ability to directly merge into `main` and might be perceived as a slowdown in development, even though the intention is to increase stability. + +3. **"So one thing we were kind of thinking about is, you know, how we could potentially segment the repository because there's functionally like the core of this repository is agent folder, right?" (00:08:05)** + * This is a hot take as it proposes a fundamental change to the structure of the repository. Segmenting the repository could be a significant undertaking and might face resistance or debate about the best way to implement modularity. It suggests a potential shift from a monolithic architecture to a more modular one, which has implications for development and maintenance. + +4. **"I think we could use some kind of organization, I know Rudolph and other folks are doing, and then the ability to execute against it. I'm not sure if there's any kind of, you know, documents we have any, you know, Google Sheets, you know, any sort of structure so that we can kind of not lose things in the discord." (00:16:55)** + * This take openly criticizes the current state of organization and communication within the project. The call for a more structured approach, moving beyond Discord chats, implies that the current methods are insufficient and potentially hindering progress. This could spark debate about the best tools and processes for project management. + +5. **"If you're doing things that are adding value, I do want to do some stuff with quadratic funding in the future. Because right now, it's just very deterministic, based on objective data." (00:39:45)** + * The introduction of quadratic funding is a hot take, especially within an open-source context. It's a relatively novel approach to funding that can be complex to implement and may be met with skepticism or misunderstanding. The discussion around contributor rewards and avoiding a "mercenary" culture adds another layer of complexity and potential controversy to this topic. It's unclear how this would work, given that quadratic funding typically requires tokens, and they stated they are not creating a token. + +These takes represent key areas of change, potential conflict, and important decisions that will shape the future of the ai16z Eliza project. They often challenge the status quo, propose significant changes, and touch on sensitive topics like code quality, project organization, and contributor compensation. diff --git a/docs/community/Contributors/weekly-contributor-meeting/2024-12-31.md b/docs/community/Contributors/weekly-contributor-meeting/2024-12-31.md new file mode 100644 index 0000000000..df2ce65192 --- /dev/null +++ b/docs/community/Contributors/weekly-contributor-meeting/2024-12-31.md @@ -0,0 +1,66 @@ +--- +title: "Weekly Contributor Meeting Notes" +date: 2024-12-31 +description: "Meeting notes covering ElizaOS's contributor discussions on improving developer onboarding, CI/CD pipelines, funding models, and technical improvements to the core platform." +--- + +# Weekly Contributor Meeting Notes + +(December 31, 2024 4:00 PM PST) + +**Streamlining Contributor Experience and Funding Models** + + +## Summary + +This was a weekly contributor meeting for ElizaOS, an open-source project by ai16z building an AI agent framework. + +**1. Discussion on Improving the Onboarding Experience:** + +* A contributor raised concerns about the default use of local Llama as an inference provider, which caused issues for users without GPUs or sufficient hardware. +* They proposed offering alternative free API providers (like Together, Google AI Studio, and others) with clear documentation to make the initial setup smoother for beginners. +* The idea was to have a dedicated "inference" channel on Discord to list free inference options and pin them for easy access. +* They also discussed the possibility of a user-friendly Electron app for launching agents in the future. + +**2. Priorities and Challenges in the Codebase:** + +* **CI/CD Pipeline:** The current CI/CD process was identified as a major bottleneck. Merging PRs was slow and complex, requiring multiple maintainers to focus on a single PR at a time. They needed a way to run tests without pulling the entire repo for every commit and to ensure that passing tests wouldn't break the `develop` branch. +* **Developer Experience (DevEx) vs. New Features:** There was a discussion about the need to prioritize DevEx improvements and bug fixes over adding new features. Many contributors were focused on building new plugins rather than improving the core codebase. +* **Incentivizing Contributions:** The group discussed the need for better incentives to encourage contributions to DevEx and core improvements. Ideas included bounties, micro-bounties, and clearer communication about priorities through issue tagging. +* **Financials and Funding:** The lack of a clear process for allocating funds and paying contributors was a significant blocker. They needed a system for tracking contributions and distributing rewards, possibly through a retroactive public goods funding (RetroPGF) model. + +**3. Deep Dive into Funding and RetroPGF:** + +* The main obstacle to implementing RetroPGF was the lack of a system to map Discord users to wallet addresses and GitHub accounts. +* A contributor volunteered to work on this issue full-time, as it was crucial for both rewarding past contributions and establishing a framework for future funding. +* They discussed using account abstraction (smart accounts) to create wallets based on social logins (GitHub or Google), potentially simplifying the process. +* The tip bot was also considered as a possible solution for distributing rewards. + +**4. Technical Discussion on Embeddings and RAG:** + +* A contributor asked about improving the RAG (Retrieval-Augmented Generation) pipeline, inspired by the performance of AIXBT on Twitter. +* They discussed the location of embedding-related code in the Eliza codebase (searching for `get_remote_embeddings`) and the issue of zero vectors being used in place of actual data. +* There was a brief discussion about how to feed external data (PDFs, Markdown files) into Eliza for context, with a mention of the `knowledge` field in character files and the limitations of the current room-based scoping of knowledge. + +**5. Action Items and Next Steps:** + +* **Prioritize Issues:** Create and categorize issues into DevEx, product stability, and feature requests to guide contributions. +* **Improve CI/CD:** Investigate ways to streamline the CI/CD pipeline and automate testing. +* **Funding and Wallet Mapping:** Work on mapping Discord/GitHub accounts to wallets to enable RetroPGF and other funding mechanisms. +* **Badges:** Explore the possibility of creating custom GitHub badges to incentivize contributions. +* **Investigate Embeddings:** Research and address the zero vector issue and improve the RAG pipeline. +* **Shaw's Conversation on X:** Gather information about Shaw's conversation on X regarding bot monetization and platform guidelines. + +**6. Concerns about Bot Behavior and Monetization:** + +* The group discussed Shaw's concerns about agents being used as "reply guys" on Twitter and the need to be good stewards of the platform. +* They acknowledged that the current Twitter client implementation might violate Twitter's terms of service and discussed the importance of educating users about the risks. +* There was a consensus that monetizing bots could negatively impact the X platform and that this should be avoided. + +**7. Closing Remarks:** + +* Participants emphasized the value of these meetings for new developers and the importance of clear communication and documentation. +* They reiterated the need to focus on core improvements and DevEx, in addition to building new features. +* The meeting concluded with a sense of optimism and a commitment to addressing the identified challenges. + +In essence, the meeting was a productive discussion about the challenges and opportunities facing the ElizaOS project. The participants identified key areas for improvement, including the onboarding experience, CI/CD pipeline, funding mechanisms, and the need to balance new feature development with core improvements. They also discussed the ethical implications of AI agents on social media platforms and the importance of responsible development. diff --git a/docs/community/Contributors/weekly-contributor-meeting/2025-01-07.md b/docs/community/Contributors/weekly-contributor-meeting/2025-01-07.md new file mode 100644 index 0000000000..82fee3c7d9 --- /dev/null +++ b/docs/community/Contributors/weekly-contributor-meeting/2025-01-07.md @@ -0,0 +1,72 @@ +--- +title: "Weekly Contributor Meeting Notes" +date: 2025-01-07 +description: "Contributor discussion on managing PR bottlenecks, transitioning to ElizaOS Discord, and implementing agent automation strategies for improved workflow efficiency." +--- + +# Weekly Contributor Meeting Notes + +(January 7, 2025 4:00 PM PST) + +**PR Bottleneck & Agent Strategy** + + +## Summary + +This was a weekly contributor meeting for the ai16z Discord, primarily focused on managing pull requests (PRs) and improving the development workflow for the Eliza project. + +**Project Updates and Transitions:** + +* The Eliza project is transitioning to a new Discord server, ElizaOS. Some channels in the old server will be shut down or archived. + +**Challenges with Pull Requests:** + +* The main issue discussed was the high volume of incoming PRs and the difficulty in merging them due to merge conflicts and the inability to directly modify PRs from forks. +* Contributors are using a tool called `PRM` to help manage PRs, but its effectiveness and whether it's redundant with existing Git functionality is unclear. +* The team is exploring the use of a "merge queue" to streamline the merging process and automatically notify contributors of conflicts. +* Shopify's open-source tool "Ship It" was mentioned as a potential solution for mass merging, as they use it to merge hundreds of PRs daily. + +**Discussion on Agents and Automation:** + +* There are currently two agents working on the repo, but they are manually controlled and not yet fully autonomous. +* A contributor, AIFlow, described a multi-agent system they built to analyze issues and provide reports. This system is open source but needs cleanup before being shared. +* The discussion touched on the need for a style guide to improve code consistency and reduce agent hallucinations. +* Using tools like Code Rabbit or a PR Agent to automate PR review and tagging was proposed to help manage the workload. +* Concerns were raised about the potential for security issues with the increasing number of plugins and the lack of proper logging and error handling in some code. + +**Other Topics:** + +* The need for a standardized way to test plugins against the runtime was discussed. +* CJ mentioned a complex PR they were working on related to dynamic imports and secret management. They proposed splitting it into smaller PRs for easier review. +* A contributor brought up the idea of adding a linter to the release pipeline to enforce code style, but it was clarified that this hadn't been a major focus before. +* The need for better documentation and onboarding for new contributors was also highlighted. + +**Action Items:** + +* Investigate `PRM` and merge queue solutions. +* Explore ways to automate test runs and handle secrets during testing. +* Surface easy merge conflicts and work on editing outside branches. +* Consider adding a linter to the release pipeline. +* Continue working on smaller, more manageable PRs. + +**Overall Tone:** + +The meeting was collaborative and focused on problem-solving. Contributors were actively engaged in discussing the challenges and proposing solutions. There was a sense of urgency to improve the workflow and manage the growing complexity of the project. + + +## Quotables + +- **Merging PRs is prioritized over fixing existing bugs** + > "What we need, at least by my estimation is yes, we do need bugs fixed and issues resolved, but we need, we don't want to be adding features right now. Cause we have a zillion people trying to do that for us. What we need to be able to do is effectively review the PRs and get them merged and then also have the appropriate issue assigned to the appropriate PR." + +- **Agents should be proactive in identifying and fixing issues** + > "But with all these agents we have, we should be proactive. So we should run a batch on everything we develop." + +- **The influx of new contributors is creating more work than it solves** + > "So other people on this problem are going to give us more work. So in a moment like this better not add too much issues for request of new features." + +- **The current testing process is inadequate and needs to be improved** + > "It would be amazing to have something that tests everything against the runtime without us to go there and install and do." + +- **A style guide is necessary to manage the growing complexity of the codebase** + > "And in order to build a proper style guide for a repo that will have agents all over it, I think it is prudent to, or at least to me, it seems prudent to take a productized agent that is specifically built to solve the problem we have and then run that and see what breaks so that we are ready for once the other agents or we know what we need to build to help us go faster." diff --git a/docs/docs/advanced/eliza-in-tee.md b/docs/docs/advanced/eliza-in-tee.md index 76d2e9e285..56eaf9012b 100644 --- a/docs/docs/advanced/eliza-in-tee.md +++ b/docs/docs/advanced/eliza-in-tee.md @@ -57,16 +57,22 @@ Example usage: const provider = new DeriveKeyProvider(teeMode); // For Solana const { keypair, attestation } = await provider.deriveEd25519Keypair( - "/", secretSalt, + "solana", agentId, ); // For EVM const { keypair, attestation } = await provider.deriveEcdsaKeypair( - "/", secretSalt, + "evm", agentId, ); + +// For raw key derivation +const rawKey = await provider.deriveRawKey( + secretSalt, + "raw", +); ``` --- @@ -161,7 +167,7 @@ To set up your environment for TEE development: 4. **Verify TEE Attestation** - You can verify the TEE attestation quote by going to the [TEE RA Explorer](https://ra-quote-explorer.vercel.app/) and pasting the attestation quote from the agent logs. Here's an example of interacting with the Eliza agent to ask for the agent's wallet address: + You can verify the TEE attestation quote by going to the [TEE RA Explorer](https://proof.t16z.com/) and pasting the attestation quote from the agent logs. Here's an example of interacting with the Eliza agent to ask for the agent's wallet address: ```bash You: what's your wallet address? @@ -227,7 +233,7 @@ Now we are ready to deploy the Eliza agent to a real TEE environment. ### Run an Eliza Agent in a Real TEE Environment -Before deploying the Eliza agent to a real TEE environment, you need to create a new TEE account on the [TEE Cloud](https://teehouse.vercel.app). Reach out to Phala Network on [Discord](https://discord.gg/phalanetwork) if you need help. +Before deploying the Eliza agent to a real TEE environment, you need to create a new TEE account on the [TEE Cloud](https://cloud.phala.network/login). Reach out to Phala Network on [Discord](https://discord.gg/phalanetwork) if you need help. Next, you will need to take the docker-compose.yaml file in the root folder of the project and edit it based on your agent configuration. @@ -285,7 +291,7 @@ volumes: tee: ``` -Now you can deploy the Eliza agent to a real TEE environment. Go to the [TEE Cloud](https://teehouse.vercel.app) and click on the `Create VM` button to configure your Eliza agent deployment. +Now you can deploy the Eliza agent to a real TEE environment. Go to the [TEE Cloud](https://cloud.phala.network/login) and click on the `Create VM` button to configure your Eliza agent deployment. Click on the `Compose Manifest Mode` tab and paste the docker-compose.yaml file content into the `Compose Manifest` field. @@ -313,7 +319,7 @@ Click on the `Logs` tab to view the agent logs. ![Agent Logs](https://i.imgur.com/aU3i0Dv.png) -Now we can verify the REAL TEE attestation quote by going to the [TEE RA Explorer](https://ra-quote-explorer.vercel.app/) and pasting the attestation quote from the agent logs. +Now we can verify the REAL TEE attestation quote by going to the [TEE RA Explorer](https://proof.t16z.com/) and pasting the attestation quote from the agent logs. ![TEE RA Explorer](https://i.imgur.com/TJ5299l.png) diff --git a/docs/docs/packages/plugins.md b/docs/docs/packages/plugins.md index c1142c79ec..18bafb92fe 100644 --- a/docs/docs/packages/plugins.md +++ b/docs/docs/packages/plugins.md @@ -469,7 +469,7 @@ const provider = new DeriveKeyProvider(); // Derive a raw key try { const rawKey = await provider.rawDeriveKey( - "/path/to/derive", + "/path/to/derive", // This is what the WALLET_SECRET_SALT is used for "subject-identifier", ); // rawKey is a DeriveKeyResponse that can be used for further processing @@ -482,7 +482,7 @@ try { // Derive a Solana keypair (Ed25519) try { const solanaKeypair = await provider.deriveEd25519Keypair( - "/path/to/derive", + "/path/to/derive", // This is what the WALLET_SECRET_SALT is used for "subject-identifier", ); // solanaKeypair can now be used for Solana operations @@ -493,7 +493,7 @@ try { // Derive an Ethereum keypair (ECDSA) try { const evmKeypair = await provider.deriveEcdsaKeypair( - "/path/to/derive", + "/path/to/derive", // This is what the WALLET_SECRET_SALT is used for "subject-identifier", ); // evmKeypair can now be used for Ethereum operations diff --git a/packages/_examples/plugin/src/plugins/samplePlugin.ts b/packages/_examples/plugin/src/plugins/samplePlugin.ts index 90fd2898a1..2a3b29888b 100644 --- a/packages/_examples/plugin/src/plugins/samplePlugin.ts +++ b/packages/_examples/plugin/src/plugins/samplePlugin.ts @@ -2,6 +2,7 @@ import { Plugin } from "@elizaos/core"; import { createResourceAction } from "../actions/sampleAction"; import { sampleProvider } from "../providers/sampleProvider"; import { sampleEvaluator } from "../evaluators/sampleEvalutor"; +import SampleService from "../services/sampleService"; export const samplePlugin: Plugin = { name: "sample", @@ -10,6 +11,6 @@ export const samplePlugin: Plugin = { providers: [sampleProvider], evaluators: [sampleEvaluator], // separate examples will be added for services and clients - services: [], + services: [new SampleService()], clients: [], }; diff --git a/packages/_examples/plugin/src/services/sampleService.ts b/packages/_examples/plugin/src/services/sampleService.ts new file mode 100644 index 0000000000..07561c5bde --- /dev/null +++ b/packages/_examples/plugin/src/services/sampleService.ts @@ -0,0 +1,136 @@ +import { + Service, + ServiceType, + IAgentRuntime, + // Memory, + // State, + elizaLogger, + // stringToUuid, +} from "@elizaos/core"; +// import { sampleProvider } from "../providers/sampleProvider"; // TODO: Uncomment this line to use the sampleProvider + +// Add SAMPLE to ServiceType enum in types.ts +declare module "@elizaos/core" { + export enum ServiceType { + SAMPLE = "sample", + } +} + +// The SampleService is a simple service that logs "Hello world" every 15 minutes. +export class SampleService extends Service { + private runtime: IAgentRuntime | null = null; + private intervalId: NodeJS.Timeout | null = null; + private readonly DEFAULT_INTERVAL = 15 * 60 * 1000; // 15 minutes in milliseconds + + static get serviceType(): ServiceType { + return ServiceType.SAMPLE; + } + + private static isInitialized = false; + + async initialize(runtime: IAgentRuntime): Promise { + // Verify if the service is already initialized + if (SampleService.isInitialized) { + return; + } + + this.runtime = runtime; + + // Start the periodic task + this.startPeriodicTask(); + SampleService.isInitialized = true; + elizaLogger.info("SampleService initialized and started periodic task"); + } + + private static activeTaskCount = 0; + + private startPeriodicTask(): void { + // Verify if a task is already active + if (SampleService.activeTaskCount > 0) { + elizaLogger.warn( + "SampleService: Periodic task already running, skipping" + ); + return; + } + + // Clear any existing interval + if (this.intervalId) { + clearInterval(this.intervalId); + } + + SampleService.activeTaskCount++; + elizaLogger.info( + `SampleService: Starting periodic task (active tasks: ${SampleService.activeTaskCount})` + ); + + // Initial call immediately + this.fetchSample(); + + // Set up periodic calls + this.intervalId = setInterval(() => { + this.fetchSample(); + }, this.DEFAULT_INTERVAL); + } + + private async fetchSample(): Promise { + if (!this.runtime) { + elizaLogger.error("SampleService: Runtime not initialized"); + return; + } + + try { + // Example of using the sampleProvider + // Create dummy memory and state objects for the provider + // const dummyMemory: Memory = { + // id: stringToUuid("sample-service-trigger"), + // userId: this.runtime.agentId, + // agentId: this.runtime.agentId, + // roomId: this.runtime.agentId, + // content: { text: "Periodic sample fetch" }, + // createdAt: Date.now(), + // }; + + // const dummyState: State = { + // userId: this.runtime.agentId, + // bio: "", + // lore: "", + // messageDirections: "", + // postDirections: "", + // roomId: this.runtime.agentId, + // actors: "", + // recentMessages: "", + // recentMessagesData: [], + // }; + // await sampleProvider.get(this.runtime, dummyMemory, dummyState); + + // hello world log example + elizaLogger.info("SampleService: Hello world"); + + elizaLogger.info( + "SampleService: Successfully fetched and processed sample" + ); + } catch (error) { + elizaLogger.error("SampleService: Error fetching sample:", error); + } + } + + // Method to stop the service + stop(): void { + if (this.intervalId) { + clearInterval(this.intervalId); + this.intervalId = null; + SampleService.activeTaskCount--; + elizaLogger.info( + `SampleService stopped (active tasks: ${SampleService.activeTaskCount})` + ); + } + SampleService.isInitialized = false; + } + + // Method to manually trigger a sample fetch (for testing) + async forceFetch(): Promise { + await this.fetchSample(); + } +} + +export default SampleService; diff --git a/packages/client-direct/src/api.ts b/packages/client-direct/src/api.ts index c19ac5279c..0cecbb4b69 100644 --- a/packages/client-direct/src/api.ts +++ b/packages/client-direct/src/api.ts @@ -103,6 +103,24 @@ export function createApiRouter( }); }); + router.delete("/agents/:agentId", async (req, res) => { + const { agentId } = validateUUIDParams(req.params, res) ?? { + agentId: null, + }; + if (!agentId) return; + + let agent: AgentRuntime = agents.get(agentId); + + if (agent) { + agent.stop(); + directClient.unregisterAgent(agent); + res.status(204).send(); + } + else { + res.status(404).json({ error: "Agent not found" }); + } + }); + router.post("/agents/:agentId/set", async (req, res) => { const { agentId } = validateUUIDParams(req.params, res) ?? { agentId: null, @@ -133,9 +151,17 @@ export function createApiRouter( } // start it up (and register it) - agent = await directClient.startAgent(character); - elizaLogger.log(`${character.name} started`); - + try { + await directClient.startAgent(character); + elizaLogger.log(`${character.name} started`); + } catch (e) { + elizaLogger.error(`Error starting agent: ${e}`); + res.status(500).json({ + success: false, + message: e.message, + }); + return; + } res.json({ id: character.id, character: character, @@ -330,3 +356,4 @@ export function createApiRouter( return router; } + diff --git a/packages/core/src/embedding.ts b/packages/core/src/embedding.ts index da585be230..ce2d00b21b 100644 --- a/packages/core/src/embedding.ts +++ b/packages/core/src/embedding.ts @@ -1,9 +1,7 @@ -import path from "node:path"; +import { getEmbeddingModelSettings, getEndpoint } from "./models.ts"; +import { IAgentRuntime, ModelProviderName } from "./types.ts"; import settings from "./settings.ts"; import elizaLogger from "./logger.ts"; -import { getVoyageAIEmbeddingConfig } from "./voyageai.ts"; -import { models, getEmbeddingModelSettings, getEndpoint } from "./models.ts"; -import { IAgentRuntime, ModelProviderName } from "./types.ts"; import LocalEmbeddingModelManager from "./localembeddingManager.ts"; interface EmbeddingOptions { @@ -22,93 +20,63 @@ export const EmbeddingProvider = { GaiaNet: "GaiaNet", Heurist: "Heurist", BGE: "BGE", - VoyageAI: "VoyageAI", } as const; export type EmbeddingProviderType = (typeof EmbeddingProvider)[keyof typeof EmbeddingProvider]; -export namespace EmbeddingProvider { - export type OpenAI = typeof EmbeddingProvider.OpenAI; - export type Ollama = typeof EmbeddingProvider.Ollama; - export type GaiaNet = typeof EmbeddingProvider.GaiaNet; - export type BGE = typeof EmbeddingProvider.BGE; - export type VoyageAI = typeof EmbeddingProvider.VoyageAI; -} - export type EmbeddingConfig = { readonly dimensions: number; readonly model: string; - readonly provider: EmbeddingProvider; - readonly endpoint?: string; - readonly apiKey?: string; - readonly maxInputTokens?: number; + readonly provider: EmbeddingProviderType; }; -// Get embedding config based on settings -export function getEmbeddingConfig(): EmbeddingConfig { - if (settings.USE_OPENAI_EMBEDDING?.toLowerCase() === "true") { - return { - dimensions: 1536, - model: "text-embedding-3-small", - provider: "OpenAI", - endpoint: "https://api.openai.com/v1", - apiKey: settings.OPENAI_API_KEY, - maxInputTokens: 1000000, - }; - } - if (settings.USE_OLLAMA_EMBEDDING?.toLowerCase() === "true") { - return { - dimensions: 1024, - model: settings.OLLAMA_EMBEDDING_MODEL || "mxbai-embed-large", - provider: "Ollama", - endpoint: "https://ollama.eliza.ai/", - apiKey: settings.OLLAMA_API_KEY, - maxInputTokens: 1000000, - }; - } - if (settings.USE_GAIANET_EMBEDDING?.toLowerCase() === "true") { - return { - dimensions: 768, - model: settings.GAIANET_EMBEDDING_MODEL || "nomic-embed", - provider: "GaiaNet", - endpoint: settings.SMALL_GAIANET_SERVER_URL || settings.MEDIUM_GAIANET_SERVER_URL || settings.LARGE_GAIANET_SERVER_URL, - apiKey: settings.GAIANET_API_KEY, - maxInputTokens: 1000000, - }; - } - if (settings.USE_VOYAGEAI_EMBEDDING?.toLowerCase() === "true") { - return getVoyageAIEmbeddingConfig(); - } - - // Fallback to local BGE - return { - dimensions: 384, - model: "BGE-small-en-v1.5", - provider: "BGE", - maxInputTokens: 1000000, - }; -}; +export const getEmbeddingConfig = (): EmbeddingConfig => ({ + dimensions: + settings.USE_OPENAI_EMBEDDING?.toLowerCase() === "true" + ? getEmbeddingModelSettings(ModelProviderName.OPENAI).dimensions + : settings.USE_OLLAMA_EMBEDDING?.toLowerCase() === "true" + ? getEmbeddingModelSettings(ModelProviderName.OLLAMA).dimensions + : settings.USE_GAIANET_EMBEDDING?.toLowerCase() === "true" + ? getEmbeddingModelSettings(ModelProviderName.GAIANET) + .dimensions + : settings.USE_HEURIST_EMBEDDING?.toLowerCase() === "true" + ? getEmbeddingModelSettings(ModelProviderName.HEURIST) + .dimensions + : 384, // BGE + model: + settings.USE_OPENAI_EMBEDDING?.toLowerCase() === "true" + ? getEmbeddingModelSettings(ModelProviderName.OPENAI).name + : settings.USE_OLLAMA_EMBEDDING?.toLowerCase() === "true" + ? getEmbeddingModelSettings(ModelProviderName.OLLAMA).name + : settings.USE_GAIANET_EMBEDDING?.toLowerCase() === "true" + ? getEmbeddingModelSettings(ModelProviderName.GAIANET).name + : settings.USE_HEURIST_EMBEDDING?.toLowerCase() === "true" + ? getEmbeddingModelSettings(ModelProviderName.HEURIST).name + : "BGE-small-en-v1.5", + provider: + settings.USE_OPENAI_EMBEDDING?.toLowerCase() === "true" + ? "OpenAI" + : settings.USE_OLLAMA_EMBEDDING?.toLowerCase() === "true" + ? "Ollama" + : settings.USE_GAIANET_EMBEDDING?.toLowerCase() === "true" + ? "GaiaNet" + : settings.USE_HEURIST_EMBEDDING?.toLowerCase() === "true" + ? "Heurist" + : "BGE", +}); async function getRemoteEmbedding( input: string, - options: EmbeddingConfig + options: EmbeddingOptions ): Promise { - elizaLogger.debug("Getting remote embedding using provider:", options.provider); + // Ensure endpoint ends with /v1 for OpenAI + const baseEndpoint = options.endpoint.endsWith("/v1") + ? options.endpoint + : `${options.endpoint}${options.isOllama ? "/v1" : ""}`; // Construct full URL - const fullUrl = `${options.endpoint}/embeddings`; - - // jank. voyageai is the only one that doesn't use "dimensions". - const body = options.provider === "VoyageAI" ? { - input, - model: options.model, - output_dimension: options.dimensions, - } : { - input, - model: options.model, - dimensions: options.dimensions, - }; + const fullUrl = `${baseEndpoint}/embeddings`; const requestOptions = { method: "POST", @@ -120,7 +88,14 @@ async function getRemoteEmbedding( } : {}), }, - body: JSON.stringify(body), + body: JSON.stringify({ + input, + model: options.model, + dimensions: + options.dimensions || + options.length || + getEmbeddingConfig().dimensions, // Prefer dimensions, fallback to length + }), }; try { @@ -166,18 +141,44 @@ export function getEmbeddingType(runtime: IAgentRuntime): "local" | "remote" { } export function getEmbeddingZeroVector(): number[] { - // Default BGE dimension is 384 - return Array(getEmbeddingConfig().dimensions).fill(0); + let embeddingDimension = 384; // Default BGE dimension + + if (settings.USE_OPENAI_EMBEDDING?.toLowerCase() === "true") { + embeddingDimension = getEmbeddingModelSettings( + ModelProviderName.OPENAI + ).dimensions; // OpenAI dimension + } else if (settings.USE_OLLAMA_EMBEDDING?.toLowerCase() === "true") { + embeddingDimension = getEmbeddingModelSettings( + ModelProviderName.OLLAMA + ).dimensions; // Ollama mxbai-embed-large dimension + } else if (settings.USE_GAIANET_EMBEDDING?.toLowerCase() === "true") { + embeddingDimension = getEmbeddingModelSettings( + ModelProviderName.GAIANET + ).dimensions; // GaiaNet dimension + } else if (settings.USE_HEURIST_EMBEDDING?.toLowerCase() === "true") { + embeddingDimension = getEmbeddingModelSettings( + ModelProviderName.HEURIST + ).dimensions; // Heurist dimension + } + + return Array(embeddingDimension).fill(0); } /** * Gets embeddings from a remote API endpoint. Falls back to local BGE/384 * * @param {string} input - The text to generate embeddings for + * @param {EmbeddingOptions} options - Configuration options including: + * - model: The model name to use + * - endpoint: Base API endpoint URL + * - apiKey: Optional API key for authentication + * - isOllama: Whether this is an Ollama endpoint + * - dimensions: Desired embedding dimensions * @param {IAgentRuntime} runtime - The agent runtime context * @returns {Promise} Array of embedding values - * @throws {Error} If the API request fails or configuration is invalid + * @throws {Error} If the API request fails */ + export async function embed(runtime: IAgentRuntime, input: string) { elizaLogger.debug("Embedding request:", { modelProvider: runtime.character.modelProvider, @@ -206,11 +207,6 @@ export async function embed(runtime: IAgentRuntime, input: string) { const config = getEmbeddingConfig(); const isNode = typeof process !== "undefined" && process.versions?.node; - // Attempt remote embedding if it is configured. - if (config.provider !== EmbeddingProvider.BGE) { - return await getRemoteEmbedding(input, config); - } - // Determine which embedding path to use if (config.provider === EmbeddingProvider.OpenAI) { return await getRemoteEmbedding(input, { @@ -275,7 +271,6 @@ export async function embed(runtime: IAgentRuntime, input: string) { getEndpoint(runtime.character.modelProvider), apiKey: runtime.token, dimensions: config.dimensions, - provider: config.provider, }); async function getLocalEmbedding(input: string): Promise { diff --git a/packages/core/src/tests/embeddings.test.ts b/packages/core/src/tests/embeddings.test.ts deleted file mode 100644 index c3f37f961e..0000000000 --- a/packages/core/src/tests/embeddings.test.ts +++ /dev/null @@ -1,102 +0,0 @@ - -import { describe, expect, vi } from "vitest"; -import { getEmbeddingConfig } from '../embedding'; -import settings from '../settings'; - -vi.mock("../settings"); -const mockedSettings = vi.mocked(settings); - -describe('getEmbeddingConfig', () => { - beforeEach(() => { - // Clear the specific mock - Object.keys(mockedSettings).forEach(key => { - delete mockedSettings[key]; - }); - - vi.clearAllMocks(); - }); - - afterEach(() => { - vi.clearAllMocks(); - }); - - it('should return BGE config by default', () => { - - mockedSettings.USE_OPENAI_EMBEDDING = 'false'; - mockedSettings.USE_OLLAMA_EMBEDDING = 'false'; - mockedSettings.USE_GAIANET_EMBEDDING = 'false'; - mockedSettings.USE_VOYAGEAI_EMBEDDING = 'false'; - - const config = getEmbeddingConfig(); - expect(config).toEqual({ - dimensions: 384, - model: 'BGE-small-en-v1.5', - provider: 'BGE', - maxInputTokens: 1000000, - }); - }); - - it('should return GaiaNet config when USE_GAIANET_EMBEDDING is true', () => { - mockedSettings.USE_GAIANET_EMBEDDING = 'true'; - mockedSettings.GAIANET_EMBEDDING_MODEL = 'test-model'; - mockedSettings.GAIANET_API_KEY = 'test-key'; - mockedSettings.SMALL_GAIANET_SERVER_URL = 'https://test.gaianet.ai'; - - const config = getEmbeddingConfig(); - expect(config).toEqual({ - dimensions: 768, - model: 'test-model', - provider: 'GaiaNet', - endpoint: 'https://test.gaianet.ai', - apiKey: 'test-key', - maxInputTokens: 1000000, - }); - }); - - - it('should return VoyageAI config when USE_VOYAGEAI_EMBEDDING is true', () => { - mockedSettings.USE_VOYAGEAI_EMBEDDING = 'true'; - mockedSettings.VOYAGEAI_API_KEY = 'test-key'; - - const config = getEmbeddingConfig(); - expect(config).toEqual({ - dimensions: 512, - model: 'voyage-3-lite', - provider: 'VoyageAI', - endpoint: 'https://api.voyageai.com/v1', - apiKey: 'test-key', - maxInputTokens: 1000000, - }); - }); - - it('should return OpenAI config when USE_OPENAI_EMBEDDING is true', () => { - mockedSettings.USE_OPENAI_EMBEDDING = 'true'; - mockedSettings.OPENAI_API_KEY = 'test-key'; - - const config = getEmbeddingConfig(); - expect(config).toEqual({ - dimensions: 1536, - model: 'text-embedding-3-small', - provider: 'OpenAI', - endpoint: 'https://api.openai.com/v1', - apiKey: 'test-key', - maxInputTokens: 1000000, - }); - }); - - it('should return Ollama config when USE_OLLAMA_EMBEDDING is true', () => { - mockedSettings.USE_OLLAMA_EMBEDDING = 'true'; - mockedSettings.OLLAMA_EMBEDDING_MODEL = 'test-model'; - mockedSettings.OLLAMA_API_KEY = 'test-key'; - - const config = getEmbeddingConfig(); - expect(config).toEqual({ - dimensions: 1024, - model: 'test-model', - provider: 'Ollama', - endpoint: 'https://ollama.eliza.ai/v1', - apiKey: 'test-key', - maxInputTokens: 1000000, - }); - }); -}); \ No newline at end of file diff --git a/packages/core/src/voyageai.ts b/packages/core/src/voyageai.ts deleted file mode 100644 index a603afa954..0000000000 --- a/packages/core/src/voyageai.ts +++ /dev/null @@ -1,156 +0,0 @@ -import settings from "./settings.ts"; -import { EmbeddingConfig } from "./embedding.ts"; - -enum VoyageAIModel { - // Current models - VOYAGE_3_LARGE = 'voyage-3-large', - VOYAGE_3 = 'voyage-3', - VOYAGE_3_LITE = 'voyage-3-lite', - VOYAGE_CODE_3 = 'voyage-code-3', - VOYAGE_FINANCE_2 = 'voyage-finance-2', - VOYAGE_LAW_2 = 'voyage-law-2', - VOYAGE_CODE_2 = 'voyage-code-2', - // Legacy models - VOYAGE_MULTILINGUAL_2 = 'voyage-multilingual-2', - VOYAGE_LARGE_2_INSTRUCT = 'voyage-large-2-instruct', - VOYAGE_LARGE_2 = 'voyage-large-2', - VOYAGE_2 = 'voyage-2', - VOYAGE_LITE_02_INSTRUCT = 'voyage-lite-02-instruct', - VOYAGE_02 = 'voyage-02', - VOYAGE_01 = 'voyage-01', - VOYAGE_LITE_01 = 'voyage-lite-01', - VOYAGE_LITE_01_INSTRUCT = 'voyage-lite-01-instruct', -} - -/** - * Gets the VoyageAI embedding model to use based on settings. - * - * Checks if VOYAGEAI_EMBEDDING_MODEL is set in settings and validates that it's - * a valid model name from the VoyageraiModel enum. If no model is configured, - * defaults to VOYAGE_3_LITE. - * - * @returns {VoyageAIModel} The VoyageAI model to use for embeddings - * @throws {Error} If an invalid model name is configured in settings - */ -function getVoyageAIEmbeddingModel(): VoyageAIModel { - const modelName = settings.VOYAGEAI_EMBEDDING_MODEL ?? VoyageAIModel.VOYAGE_3_LITE; - - try { - return modelName as VoyageAIModel; - } catch { - throw new Error(`Invalid voyageai embedding model: ${modelName}`); - } -} - -/** - * Gets the embedding dimensions for the configured VoyageAI model. - * - * Each model supports specific dimension options. If VOYAGEAI_EMBEDDING_DIMENSIONS - * is set in settings, validates that it's a valid option for the model. - * Otherwise returns the default dimensions for that model. - * - * Dimensions by model: - * - VOYAGE_3_LARGE: 256, 512, 1024 (default), 2048 - * - VOYAGE_3: 1024 only - * - VOYAGE_3_LITE: 512 only - * - VOYAGE_CODE_3: 256, 512, 1024 (default), 2048 - * - VOYAGE_FINANCE_2/LAW_2: 1024 only - * - VOYAGE_CODE_2/LARGE_2: 1536 only - * - All legacy models: 1024 only - * - * @returns {number} The embedding dimensions to use - * @throws {Error} If an invalid dimension is configured for the model - * @see {@link getVoyageAIEmbeddingModel} - */ -function getVoyageAIEmbeddingDimensions(): number { - const model = getVoyageAIEmbeddingModel(); - - function validateDimensions(model: VoyageAIModel, defaultDimensions: number, validOptions: number[]) { - if (settings.VOYAGEAI_EMBEDDING_DIMENSIONS != null) { - const dim = Number(settings.VOYAGEAI_EMBEDDING_DIMENSIONS); - if (!validOptions.includes(dim)) { - throw new Error(`Invalid dimensions for ${model}: ${dim}. Valid options are: ${validOptions.join(', ')}`); - } - return dim; - } - return defaultDimensions; - } - - switch (model) { - // Current models - case VoyageAIModel.VOYAGE_3_LARGE: - return validateDimensions(model, 1024, [256, 512, 1024, 2048]); - - case VoyageAIModel.VOYAGE_3: - return validateDimensions(model, 1024, [1024]); - - case VoyageAIModel.VOYAGE_3_LITE: - return validateDimensions(model, 512, [512]); - - case VoyageAIModel.VOYAGE_CODE_3: - return validateDimensions(model, 1024, [256, 512, 1024, 2048]); - - case VoyageAIModel.VOYAGE_FINANCE_2: - case VoyageAIModel.VOYAGE_LAW_2: - return validateDimensions(model, 1024, [1024]); - - case VoyageAIModel.VOYAGE_CODE_2: - case VoyageAIModel.VOYAGE_LARGE_2: - return validateDimensions(model, 1536, [1536]); - - // Legacy models - case VoyageAIModel.VOYAGE_MULTILINGUAL_2: - case VoyageAIModel.VOYAGE_LARGE_2_INSTRUCT: - case VoyageAIModel.VOYAGE_2: - case VoyageAIModel.VOYAGE_LITE_02_INSTRUCT: - case VoyageAIModel.VOYAGE_02: - case VoyageAIModel.VOYAGE_01: - case VoyageAIModel.VOYAGE_LITE_01: - case VoyageAIModel.VOYAGE_LITE_01_INSTRUCT: - return validateDimensions(model, 1024, [1024]); - } - - // Should be unreachable. - throw new Error(`Invalid voyageai embedding model: ${model}`); -} - -/** - * Gets the maximum number of input tokens allowed for the current VoyageAI embedding model - * - * Different VoyageAI models have different token limits: - * - VOYAGE_3_LITE: 1,000,000 tokens - * - VOYAGE_3/VOYAGE_2: 320,000 tokens - * - Other models: 120,000 tokens - * - * @returns {number} The maximum number of input tokens allowed for the current model - */ -function getVoyageAIMaxInputTokens() { - switch (getVoyageAIEmbeddingModel()) { - case VoyageAIModel.VOYAGE_3_LITE: - return 1000000; - case VoyageAIModel.VOYAGE_3: - case VoyageAIModel.VOYAGE_2: - return 320000; - case VoyageAIModel.VOYAGE_3_LARGE: - case VoyageAIModel.VOYAGE_CODE_3: - case VoyageAIModel.VOYAGE_LARGE_2_INSTRUCT: - case VoyageAIModel.VOYAGE_FINANCE_2: - case VoyageAIModel.VOYAGE_MULTILINGUAL_2: - case VoyageAIModel.VOYAGE_LAW_2: - case VoyageAIModel.VOYAGE_LARGE_2: - return 120000; - default: - return 120000; // Default to most conservative limit - } -} - -export function getVoyageAIEmbeddingConfig(): EmbeddingConfig { - return { - dimensions: getVoyageAIEmbeddingDimensions(), - model: getVoyageAIEmbeddingModel(), - provider: "VoyageAI", - maxInputTokens: getVoyageAIMaxInputTokens(), - endpoint: "https://api.voyageai.com/v1", - apiKey: settings.VOYAGEAI_API_KEY, - }; -} diff --git a/packages/plugin-b2/src/index.ts b/packages/plugin-b2/src/index.ts index 7cc4d86f4d..54c1a188b4 100644 --- a/packages/plugin-b2/src/index.ts +++ b/packages/plugin-b2/src/index.ts @@ -5,7 +5,7 @@ import { unstakeAction } from "./actions/unstake"; import { withdrawAction } from "./actions/withdraw"; import { walletProvider } from "./providers"; -const b2Plugin: Plugin = { +export const b2Plugin: Plugin = { name: "b2", description: "B2 Network Plugin for Eliza", actions: [transferAction, stakeAction, unstakeAction, withdrawAction], diff --git a/packages/plugin-evm/src/providers/wallet.ts b/packages/plugin-evm/src/providers/wallet.ts index 50cc2aaabb..90a3b89b5b 100644 --- a/packages/plugin-evm/src/providers/wallet.ts +++ b/packages/plugin-evm/src/providers/wallet.ts @@ -306,8 +306,8 @@ export const initWalletProvider = async (runtime: IAgentRuntime) => { const deriveKeyProvider = new DeriveKeyProvider(teeMode); const deriveKeyResult = await deriveKeyProvider.deriveEcdsaKeypair( - "/", walletSecretSalt, + "evm", runtime.agentId ); return new WalletProvider( diff --git a/packages/plugin-gitcoin-passport/README.md b/packages/plugin-gitcoin-passport/README.md new file mode 100644 index 0000000000..80de1caedf --- /dev/null +++ b/packages/plugin-gitcoin-passport/README.md @@ -0,0 +1,38 @@ +# `@elizaos/plugin-passport` + +This plugin provides actions for interacting with Gitcoin passport +https://docs.passport.xyz/building-with-passport/passport-api/overview + +--- + +## Installation + +Just add it under your character profile in plugins as + +``` + "plugins": [ + "@elizaos/plugin-gitcoin-passport" + ], +``` + +## Configuration + +Getting Your API Key + +1. Log in to the developer portal: Go to developer.passport.xyz and log in to your account by connecting your wallet. +2. Navigate to the API Keys section: After logging in, go to the "API Keys" section. +3. Generate an API key: Click on the "+ Create a Key" button to generate a unique API key for your account. +4. Store your API key securely: Store your API key in a secure place, as it will be used to access the Passport API. + +Getting your Scorer ID + +1. Log in to the Developer Portal: Go to developer.passport.xyz and log in to your account by connecting your wallet. +2. Navigate to the Scorer section: After logging in, go to the "Scorer" section +3. Create a Scorer: Click on the "+ Create a Scorer" button and input a Scorer name and description. Make sure you use the Unique Humanity scorer, and not the Binary scorer. +4. Find your Scorer ID: Click on the newly created Scorer and you will see the Scorer ID in the page URL. + Example: https://developer.passport.xyz/dashboard/scorer/{scorer_id} + +## Usage + +Results are saved as message and agents can retrive it from there for different use cases. +Default passport treshold of 20 is used, but you can pick your own value and match it agains that diff --git a/packages/plugin-gitcoin-passport/eslint.config.mjs b/packages/plugin-gitcoin-passport/eslint.config.mjs new file mode 100644 index 0000000000..92fe5bbebe --- /dev/null +++ b/packages/plugin-gitcoin-passport/eslint.config.mjs @@ -0,0 +1,3 @@ +import eslintGlobalConfig from "../../eslint.config.mjs"; + +export default [...eslintGlobalConfig]; diff --git a/packages/plugin-gitcoin-passport/package.json b/packages/plugin-gitcoin-passport/package.json new file mode 100644 index 0000000000..7e6b183fd1 --- /dev/null +++ b/packages/plugin-gitcoin-passport/package.json @@ -0,0 +1,20 @@ +{ + "name": "@elizaos/plugin-gitcoin-passport", + "version": "0.1.7-alpha.2", + "main": "dist/index.js", + "type": "module", + "types": "dist/index.d.ts", + "dependencies": { + "@elizaos/core": "workspace:*", + "tsup": "8.3.5" + }, + "scripts": { + "build": "tsup --format esm --dts", + "dev": "tsup --format esm --dts --watch", + "test": "vitest run", + "lint": "eslint --fix --cache ." + }, + "peerDependencies": { + "whatwg-url": "7.1.0" + } +} \ No newline at end of file diff --git a/packages/plugin-gitcoin-passport/src/actions/getScore.ts b/packages/plugin-gitcoin-passport/src/actions/getScore.ts new file mode 100644 index 0000000000..df9bfff8ab --- /dev/null +++ b/packages/plugin-gitcoin-passport/src/actions/getScore.ts @@ -0,0 +1,137 @@ +import { + Action, + elizaLogger, + IAgentRuntime, + Memory, + HandlerCallback, + State, + getEmbeddingZeroVector, + Content, + composeContext, + generateMessageResponse, + ModelClass, +} from "@elizaos/core"; + +interface PassportScore { + address: string; + score: string; + threshold: string; + passing_score: string; +} + +const createTokenMemory = async ( + runtime: IAgentRuntime, + _message: Memory, + formattedOutput: string +) => { + const memory: Memory = { + userId: _message.userId, + agentId: _message.agentId, + roomId: _message.roomId, + content: { text: formattedOutput }, + createdAt: Date.now(), + embedding: getEmbeddingZeroVector(), + }; + await runtime.messageManager.createMemory(memory); +}; + +export const addressTemplate = `From previous sentence extract only the Ethereum address being asked about. +Respond with a JSON markdown block containing only the extracted value: + +\`\`\`json +{ +"address": string | null +} +\`\`\` +`; + +export const getPassportScoreAction: Action = { + name: "GET_PASSPORT_SCORE", + description: "Get score from Passport API for an address", + validate: async (runtime: IAgentRuntime, _message: Memory) => { + elizaLogger.log("Validating runtime for GET_PASSPORT_SCORE..."); + const apiKey = runtime.getSetting("PASSPORT_API_KEY"); + const scorerId = runtime.getSetting("PASSPORT_SCORER"); + if (!apiKey || !scorerId) { + elizaLogger.error( + "Missing PASSPORT_API_KEY or PASSPORT_SCORER settings" + ); + return false; + } + return true; + }, + handler: async ( + runtime: IAgentRuntime, + _message: Memory, + state: State, + _options: any, + callback: HandlerCallback + ) => { + elizaLogger.log("Starting GET_PASSPORT_SCORE handler..."); + const apiKey = runtime.getSetting("PASSPORT_API_KEY"); + const scorerId = runtime.getSetting("PASSPORT_SCORER"); + + if (!state) { + state = (await runtime.composeState(_message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + const context = composeContext({ + state, + template: `${_message.content.text}\n${addressTemplate}`, + }); + + const addressRequest = await generateMessageResponse({ + runtime, + context, + modelClass: ModelClass.SMALL, + }); + + const address = addressRequest.address as string; + + if (!address) { + callback({ text: "Address is required." }, []); + return; + } + + try { + const response = await fetch( + `https://api.passport.xyz/v2/stamps/${scorerId}/score/${address}`, + { + method: "GET", + headers: { + "X-API-KEY": apiKey, + accept: "application/json", + }, + } + ); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data: PassportScore = await response.json(); + const formattedOutput = `Address: ${data.address}\nScore: ${data.score}${data.passing_score ? "\nScore is above threshold" : `\nScore is below threshold (${data.threshold})`}`; + + await createTokenMemory(runtime, _message, formattedOutput); + + callback({ text: formattedOutput }, []); + } catch (error) { + elizaLogger.error("Error fetching Passport score:", error); + callback( + { + text: "Failed to fetch Passport score. Please check the logs for more details.", + }, + [] + ); + } + }, + examples: [], + similes: [ + "GET_PASSPORT_SCORE", + "FETCH_PASSPORT_SCORE", + "CHECK_PASSPORT_SCORE", + "VIEW_PASSPORT_SCORE", + ], +}; diff --git a/packages/plugin-gitcoin-passport/src/index.ts b/packages/plugin-gitcoin-passport/src/index.ts new file mode 100644 index 0000000000..638affd8c1 --- /dev/null +++ b/packages/plugin-gitcoin-passport/src/index.ts @@ -0,0 +1,15 @@ +export * from "./actions/getScore"; + +import type { Plugin } from "@elizaos/core"; +import { getPassportScoreAction } from "./actions/getScore"; + +export const gitcoinPassportPlugin: Plugin = { + name: "passport", + description: "Gitcoin passport integration plugin", + providers: [], + evaluators: [], + services: [], + actions: [getPassportScoreAction], +}; + +export default gitcoinPassportPlugin; diff --git a/packages/plugin-gitcoin-passport/tsconfig.json b/packages/plugin-gitcoin-passport/tsconfig.json new file mode 100644 index 0000000000..2d8d3fe818 --- /dev/null +++ b/packages/plugin-gitcoin-passport/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../core/tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "./src", + "typeRoots": [ + "./node_modules/@types", + "./src/types" + ], + "declaration": true + }, + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/packages/plugin-gitcoin-passport/tsup.config.ts b/packages/plugin-gitcoin-passport/tsup.config.ts new file mode 100644 index 0000000000..a68ccd636a --- /dev/null +++ b/packages/plugin-gitcoin-passport/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", + "viem", + "@lifi/sdk", + ], +}); diff --git a/packages/plugin-solana/src/keypairUtils.ts b/packages/plugin-solana/src/keypairUtils.ts index 6dbacdb841..f8405424bc 100644 --- a/packages/plugin-solana/src/keypairUtils.ts +++ b/packages/plugin-solana/src/keypairUtils.ts @@ -30,8 +30,8 @@ export async function getWalletKey( const deriveKeyProvider = new DeriveKeyProvider(teeMode); const deriveKeyResult = await deriveKeyProvider.deriveEd25519Keypair( - "/", walletSecretSalt, + "solana", runtime.agentId ); diff --git a/packages/plugin-tee/package.json b/packages/plugin-tee/package.json index 58e2d94b7d..852c29bf9c 100644 --- a/packages/plugin-tee/package.json +++ b/packages/plugin-tee/package.json @@ -32,7 +32,8 @@ "scripts": { "build": "tsup --format esm --dts", "dev": "tsup --format esm --dts --watch", - "lint": "eslint --fix --cache ." + "lint": "eslint --fix --cache .", + "test": "vitest run" }, "peerDependencies": { "whatwg-url": "7.1.0" diff --git a/packages/plugin-tee/src/actions/remoteAttestation.ts b/packages/plugin-tee/src/actions/remoteAttestation.ts index f1fddbdd90..87fa15dde0 100644 --- a/packages/plugin-tee/src/actions/remoteAttestation.ts +++ b/packages/plugin-tee/src/actions/remoteAttestation.ts @@ -1,6 +1,7 @@ import type { IAgentRuntime, Memory, State, HandlerCallback } from "@elizaos/core"; import { RemoteAttestationProvider } from "../providers/remoteAttestationProvider"; import { fetch, type BodyInit } from "undici"; +import { RemoteAttestationMessage } from "../types/tee"; function hexToUint8Array(hex: string) { hex = hex.trim(); @@ -42,23 +43,33 @@ export const remoteAttestationAction = { description: "Generate a remote attestation to prove that the agent is running in a TEE", handler: async ( runtime: IAgentRuntime, - _message: Memory, + message: Memory, _state: State, _options: { [key: string]: unknown }, callback: HandlerCallback, ) => { try { + // Attestation will be generated based on the message info + const attestationMessage: RemoteAttestationMessage = { + agentId: runtime.agentId, + timestamp: Date.now(), + message: { + userId: message.userId, + roomId: message.roomId, + content: message.content.text, + }, + }; // Get the remote attestation of the agentId - const agentId = runtime.agentId; const teeMode = runtime.getSetting("TEE_MODE"); const provider = new RemoteAttestationProvider(teeMode); - const attestation = await provider.generateAttestation(agentId, 'raw'); + + const attestation = await provider.generateAttestation(JSON.stringify(attestationMessage)); const attestationData = hexToUint8Array(attestation.quote); const response = await uploadUint8Array(attestationData); const data = await response.json(); callback({ text: `Here's my 🧾 RA Quote 🫡 - https://proof.t16z.com/reports/${data.checksum}`, +https://proof.t16z.com/reports/${data.checksum}`, action: "NONE", }); return true; diff --git a/packages/plugin-tee/src/providers/deriveKeyProvider.ts b/packages/plugin-tee/src/providers/deriveKeyProvider.ts index 96430f2358..cf6cfd4592 100644 --- a/packages/plugin-tee/src/providers/deriveKeyProvider.ts +++ b/packages/plugin-tee/src/providers/deriveKeyProvider.ts @@ -11,12 +11,7 @@ import { DeriveKeyResponse, TappdClient } from "@phala/dstack-sdk"; import { privateKeyToAccount } from "viem/accounts"; import { PrivateKeyAccount, keccak256 } from "viem"; import { RemoteAttestationProvider } from "./remoteAttestationProvider"; -import { TEEMode, RemoteAttestationQuote } from "../types/tee"; - -interface DeriveKeyAttestationData { - agentId: string; - publicKey: string; -} +import { TEEMode, RemoteAttestationQuote, DeriveKeyAttestationData } from "../types/tee"; class DeriveKeyProvider { private client: TappdClient; @@ -57,11 +52,13 @@ class DeriveKeyProvider { private async generateDeriveKeyAttestation( agentId: string, - publicKey: string + publicKey: string, + subject?: string ): Promise { const deriveKeyData: DeriveKeyAttestationData = { agentId, publicKey, + subject, }; const reportdata = JSON.stringify(deriveKeyData); elizaLogger.log( @@ -72,6 +69,12 @@ class DeriveKeyProvider { return quote; } + /** + * Derives a raw key from the given path and subject. + * @param path - The path to derive the key from. This is used to derive the key from the root of trust. + * @param subject - The subject to derive the key from. This is used for the certificate chain. + * @returns The derived key. + */ async rawDeriveKey( path: string, subject: string @@ -94,6 +97,13 @@ class DeriveKeyProvider { } } + /** + * Derives an Ed25519 keypair from the given path and subject. + * @param path - The path to derive the key from. This is used to derive the key from the root of trust. + * @param subject - The subject to derive the key from. This is used for the certificate chain. + * @param agentId - The agent ID to generate an attestation for. + * @returns An object containing the derived keypair and attestation. + */ async deriveEd25519Keypair( path: string, subject: string, @@ -130,6 +140,13 @@ class DeriveKeyProvider { } } + /** + * Derives an ECDSA keypair from the given path and subject. + * @param path - The path to derive the key from. This is used to derive the key from the root of trust. + * @param subject - The subject to derive the key from. This is used for the certificate chain. + * @param agentId - The agent ID to generate an attestation for. This is used for the certificate chain. + * @returns An object containing the derived keypair and attestation. + */ async deriveEcdsaKeypair( path: string, subject: string, @@ -184,13 +201,13 @@ const deriveKeyProvider: Provider = { const secretSalt = runtime.getSetting("WALLET_SECRET_SALT") || "secret_salt"; const solanaKeypair = await provider.deriveEd25519Keypair( - "/", secretSalt, + "solana", agentId ); const evmKeypair = await provider.deriveEcdsaKeypair( - "/", secretSalt, + "evm", agentId ); return JSON.stringify({ diff --git a/packages/plugin-tee/src/providers/remoteAttestationProvider.ts b/packages/plugin-tee/src/providers/remoteAttestationProvider.ts index 262b58c34f..fb9c174713 100644 --- a/packages/plugin-tee/src/providers/remoteAttestationProvider.ts +++ b/packages/plugin-tee/src/providers/remoteAttestationProvider.ts @@ -6,7 +6,7 @@ import { elizaLogger, } from "@elizaos/core"; import { TdxQuoteResponse, TappdClient, TdxQuoteHashAlgorithms } from "@phala/dstack-sdk"; -import { RemoteAttestationQuote, TEEMode } from "../types/tee"; +import { RemoteAttestationQuote, TEEMode, RemoteAttestationMessage } from "../types/tee"; class RemoteAttestationProvider { private client: TappdClient; @@ -74,14 +74,23 @@ class RemoteAttestationProvider { // Keep the original provider for backwards compatibility const remoteAttestationProvider: Provider = { - get: async (runtime: IAgentRuntime, _message: Memory, _state?: State) => { + get: async (runtime: IAgentRuntime, message: Memory, _state?: State) => { const teeMode = runtime.getSetting("TEE_MODE"); const provider = new RemoteAttestationProvider(teeMode); const agentId = runtime.agentId; try { - elizaLogger.log("Generating attestation for: ", agentId); - const attestation = await provider.generateAttestation(agentId, 'raw'); + const attestationMessage: RemoteAttestationMessage = { + agentId: agentId, + timestamp: Date.now(), + message: { + userId: message.userId, + roomId: message.roomId, + content: message.content.text, + } + }; + elizaLogger.log("Generating attestation for: ", JSON.stringify(attestationMessage)); + const attestation = await provider.generateAttestation(JSON.stringify(attestationMessage)); return `Your Agent's remote attestation is: ${JSON.stringify(attestation)}`; } catch (error) { console.error("Error in remote attestation provider:", error); diff --git a/packages/plugin-tee/src/providers/walletProvider.ts b/packages/plugin-tee/src/providers/walletProvider.ts index a31cc65cea..0d08c0e07b 100644 --- a/packages/plugin-tee/src/providers/walletProvider.ts +++ b/packages/plugin-tee/src/providers/walletProvider.ts @@ -299,8 +299,8 @@ const walletProvider: Provider = { keypair: Keypair; attestation: RemoteAttestationQuote; } = await deriveKeyProvider.deriveEd25519Keypair( - "/", runtime.getSetting("WALLET_SECRET_SALT"), + "solana", agentId ); publicKey = derivedKeyPair.keypair.publicKey; diff --git a/packages/plugin-tee/src/tests/deriveKey.test.ts b/packages/plugin-tee/src/tests/deriveKey.test.ts new file mode 100644 index 0000000000..4611f1e307 --- /dev/null +++ b/packages/plugin-tee/src/tests/deriveKey.test.ts @@ -0,0 +1,133 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { DeriveKeyProvider } from '../providers/deriveKeyProvider'; +import { TappdClient } from '@phala/dstack-sdk'; +import { TEEMode } from '../types/tee'; + +// Mock dependencies +vi.mock('@phala/dstack-sdk', () => ({ + TappdClient: vi.fn().mockImplementation(() => ({ + deriveKey: vi.fn().mockResolvedValue({ + asUint8Array: () => new Uint8Array([1, 2, 3, 4, 5]) + }), + tdxQuote: vi.fn().mockResolvedValue({ + quote: 'mock-quote-data', + replayRtmrs: () => ['rtmr0', 'rtmr1', 'rtmr2', 'rtmr3'] + }), + rawDeriveKey: vi.fn() + })) +})); + +vi.mock('@solana/web3.js', () => ({ + Keypair: { + fromSeed: vi.fn().mockReturnValue({ + publicKey: { + toBase58: () => 'mock-solana-public-key' + } + }) + } +})); + +vi.mock('viem/accounts', () => ({ + privateKeyToAccount: vi.fn().mockReturnValue({ + address: 'mock-evm-address' + }) +})); + +describe('DeriveKeyProvider', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('constructor', () => { + it('should initialize with LOCAL mode', () => { + const _provider = new DeriveKeyProvider(TEEMode.LOCAL); + expect(TappdClient).toHaveBeenCalledWith('http://localhost:8090'); + }); + + it('should initialize with DOCKER mode', () => { + const _provider = new DeriveKeyProvider(TEEMode.DOCKER); + expect(TappdClient).toHaveBeenCalledWith('http://host.docker.internal:8090'); + }); + + it('should initialize with PRODUCTION mode', () => { + const _provider = new DeriveKeyProvider(TEEMode.PRODUCTION); + expect(TappdClient).toHaveBeenCalledWith(); + }); + + it('should throw error for invalid mode', () => { + expect(() => new DeriveKeyProvider('INVALID_MODE')).toThrow('Invalid TEE_MODE'); + }); + }); + + describe('rawDeriveKey', () => { + let _provider: DeriveKeyProvider; + + beforeEach(() => { + _provider = new DeriveKeyProvider(TEEMode.LOCAL); + }); + + it('should derive raw key successfully', async () => { + const path = 'test-path'; + const subject = 'test-subject'; + const result = await _provider.rawDeriveKey(path, subject); + + const client = TappdClient.mock.results[0].value; + expect(client.deriveKey).toHaveBeenCalledWith(path, subject); + expect(result.asUint8Array()).toEqual(new Uint8Array([1, 2, 3, 4, 5])); + }); + + it('should handle errors during raw key derivation', async () => { + const mockError = new Error('Key derivation failed'); + vi.mocked(TappdClient).mockImplementationOnce(() => { + const instance = new TappdClient(); + instance.deriveKey = vi.fn().mockRejectedValueOnce(mockError); + instance.tdxQuote = vi.fn(); + instance.rawDeriveKey = vi.fn(); + return instance; + }); + + const provider = new DeriveKeyProvider(TEEMode.LOCAL); + await expect(provider.rawDeriveKey('path', 'subject')).rejects.toThrow(mockError); + }); + }); + + describe('deriveEd25519Keypair', () => { + let provider: DeriveKeyProvider; + + beforeEach(() => { + provider = new DeriveKeyProvider(TEEMode.LOCAL); + }); + + it('should derive Ed25519 keypair successfully', async () => { + const path = 'test-path'; + const subject = 'test-subject'; + const agentId = 'test-agent'; + + const result = await provider.deriveEd25519Keypair(path, subject, agentId); + + expect(result).toHaveProperty('keypair'); + expect(result).toHaveProperty('attestation'); + expect(result.keypair.publicKey.toBase58()).toBe('mock-solana-public-key'); + }); + }); + + describe('deriveEcdsaKeypair', () => { + let provider: DeriveKeyProvider; + + beforeEach(() => { + provider = new DeriveKeyProvider(TEEMode.LOCAL); + }); + + it('should derive ECDSA keypair successfully', async () => { + const path = 'test-path'; + const subject = 'test-subject'; + const agentId = 'test-agent'; + + const result = await provider.deriveEcdsaKeypair(path, subject, agentId); + + expect(result).toHaveProperty('keypair'); + expect(result).toHaveProperty('attestation'); + expect(result.keypair.address).toBe('mock-evm-address'); + }); + }); +}); \ No newline at end of file diff --git a/packages/plugin-tee/src/tests/remoteAttestation.test.ts b/packages/plugin-tee/src/tests/remoteAttestation.test.ts new file mode 100644 index 0000000000..d9c401f256 --- /dev/null +++ b/packages/plugin-tee/src/tests/remoteAttestation.test.ts @@ -0,0 +1,81 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { RemoteAttestationProvider } from '../providers/remoteAttestationProvider'; +import { TappdClient } from '@phala/dstack-sdk'; +import { TEEMode } from '../types/tee'; + +// Mock TappdClient +vi.mock('@phala/dstack-sdk', () => ({ + TappdClient: vi.fn().mockImplementation(() => ({ + tdxQuote: vi.fn().mockResolvedValue({ + quote: 'mock-quote-data', + replayRtmrs: () => ['rtmr0', 'rtmr1', 'rtmr2', 'rtmr3'] + }), + deriveKey: vi.fn() + })) +})); + +describe('RemoteAttestationProvider', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('constructor', () => { + it('should initialize with LOCAL mode', () => { + const _provider = new RemoteAttestationProvider(TEEMode.LOCAL); + expect(TappdClient).toHaveBeenCalledWith('http://localhost:8090'); + }); + + it('should initialize with DOCKER mode', () => { + const _provider = new RemoteAttestationProvider(TEEMode.DOCKER); + expect(TappdClient).toHaveBeenCalledWith('http://host.docker.internal:8090'); + }); + + it('should initialize with PRODUCTION mode', () => { + const _provider = new RemoteAttestationProvider(TEEMode.PRODUCTION); + expect(TappdClient).toHaveBeenCalledWith(); + }); + + it('should throw error for invalid mode', () => { + expect(() => new RemoteAttestationProvider('INVALID_MODE')).toThrow('Invalid TEE_MODE'); + }); + }); + + describe('generateAttestation', () => { + let provider: RemoteAttestationProvider; + + beforeEach(() => { + provider = new RemoteAttestationProvider(TEEMode.LOCAL); + }); + + it('should generate attestation successfully', async () => { + const reportData = 'test-report-data'; + const quote = await provider.generateAttestation(reportData); + + expect(quote).toEqual({ + quote: 'mock-quote-data', + timestamp: expect.any(Number) + }); + }); + + it('should handle errors during attestation generation', async () => { + const mockError = new Error('TDX Quote generation failed'); + const mockTdxQuote = vi.fn().mockRejectedValue(mockError); + vi.mocked(TappdClient).mockImplementationOnce(() => ({ + tdxQuote: mockTdxQuote, + deriveKey: vi.fn() + })); + + const provider = new RemoteAttestationProvider(TEEMode.LOCAL); + await expect(provider.generateAttestation('test-data')).rejects.toThrow('Failed to generate TDX Quote'); + }); + + it('should pass hash algorithm to tdxQuote when provided', async () => { + const reportData = 'test-report-data'; + const hashAlgorithm = 'raw'; + await provider.generateAttestation(reportData, hashAlgorithm); + + const client = TappdClient.mock.results[0].value; + expect(client.tdxQuote).toHaveBeenCalledWith(reportData, hashAlgorithm); + }); + }); +}); \ No newline at end of file diff --git a/packages/plugin-tee/src/tests/remoteAttestationAction.test.ts b/packages/plugin-tee/src/tests/remoteAttestationAction.test.ts new file mode 100644 index 0000000000..3cec5f7d3b --- /dev/null +++ b/packages/plugin-tee/src/tests/remoteAttestationAction.test.ts @@ -0,0 +1,103 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { remoteAttestationAction } from '../actions/remoteAttestation'; +import { RemoteAttestationProvider } from '../providers/remoteAttestationProvider'; + +// Mock dependencies +vi.mock('../providers/remoteAttestationProvider'); +vi.mock('undici', () => ({ + fetch: vi.fn().mockResolvedValue({ + json: () => Promise.resolve({ checksum: 'mock-checksum' }) + }) +})); + +describe('remoteAttestationAction', () => { + const mockRuntime = { + agentId: 'test-agent-id', + getSetting: vi.fn().mockReturnValue('LOCAL'), + getState: vi.fn(), + setState: vi.fn(), + message:{ userId: 'user', roomId: 'room', content: { text: 'If you are running in a TEE, generate a remote attestation' } }, + setConversation: vi.fn() + }; + + const mockCallback = vi.fn(); + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(RemoteAttestationProvider).mockImplementation(() => ({ + generateAttestation: vi.fn().mockResolvedValue({ + quote: '0x1234', + timestamp: Date.now() + }) + })); + }); + + it('should have correct name and similes', () => { + expect(remoteAttestationAction.name).toBe('REMOTE_ATTESTATION'); + expect(remoteAttestationAction.similes).toContain('REMOTE_ATTESTATION'); + expect(remoteAttestationAction.similes).toContain('TEE_REMOTE_ATTESTATION'); + expect(remoteAttestationAction.similes).toContain('TEE_ATTESTATION'); + }); + + describe('handler', () => { + it('should generate and upload attestation successfully', async () => { + const result = await remoteAttestationAction.handler( + mockRuntime, + mockRuntime.message, + {}, + {}, + mockCallback + ); + + expect(result).toBe(true); + expect(mockCallback).toHaveBeenCalledWith({ + text: expect.stringContaining('https://proof.t16z.com/reports/mock-checksum'), + action: 'NONE' + }); + }); + + it('should handle errors during attestation generation', async () => { + const mockError = new Error('Attestation generation failed'); + vi.mocked(RemoteAttestationProvider).mockImplementation(() => ({ + generateAttestation: vi.fn().mockRejectedValueOnce(mockError), + client: { + tdxQuote: vi.fn(), + deriveKey: vi.fn() + } + })); + + const result = await remoteAttestationAction.handler( + mockRuntime, + {}, + {}, + {}, + mockCallback + ); + + expect(result).toBe(false); + }); + + }); + + describe('validate', () => { + it('should always return true', async () => { + const result = await remoteAttestationAction.validate(mockRuntime); + expect(result).toBe(true); + }); + }); + + describe('examples', () => { + it('should have valid example conversations', () => { + expect(remoteAttestationAction.examples).toBeInstanceOf(Array); + expect(remoteAttestationAction.examples[0]).toBeInstanceOf(Array); + + const [userMessage, agentMessage] = remoteAttestationAction.examples[0]; + expect(userMessage.user).toBe('{{user1}}'); + expect(userMessage.content.text).toBe('If you are running in a TEE, generate a remote attestation'); + + expect(agentMessage.user).toBe('{{user2}}'); + expect(agentMessage.content.text).toBe('Of course, one second...'); + expect(agentMessage.content.action).toBe('REMOTE_ATTESTATION'); + }); + }); +}); \ No newline at end of file diff --git a/packages/plugin-tee/src/types/tee.ts b/packages/plugin-tee/src/types/tee.ts index de974c3924..9e3ae1fed5 100644 --- a/packages/plugin-tee/src/types/tee.ts +++ b/packages/plugin-tee/src/types/tee.ts @@ -9,3 +9,19 @@ export interface RemoteAttestationQuote { quote: string; timestamp: number; } + +export interface DeriveKeyAttestationData { + agentId: string; + publicKey: string; + subject?: string; +} + +export interface RemoteAttestationMessage { + agentId: string; + timestamp: number; + message: { + userId: string; + roomId: string; + content: string; + } +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42065374b1..a252b4b0d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -193,6 +193,9 @@ importers: '@elizaos/plugin-binance': specifier: workspace:* version: link:../packages/plugin-binance + '@elizaos/plugin-birdeye': + specifier: workspace:* + version: link:../packages/plugin-birdeye '@elizaos/plugin-bootstrap': specifier: workspace:* version: link:../packages/plugin-bootstrap @@ -238,6 +241,9 @@ importers: '@elizaos/plugin-gitbook': specifier: workspace:* version: link:../packages/plugin-gitbook + '@elizaos/plugin-gitcoin-passport': + specifier: workspace:* + version: link:../packages/plugin-gitcoin-passport '@elizaos/plugin-goat': specifier: workspace:* version: link:../packages/plugin-goat @@ -304,6 +310,9 @@ importers: '@elizaos/plugin-solana-agentkit': specifier: workspace:* version: link:../packages/plugin-solana-agentkit + '@elizaos/plugin-squid-router': + specifier: workspace:* + version: link:../packages/plugin-squid-router '@elizaos/plugin-stargaze': specifier: workspace:* version: link:../packages/plugin-stargaze @@ -1525,6 +1534,64 @@ importers: specifier: 8.3.5 version: 8.3.5(@swc/core@1.10.7(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.0)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) + packages/plugin-birdeye: + dependencies: + '@coral-xyz/anchor': + specifier: 0.30.1 + version: 0.30.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@elizaos/core': + specifier: workspace:* + version: link:../core + '@solana/spl-token': + specifier: 0.4.9 + version: 0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(utf-8-validate@5.0.10) + '@solana/web3.js': + specifier: 1.95.8 + version: 1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + bignumber: + specifier: 1.1.0 + version: 1.1.0 + bignumber.js: + specifier: 9.1.2 + version: 9.1.2 + bs58: + specifier: 6.0.0 + version: 6.0.0 + fomo-sdk-solana: + specifier: 1.3.2 + version: 1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(utf-8-validate@5.0.10) + form-data: + specifier: 4.0.1 + version: 4.0.1 + node-cache: + specifier: 5.1.2 + version: 5.1.2 + pumpdotfun-sdk: + specifier: 1.3.2 + version: 1.3.2(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(rollup@4.30.1)(typescript@5.7.3)(utf-8-validate@5.0.10) + tsup: + specifier: 8.3.5 + version: 8.3.5(@swc/core@1.10.7(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.0)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) + vitest: + specifier: 2.1.4 + version: 2.1.4(@types/node@22.10.6)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + whatwg-url: + specifier: 7.1.0 + version: 7.1.0 + devDependencies: + '@types/node': + specifier: ^22.10.2 + version: 22.10.6 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.10.7(@swc/helpers@0.5.15))(@types/node@22.10.6)(typescript@5.7.3) + tsconfig-paths: + specifier: ^4.2.0 + version: 4.2.0 + typescript: + specifier: ^5.7.2 + version: 5.7.3 + packages/plugin-bootstrap: dependencies: '@elizaos/core': @@ -1814,6 +1881,18 @@ importers: specifier: 8.3.5 version: 8.3.5(@swc/core@1.10.7(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.0)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) + packages/plugin-gitcoin-passport: + dependencies: + '@elizaos/core': + specifier: workspace:* + version: link:../core + tsup: + specifier: 8.3.5 + version: 8.3.5(@swc/core@1.10.7(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.0)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) + whatwg-url: + specifier: 7.1.0 + version: 7.1.0 + packages/plugin-goat: dependencies: '@elizaos/core': @@ -2649,6 +2728,33 @@ importers: specifier: ^5.0.0 version: 5.6.3 + packages/plugin-squid-router: + dependencies: + '@0xsquid/sdk': + specifier: 2.8.29 + version: 2.8.29(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@0xsquid/squid-types': + specifier: 0.1.122 + version: 0.1.122(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.7.3)(utf-8-validate@5.0.10) + '@elizaos/core': + specifier: workspace:* + version: link:../core + ethers: + specifier: 6.8.1 + version: 6.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optional: + specifier: 0.1.4 + version: 0.1.4 + sharp: + specifier: 0.33.5 + version: 0.33.5 + tsup: + specifier: 8.3.5 + version: 8.3.5(@swc/core@1.10.7(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.0)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) + whatwg-url: + specifier: 7.1.0 + version: 7.1.0 + packages/plugin-stargaze: dependencies: '@elizaos/core': @@ -3033,6 +3139,14 @@ packages: graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 typescript: ^5.0.0 + '@0xsquid/sdk@2.8.29': + resolution: {integrity: sha512-u8oJ67sN9rkc1VMoCuTYyNlSUxFrp5SXnium8YASii0NKuK+42uTUn5T+4gFKh529ZzChJTYgTXLVWk4Vja8Kg==} + + '@0xsquid/squid-types@0.1.122': + resolution: {integrity: sha512-rO1/a1l6RwzZrQY3V92spGKYzEk8DLl1KSNRqqytUqBZHPkwUSCP/nU4uahkimCLuvXmiVf/n31Xo9d8ytdjyA==} + peerDependencies: + typescript: ^5.3.3 + '@3land/listings-sdk@0.0.4': resolution: {integrity: sha512-Ljq8R4e7y+wl4m8BGhiInFPCHEzHZZFz1qghnbc8B3bLEKXWM9+2gZOCAa84rdUKuLfzenEdeS2LclTKhdKTFQ==} @@ -3050,6 +3164,9 @@ packages: resolution: {integrity: sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==} engines: {node: '>=0.8'} + '@adraffy/ens-normalize@1.10.0': + resolution: {integrity: sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==} + '@adraffy/ens-normalize@1.10.1': resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} @@ -3616,6 +3733,20 @@ packages: resolution: {integrity: sha512-5xK2SqGU1mzzsOeemy7cy3fGKxR1sEpUs4pEiIjaT0OIvU+fZaDVUEYWOqsgns6wI90XZEQJlXtI8uAHX/do5Q==} engines: {node: '>=18.0.0'} + '@axelar-network/axelar-cgp-solidity@6.4.0': + resolution: {integrity: sha512-Xnw5xi234B1cmTCzgudV8zq+DDjJ1d1U362CM0vKH1FWmZprKIdqgmOYkiRyu+QiVhnznKiBURiSEHVrNjtYpw==} + engines: {node: '>=18'} + + '@axelar-network/axelar-gmp-sdk-solidity@5.10.0': + resolution: {integrity: sha512-s8SImALvYB+5AeiT3tbfWNBI2Mhqw1x91i/zM3DNpVUCnAR2HKtsB9T84KnUn/OJjOVgb4h0lv7q9smeYniRPw==} + engines: {node: '>=18'} + + '@axelar-network/axelarjs-sdk@0.16.2': + resolution: {integrity: sha512-Fz/fovSzJRDNg9WrAzDffWzROe2Cm/e/f2OmwUossFbwm9xzZvoOHoJAEHd4T3ZK5SELqpcCBDvpe9wWtZDokg==} + + '@axelar-network/axelarjs-types@0.33.0': + resolution: {integrity: sha512-aCbX/5G+tgWPjr9tl3dQfJftWwRMkILz61ytach7dKqxtO9G9jlxpNvELJQ6gKVOodUtSY8qBCO/fWU19v4hdQ==} + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -4500,6 +4631,9 @@ packages: '@cosmjs/encoding@0.32.4': resolution: {integrity: sha512-tjvaEy6ZGxJchiizzTn7HVRiyTg1i4CObRRaTRPknm5EalE13SV+TCHq38gIDfyUeden4fCuaBVEdBR5+ti7Hw==} + '@cosmjs/json-rpc@0.30.1': + resolution: {integrity: sha512-pitfC/2YN9t+kXZCbNuyrZ6M8abnCC2n62m+JtU9vQUfaEtVsgy+1Fk4TRQ175+pIWSdBMFi2wT8FWVEE4RhxQ==} + '@cosmjs/json-rpc@0.31.3': resolution: {integrity: sha512-7LVYerXjnm69qqYR3uA6LGCrBW2EO5/F7lfJxAmY+iII2C7xO3a0vAjMSt5zBBh29PXrJVS6c2qRP22W1Le2Wg==} @@ -4533,6 +4667,9 @@ packages: '@cosmjs/socket@0.32.4': resolution: {integrity: sha512-davcyYziBhkzfXQTu1l5NrpDYv0K9GekZCC9apBRvL1dvMc9F/ygM7iemHjUA+z8tJkxKxrt/YPjJ6XNHzLrkw==} + '@cosmjs/stargate@0.31.0-alpha.1': + resolution: {integrity: sha512-kCTUT3niB2hvcHjhlxpM8cNw1KOVmgZROdJUQaO8Ts4j22OyRZRFdwRPrOIuAKpqhVW2I1vI2HQL9Bg7pk9Glw==} + '@cosmjs/stargate@0.31.3': resolution: {integrity: sha512-53NxnzmB9FfXpG4KjOUAYAvWLYKdEmZKsutcat/u2BrDXNZ7BN8jim/ENcpwXfs9/Og0K24lEIdvA4gsq3JDQw==} @@ -4542,6 +4679,9 @@ packages: '@cosmjs/stargate@0.32.4': resolution: {integrity: sha512-usj08LxBSsPRq9sbpCeVdyLx2guEcOHfJS9mHGCLCXpdAPEIEQEtWLDpEUc0LEhWOx6+k/ChXTc5NpFkdrtGUQ==} + '@cosmjs/stream@0.30.1': + resolution: {integrity: sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ==} + '@cosmjs/stream@0.31.3': resolution: {integrity: sha512-8keYyI7X0RjsLyVcZuBeNjSv5FA4IHwbFKx7H60NHFXszN8/MvXL6aZbNIvxtcIHHsW7K9QSQos26eoEWlAd+w==} @@ -9099,6 +9239,9 @@ packages: resolution: {integrity: sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==} engines: {node: '>=18.0.0'} + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@solana-developers/helpers@2.5.6': resolution: {integrity: sha512-NPWZblVMl4LuVVSJOZG0ZF0VYnrMUjCyMNTiGwNUXPK2WWYJCqpuDyzs/PMqwvM4gMTjk4pEToBX8N2UxDvZkQ==} @@ -9988,6 +10131,9 @@ packages: '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + '@types/node@18.15.13': + resolution: {integrity: sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==} + '@types/node@18.19.70': resolution: {integrity: sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==} @@ -13379,6 +13525,13 @@ packages: end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + engine.io-client@6.6.2: + resolution: {integrity: sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + enhanced-resolve@5.18.0: resolution: {integrity: sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==} engines: {node: '>=10.13.0'} @@ -13765,6 +13918,12 @@ packages: resolution: {integrity: sha512-Ij7U9OgVZc4MAui8BttPCEaWUrAXy+eo2IbVfIxZyfzfFxMQrbIWXRUbrsRBqRrIhJ75T8P+KQRKpKTaG0Du8Q==} engines: {node: ^14.21.3 || >=16, npm: '>=9'} + ethers-multicall-provider@5.0.0: + resolution: {integrity: sha512-dsfIwBSbr8yG+F0o87uoMFje1k5w988883MMJvK7R66mYT6NApQhQ7sMH/cxKGXTRf3at+nGt/4QIYHbYhe/8A==} + engines: {node: '>=12.0'} + peerDependencies: + lodash: ^4.17.0 + ethers@5.7.2: resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} @@ -13772,6 +13931,10 @@ packages: resolution: {integrity: sha512-21YtnZVg4/zKkCQPjrDj38B1r4nQvTZLopUGMLQ1ePU2zV/joCfDC3t3iKQjWRzjjjbzR+mdAIoikeBRNkdllA==} engines: {node: '>=14.0.0'} + ethers@6.8.1: + resolution: {integrity: sha512-iEKm6zox5h1lDn6scuRWdIdFJUCGg3+/aQWu0F4K0GVyEZiktFkqrJbRjTn1FlYEPz7RKA707D6g5Kdk6j7Ljg==} + engines: {node: '>=14.0.0'} + ethjs-unit@0.1.6: resolution: {integrity: sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==} engines: {node: '>=6.5.0', npm: '>=3'} @@ -20077,6 +20240,14 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + socket.io-client@4.8.1: + resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + sockjs@0.3.24: resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} @@ -20263,6 +20434,12 @@ packages: standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + standard-error@1.1.0: + resolution: {integrity: sha512-4v7qzU7oLJfMI5EltUSHCaaOd65J6S4BqKRWgzMi4EYaE5fvNabPxmAPGdxpGXqrcWjhDGI/H09CIdEuUOUeXg==} + + standard-http-error@2.0.1: + resolution: {integrity: sha512-DX/xPIoyXQTuY6BMZK4Utyi4l3A4vFoafsfqrU6/dO4Oe/59c7PyqPd2IQj9m+ZieDg2K3RL9xOYJsabcD9IUA==} + starknet@6.18.0: resolution: {integrity: sha512-nlxz7bK/YBY8W8NUevkycxFwphsX27oi+4YCl36TYFdrJpTOMqmJDnZ27ssr7z0eEDQLQscIxt1gXrZzCJua7g==} @@ -20329,6 +20506,9 @@ packages: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} + string-similarity-js@2.1.4: + resolution: {integrity: sha512-uApODZNjCHGYROzDSAdCmAHf60L/pMDHnP/yk6TAbvGg7JSPZlSto/ceCI7hZEqzc53/juU2aOJFkM2yUVTMTA==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -21024,6 +21204,9 @@ packages: tslib@1.9.3: resolution: {integrity: sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==} + tslib@2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + tslib@2.7.0: resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} @@ -22465,6 +22648,18 @@ packages: utf-8-validate: optional: true + ws@8.5.0: + resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + wtf_wikipedia@10.3.2: resolution: {integrity: sha512-8C1eUKDK6NaosrtocTEA4fz5Lm5nO6Hb92zLUqI7S1uVVjwEtI0mvSGSdGd/xR1nfSpDYm1ckBG1aLHEAF1pBg==} engines: {node: '>=12.0.0'} @@ -22485,6 +22680,10 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + xsalsa20@1.2.0: resolution: {integrity: sha512-FIr/DEeoHfj7ftfylnoFt3rAIRoWXpx2AoDfrT2qD2wtp7Dp+COajvs/Icb7uHqRW9m60f5iXZwdsJJO3kvb7w==} @@ -22640,6 +22839,40 @@ snapshots: graphql: 16.10.0 typescript: 5.7.3 + '@0xsquid/sdk@2.8.29(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)': + dependencies: + '@cosmjs/cosmwasm-stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@cosmjs/encoding': 0.32.4 + '@cosmjs/stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/wallet': 5.7.0 + '@project-serum/anchor': 0.26.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.3(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + axios: 1.7.9(debug@4.4.0) + bs58: 6.0.0 + cosmjs-types: 0.9.0 + ethers: 6.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ethers-multicall-provider: 5.0.0(bufferutil@4.0.9)(lodash@4.17.21)(utf-8-validate@5.0.10) + lodash: 4.17.21 + long: 5.2.4 + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - utf-8-validate + + '@0xsquid/squid-types@0.1.122(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.7.3)(utf-8-validate@5.0.10)': + dependencies: + '@axelar-network/axelarjs-sdk': 0.16.2(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@ethersproject/providers': 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + typescript: 5.7.3 + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - supports-color + - utf-8-validate + '@3land/listings-sdk@0.0.4(@swc/core@1.10.7(@swc/helpers@0.5.15))(@types/node@20.17.9)(arweave@1.15.5)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(utf-8-validate@5.0.10)': dependencies: '@coral-xyz/borsh': 0.30.1(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) @@ -22685,6 +22918,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@adraffy/ens-normalize@1.10.0': {} + '@adraffy/ens-normalize@1.10.1': {} '@adraffy/ens-normalize@1.11.0': {} @@ -23873,6 +24108,43 @@ snapshots: '@smithy/types': 4.1.0 tslib: 2.8.1 + '@axelar-network/axelar-cgp-solidity@6.4.0': + dependencies: + '@axelar-network/axelar-gmp-sdk-solidity': 5.10.0 + + '@axelar-network/axelar-gmp-sdk-solidity@5.10.0': {} + + '@axelar-network/axelarjs-sdk@0.16.2(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)': + dependencies: + '@axelar-network/axelar-cgp-solidity': 6.4.0 + '@axelar-network/axelarjs-types': 0.33.0 + '@cosmjs/json-rpc': 0.30.1 + '@cosmjs/stargate': 0.31.0-alpha.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/networks': 5.7.1 + '@ethersproject/providers': 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@types/uuid': 8.3.4 + bech32: 2.0.0 + clone-deep: 4.0.1 + cross-fetch: 3.2.0(encoding@0.1.13) + ethers: 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + standard-http-error: 2.0.1 + string-similarity-js: 2.1.4 + uuid: 8.3.2 + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - supports-color + - utf-8-validate + + '@axelar-network/axelarjs-types@0.33.0': + dependencies: + long: 5.2.4 + protobufjs: 7.4.0 + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 @@ -24847,7 +25119,7 @@ snapshots: '@coinbase/wallet-sdk@4.2.4': dependencies: - '@noble/hashes': 1.6.1 + '@noble/hashes': 1.7.0 clsx: 1.2.1 eventemitter3: 5.0.1 preact: 10.25.4 @@ -25216,6 +25488,11 @@ snapshots: bech32: 1.1.4 readonly-date: 1.0.0 + '@cosmjs/json-rpc@0.30.1': + dependencies: + '@cosmjs/stream': 0.30.1 + xstream: 11.14.0 + '@cosmjs/json-rpc@0.31.3': dependencies: '@cosmjs/stream': 0.31.3 @@ -25298,6 +25575,25 @@ snapshots: - bufferutil - utf-8-validate + '@cosmjs/stargate@0.31.0-alpha.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@confio/ics23': 0.6.8 + '@cosmjs/amino': 0.31.3 + '@cosmjs/encoding': 0.31.3 + '@cosmjs/math': 0.31.3 + '@cosmjs/proto-signing': 0.31.3 + '@cosmjs/stream': 0.31.3 + '@cosmjs/tendermint-rpc': 0.31.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@cosmjs/utils': 0.31.3 + cosmjs-types: 0.8.0 + long: 4.0.0 + protobufjs: 6.11.4 + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + '@cosmjs/stargate@0.31.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@confio/ics23': 0.6.8 @@ -25351,6 +25647,10 @@ snapshots: - debug - utf-8-validate + '@cosmjs/stream@0.30.1': + dependencies: + xstream: 11.14.0 + '@cosmjs/stream@0.31.3': dependencies: xstream: 11.14.0 @@ -32459,6 +32759,8 @@ snapshots: '@smithy/types': 4.1.0 tslib: 2.8.1 + '@socket.io/component-emitter@3.1.2': {} + '@solana-developers/helpers@2.5.6(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(utf-8-validate@5.0.10)': dependencies: '@solana/spl-token': 0.4.9(@solana/web3.js@1.95.8(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)(utf-8-validate@5.0.10) @@ -33955,6 +34257,8 @@ snapshots: '@types/node@17.0.45': {} + '@types/node@18.15.13': {} + '@types/node@18.19.70': dependencies: undici-types: 5.26.5 @@ -38780,6 +39084,20 @@ snapshots: dependencies: once: 1.4.0 + engine.io-client@6.6.2(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + enhanced-resolve@5.18.0: dependencies: graceful-fs: 4.2.11 @@ -39532,6 +39850,14 @@ snapshots: '@scure/bip32': 1.5.0 '@scure/bip39': 1.4.0 + ethers-multicall-provider@5.0.0(bufferutil@4.0.9)(lodash@4.17.21)(utf-8-validate@5.0.10): + dependencies: + ethers: 6.13.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) + lodash: 4.17.21 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + ethers@5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@ethersproject/abi': 5.7.0 @@ -39630,6 +39956,19 @@ snapshots: - bufferutil - utf-8-validate + ethers@6.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@adraffy/ens-normalize': 1.10.0 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 18.15.13 + aes-js: 4.0.0-beta.5 + tslib: 2.4.0 + ws: 8.5.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + ethjs-unit@0.1.6: dependencies: bn.js: 4.11.6 @@ -45062,8 +45401,8 @@ snapshots: ox@0.4.2(typescript@5.7.3)(zod@3.24.1): dependencies: '@adraffy/ens-normalize': 1.11.0 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 + '@noble/curves': 1.8.0 + '@noble/hashes': 1.7.0 '@scure/bip32': 1.6.1 '@scure/bip39': 1.5.1 abitype: 1.0.7(typescript@5.7.3)(zod@3.24.1) @@ -47983,6 +48322,24 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 + socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + engine.io-client: 6.6.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + sockjs@0.3.24: dependencies: faye-websocket: 0.11.4 @@ -48265,6 +48622,12 @@ snapshots: standard-as-callback@2.1.0: {} + standard-error@1.1.0: {} + + standard-http-error@2.0.1: + dependencies: + standard-error: 1.1.0 + starknet@6.18.0(encoding@0.1.13): dependencies: '@noble/curves': 1.3.0 @@ -48351,6 +48714,8 @@ snapshots: char-regex: 1.0.2 strip-ansi: 6.0.1 + string-similarity-js@2.1.4: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -49222,6 +49587,8 @@ snapshots: tslib@1.9.3: {} + tslib@2.4.0: {} + tslib@2.7.0: {} tslib@2.8.1: {} @@ -51431,6 +51798,11 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 6.0.5 + ws@8.5.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 + wtf_wikipedia@10.3.2(encoding@0.1.13): dependencies: isomorphic-unfetch: 3.1.0(encoding@0.1.13) @@ -51448,6 +51820,8 @@ snapshots: xmlchars@2.2.0: {} + xmlhttprequest-ssl@2.1.2: {} + xsalsa20@1.2.0: {} xstream@11.14.0: @@ -51582,4 +51956,4 @@ snapshots: zwitch@1.0.5: {} - zwitch@2.0.4: {} \ No newline at end of file + zwitch@2.0.4: {}