Custom Agents

Build your own agents for specialized use cases.

Overview

While reference agents cover common use cases, you can build custom agents for specialized needs. Custom agents use the same SDK and infrastructure but with your own logic and capabilities.

Project Setup

Create a new agent project:

# Create new agent project
$ mkdir my-custom-agent && cd my-custom-agent
$ pnpm init

# Install SDK and dependencies
$ pnpm add @hyperfold/actions-sdk
$ pnpm add -D typescript @types/node

# Initialize TypeScript
$ npx tsc --init

# Create project structure
$ mkdir -p src/{handlers,tools,utils}
$ touch src/index.ts src/agent.ts

Agent Structure

Basic structure of a custom agent:

import {
  HyperfoldAgent,
  OnACPEvent,
  OnSchedule,
  OnEndpoint,
  getProduct,
  calculateDynamicPrice,
  sendEmail,
} from '@hyperfold/actions-sdk';

@HyperfoldAgent({
  name: 'my-custom-agent',
  type: 'custom',
  model: {
    provider: 'openai',
    model: 'gpt-4o',
    temperature: 0.7,
  },
  systemPrompt: `
    You are a specialized agent for [your use case].
    [Your custom instructions here]
  `,
  capabilities: ['custom_capability'],
  integrations: {
    catalog: 'default',
    payments: 'stripe',
  },
})
export class MyCustomAgent {

  @OnACPEvent('custom_action')
  async handleCustomAction(data: CustomActionData) {
    return { success: true, result: data };
  }

  @OnSchedule('0 9 * * *')
  async dailyTask() {
    await this.processDaily();
  }

  @OnEndpoint('/custom/webhook')
  async handleWebhook(request: Request) {
    const payload = request.body;
    await this.processWebhook(payload);
    return { received: true };
  }

  private async processDaily() {
    // Your daily processing logic
  }

  private async processWebhook(payload: any) {
    // Your webhook processing logic
  }
}

// src/index.ts
export { MyCustomAgent } from './agent';

Local Testing

Test your agent locally before deployment:

import { TestRunner } from '@hyperfold/actions-sdk/testing';
import { MyCustomAgent } from './agent';

const runner = new TestRunner(MyCustomAgent);

async function test() {
  const result = await runner.invoke('custom_action', {
    data: { test: true },
  });
  console.log('Custom action result:', result);

  const quoteResult = await runner.invoke('quote', {
    productId: 'prod_test',
    offer: 100,
    context: {
      customer_id: 'cust_test',
      loyalty_tier: 'gold',
    },
  });
  console.log('Quote result:', quoteResult);
}

test();
$ npx ts-node src/test.ts

# Or use the CLI simulator
$ hyperfold sim local ./src/agent.ts

> [Local] Starting local agent simulation...
> [Ready] Agent loaded. Type messages to interact.

You: Test my custom action
Agent: [Executing custom_action handler...]
Agent: Custom action completed successfully.

Deployment

Deploy your custom agent:

# Build the agent
$ pnpm build

# Deploy to Hyperfold
$ hyperfold agent deploy ./dist

> [Scan] Found agent: my-custom-agent
> [Build] Packaging agent code...
> [Push] Uploading to Container Registry...
> [Deploy] Creating Cloud Run service...
> [Config] Setting up Firestore collections...

 Agent deployed successfully!

  Name:    my-custom-agent
  URL:     https://my-custom-agent-xyz.run.app
  Status:  active

# Deploy with environment variables
$ hyperfold agent deploy ./dist \
  --set-env="API_KEY=xxx" \
  --set-env="FEATURE_FLAG=true"

# Deploy to specific environment
$ hyperfold agent deploy ./dist --env=production

# Verify deployment
$ hyperfold agent get my-custom-agent

Dockerfile

The SDK auto-generates a Dockerfile, but you can customize it:

# Dockerfile (auto-generated, customize if needed)
FROM node:20-slim

WORKDIR /app

COPY package*.json ./
COPY pnpm-lock.yaml ./

RUN npm install -g pnpm && pnpm install --frozen-lockfile

COPY . .

RUN pnpm build

CMD ["node", "dist/index.js"]

Examples

Example custom agents for common use cases:

// Example 1: Subscription Management Agent
@HyperfoldAgent({
  name: 'subscription-agent',
  type: 'custom',
  capabilities: ['subscribe', 'manage_subscription'],
})
export class SubscriptionAgent {

  @OnACPEvent('subscribe')
  async handleSubscribe(productId: string, plan: string, context: BuyerContext) {
    const product = await getProduct(productId);
    const subscription = await this.stripe.subscriptions.create({
      customer: context.stripe_customer_id,
      items: [{ price: product.stripe_price_id }],
    });
    return {
      status: 'subscribed',
      subscription_id: subscription.id,
      next_billing: subscription.current_period_end,
    };
  }

  @OnSchedule('0 0 * * *')
  async processRenewals() {
    const expiringToday = await this.getExpiringSubscriptions();
    for (const sub of expiringToday) {
      await this.sendRenewalReminder(sub);
    }
  }
}

// Example 2: Inventory Alert Agent
@HyperfoldAgent({
  name: 'inventory-alert-agent',
  type: 'custom',
})
export class InventoryAlertAgent {

  @OnSchedule('*/15 * * * *')
  async checkInventoryLevels() {
    const lowStock = await this.catalog.findLowStock({ threshold: 10 });
    if (lowStock.length > 0) {
      await this.sendSlackAlert({
        channel: '#inventory-alerts',
        message: `⚠️ Low stock alert: ${lowStock.length} products below threshold`,
        products: lowStock,
      });
    }
  }

  @OnEndpoint('/reorder', { method: 'POST' })
  async triggerReorder(request: Request) {
    const { product_id, quantity } = request.body;
    await this.createPurchaseOrder(product_id, quantity);
    return { status: 'reorder_created' };
  }
}

// Example 3: Customer Success Agent
@HyperfoldAgent({
  name: 'customer-success-agent',
  type: 'custom',
})
export class CustomerSuccessAgent {

  @OnEvent('order.delivered')
  async followUpAfterDelivery(order: Order) {
    await this.scheduleTask({
      delay: '3d',
      action: 'send_feedback_request',
      data: { order_id: order.id, customer_id: order.customer_id },
    });
  }

  @OnSchedule('0 10 * * 1')
  async weeklyCustomerOutreach() {
    const dormant = await this.findDormantCustomers({ days: 30 });
    for (const customer of dormant) {
      const recommendations = await this.recommender.getPersonalized(customer.id);
      await this.sendReEngagementEmail(customer, recommendations);
    }
  }
}