Adding Agent Actions

Adding Agent Actions

The Starknet Agent Kit follows a decentralized approach where each contributor can add their own actions for a specific plugin. Each plugin is implemented as a separate package, allowing for modular development and clear dependency management. The goal is to make it easier to understand and scale to support the entire Network by clearly isolating the logic for each plugin. --Tutorial video--

Folder Organization

Plugins Library (./plugins/)

Each plugin implementation should be placed in its own directory under ./plugins/ with the following structure:

/plugins/
└── [plugin-name]/
    ├── src/
    │   ├── abis/         # Plugin-specific ABIs
    │   ├── actions/      # Plugin actions
    │   ├── tools/        # Plugin tools
    │   ├── schema/       # Plugin schema
    │   ├── utils/        # Utility functions
    │   ├── constant/     # Constants and addresses
    │   ├── interface/    # Interfaces and schemas
    │   ├── types/        # Type definitions 
    │   └── index.ts
    ├── test/
    ├── package.json        
    ├── tsconfig.json      
    ├── tsconfig.build.json       
    └── readme.md
ℹ️

To create your own plugin, you can use the command npx create-plugins <name_of_your_plugins>, which will automatically generate all the necessary files and folder structure required for plugin development.

For example, for a plugin named myPlugin, the structure could look like:

./plugins/myPlugin/
  ├── src/
  │  ├── abis/
  │  │   └── accountABI.json
  │  ├── actions/
  │  │   └── myAction.ts
  │  ├── tools/
  │  │   └──myToolsAction.ts
  │  ├── schema/
  │  │   └──myActionSchema.ts
  │  ├── utils/
  │  │   └── formatter.ts
  │  ├── constants/
  │  │   └── networkConstants.ts
  │  ├── interfaces/
  │  │   └── myActionInterfaces.ts
  │  └── types/
  │      └── myActionTypes.ts
  ├── test/
  │    └── index.ts
  ├── package.json        
  ├── tsconfig.json      
  ├── tsconfig.build.json       
  └── readme.md

Step-by-Step Implementation

Create the Validation Schema

Create a schema in ./plugins/myPlugin/schema/myActionSchema.ts:

import { z } from "zod";
 
export const myActionSchema = z.object({
  param1: z.string().describe("Description for the first parameter"),
  param2: z.number().describe("Description for the second parameter"),
  // Add more parameters as needed
});

Implement the Action

Place your action implementation in ./plugins/myPlugin/actions/myAction.ts:

import { myActionSchema } from "../interface/myActionSchema";
 
export const myAction = async (
  agent: StarknetAgentInterface,
  params: z.infer<typeof myActionSchema>
): Promise<string> => {
  try {
    // Plugin-specific implementation for myPlugin
    const result = {}; // Replace with the actual logic
    return JSON.stringify({
      status: "success",
      data: result,
    });
  } catch (error) {
    return JSON.stringify({
      status: "error",
      error: error instanceof Error ? error.message : "Unknown error",
    });
  }
};
ℹ️

If you want to avoid working directly with Zod objects but still need the type information, you can use z.infer<typeof YourZodSchema> to extract and use the TypeScript type definition instead of the full Zod schema.

Add Utilities, Constants, or Types

  • Place plugin-specific constants in ./plugins/myPlugin/constant/
  • Add helper functions in ./plugins/myPlugin/utils/
  • Define any additional types in ./plugins/myPlugin/types/

Register Your Action

Register your actions tools in ./plugins/myPlugin/tools/myActionsTools.ts so the agent can recognize and use it. For example:

import { StarknetTool } from "path/to/registry";
import { myActionSchema } from "../schema/myActionSchema";
import { myAction } from "../actions/myAction";
 
export const registerMyPluginTools = (StarknetToolRegistry : StarknetTool[]) => {
 
  StarknetToolRegistry.push({
    name: "my_action",
    plugin: "myPlugin"
    description: "Description of what your action does",
    schema: myActionSchema,
    execute: myAction,
  });
 
};

Test Your Action

Ensure your action works as expected by writing tests. Create a test file in test/unit-test/plugins/myPlugin/myAction.spec.ts:

// test/unit-test/plugins/myPlugin/myAction.spec.ts
 
describe("myAction", () => {
  let agent: StarknetAgentInterface;
 
  beforeEach(() => {
    agent = createMockAgent();
  });
 
  it("should execute successfully", async () => {
    const result = await myAction(agent, {
      param1: "test",
      param2: 123,
    });
    expect(JSON.parse(result).status).toBe("success");
  });
});

Best Practices

Plugin Organization

Follow these organizational principles:

  • Use consistent naming:
    • plugins: camelCase
    • Types: PascalCase
    • Files: kebab-case
  • One tool per file
  • Group related tools logically