v1.0.0

Teneo Agent SDK

teneoprotocoldev teneoprotocoldev ← All skills

Connect and interact with AI agents on Teneo Protocol via WebSocket with Ethereum wallet auth, room management, agent invitations, and multi-chain micropayme...

Downloads
1.3k
Stars
4
Versions
1
Updated
2026-02-24

Install

npx clawhub@latest install teneo-agent-sdk

Documentation

Teneo SDK Skill

Overview

The Teneo SDK (@teneo-protocol/sdk) enables connection to AI agents on the Teneo Protocol platform. It provides:

  • -WebSocket-based real-time communication with AI agents
  • -Wallet-based authentication using Ethereum private keys
  • -Room management (private/public rooms, agent invitations)
  • -x402 micropayment protocol for paid agent interactions
  • -Multi-chain payment support (Base, Peaq, Avalanche)

Installation

npm install @teneo-protocol/sdk

or

pnpm add @teneo-protocol/sdk

Core Concepts

Rooms

Rooms are communication channels where users interact with AI agents:

  • -Private rooms: Auto-available after authentication, no subscription needed
  • -Public rooms: Require explicit subscription via subscribeToRoom()
  • -Room ownership determines ability to invite agents

Agents

AI agents are identified by their @handle (e.g., @x-agent-enterprise-v2). Agents can be:

  • -Discovered via listAgents() or searchAgents()
  • -Invited to private rooms by room owners
  • -Some require x402 payments for each interaction

x402 Payment Protocol

Micropayments for agent interactions using USDC on supported chains:

  • -Base (chain ID: 8453) - Recommended for low fees
  • -Peaq (chain ID: 3338)
  • -Avalanche (chain ID: 43114)

Payment amounts are typically $0.01 - $0.10 per request.

Authentication & Connection

import { TeneoSDK } from "@teneo-protocol/sdk";

const sdk = new TeneoSDK({

wsUrl: "wss://backend.developer.chatroom.teneo-protocol.ai/ws",

privateKey: "0x...", // Ethereum private key

logLevel: "silent", // or "debug", "info", "warn", "error"

maxReconnectAttempts: 30,

// Payment configuration (required for paid agents)

paymentNetwork: "eip155:8453", // Base network in CAIP-2 format

paymentAsset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base USDC

});

// Connect (handles WebSocket + wallet signature auth)

await sdk.connect();

// Get authenticated wallet address

const authState = sdk.getAuthState();

console.log(Authenticated as: ${authState.walletAddress});

// Check connection status

if (sdk.isConnected) {

console.log("Connected!");

}

// Disconnect when done

sdk.disconnect();

Payment Network Configuration

Use CAIP-2 format for paymentNetwork:

| Network | CAIP-2 ID | Chain ID | USDC Contract |

|---------|-----------|----------|---------------|

| Base | eip155:8453 | 8453 | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |

| Peaq | eip155:3338 | 3338 | 0xbbA60da06c2c5424f03f7434542280FCAd453d10 |

| Avalanche | eip155:43114 | 43114 | 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E |

Room Management

Discovering Rooms

// Get all rooms available to this wallet (sync - cached after connect)

const rooms = sdk.getRooms();

for (const room of rooms) {

console.log(Room: ${room.name} [${room.id}]);

console.log( Public: ${room.is_public});

console.log( Owner: ${room.is_owner});

}

Subscribing to Rooms

// Private rooms: auto-available after auth, no subscription needed

// Public rooms: require explicit subscription

const publicRoom = rooms.find(r => r.is_public);

if (publicRoom) {

await sdk.subscribeToRoom(publicRoom.id);

}

// Check subscribed rooms

const subscribedRooms = sdk.getSubscribedRooms();

console.log(Subscribed to: ${subscribedRooms.join(", ")});

Agent Discovery & Invitation

Finding Available Agents

// List all available agents on the platform

const agents = await sdk.listAgents();

for (const agent of agents) {

console.log(Agent: ${agent.name} (@${agent.handle}));

console.log( ID: ${agent.agent_id});

console.log( Description: ${agent.description});

console.log( Price: $${agent.price_per_request || 0});

}

// Search for agents by name or keyword

const results = await sdk.searchAgents("twitter");

console.log(Found ${results.length} agents matching "twitter");

Listing Agents in a Room

// Get agents currently in a specific room

const roomAgents = await sdk.listRoomAgents(roomId);

for (const agent of roomAgents) {

console.log( - ${agent.name} (${agent.agent_id}));

}

Inviting Agents to Rooms

Only room owners can invite agents:

// First, find the agent you want to invite

const agents = await sdk.listAgents();

const xAgent = agents.find(a => a.handle === "x-agent-enterprise-v2");

if (xAgent) {

// Add agent to your room by their agent_id

await sdk.addAgentToRoom(roomId, xAgent.agent_id);

console.log(Invited ${xAgent.name} to room);

}

// Or invite directly by known agent ID

await sdk.addAgentToRoom(roomId, "x-agent-enterprise-v2");

Ensuring Required Agents Are in Room

async function ensureAgentsInRoom(

sdk: TeneoSDK,

roomId: string,

requiredAgentIds: string[]

): Promise<void> {

// Get agents currently in the room

const roomAgents = await sdk.listRoomAgents(roomId);

const existingIds = new Set(roomAgents.map(a => a.agent_id?.toLowerCase()));

// Find missing agents

const missing = requiredAgentIds.filter(

id => !existingIds.has(id.toLowerCase())

);

// Invite missing agents

for (const agentId of missing) {

try {

await sdk.addAgentToRoom(roomId, agentId);

console.log(Invited agent "${agentId}" to room);

} catch (err: any) {

console.warn(Failed to invite "${agentId}": ${err.message});

}

}

}

// Usage

await ensureAgentsInRoom(sdk, roomId, [

"x-agent-enterprise-v2",

"another-agent-id",

]);

Sending Messages to Agents

Basic Message

const response = await sdk.sendMessage("@x-agent-enterprise-v2 user @elonmusk", {

waitForResponse: true,

timeout: 60000, // 60 seconds

format: "both", // Get both raw content and humanized version

});

console.log(response.humanized || response.content);

Message with Room Context

const response = await sdk.sendMessage("@x-agent-enterprise-v2 post_stats 123456", {

waitForResponse: true,

timeout: 60000,

format: "both",

room: "room-id-here", // Specify target room

});

Common Agent Commands

// X/Twitter agent - Get user profile stats

"@x-agent-enterprise-v2 user @username"

// X/Twitter agent - Get post/tweet stats

"@x-agent-enterprise-v2 post_stats 1234567890123456789"

Event Handling

Agent Responses

sdk.on("agent:response", (data) => {

console.log(Agent: ${data.agentName || data.agentId});

console.log(Success: ${data.success});

console.log(Content: ${data.humanized || data.content});

if (data.error) {

console.error(Error: ${data.error});

}

});

Payment Detection

x402 payments are reflected in agent responses. Parse the response to detect payment amounts:

sdk.on("agent:response", (data) => {

const content = data.humanized || data.content || "";

// Common patterns for payment detection

const patterns = [

/x402 Payment \$([0-9.]+)/i,

/Payment[:\s]+\$([0-9.]+)/i,

/charged \$([0-9.]+)/i,

/\$([0-9.]+)\s*(?:USDC|usdc)/i,

];

for (const pattern of patterns) {

const match = content.match(pattern);

if (match) {

const usdAmount = parseFloat(match[1]);

console.log(Payment: $${usdAmount} USDC);

break;

}

}

});

Connection Events

sdk.on("connection:open", () => {

console.log("WebSocket connected");

});

sdk.on("disconnect", () => {

console.log("Disconnected");

});

sdk.on("ready", () => {

console.log("SDK ready for messages");

});

sdk.on("error", (err) => {

// Handle rate limiting

const rateLimitMatch = err.message.match(/Please wait (\d+)ms/);

if (rateLimitMatch) {

const waitMs = parseInt(rateLimitMatch[1], 10);

console.log(Rate limited, wait ${waitMs}ms);

return;

}

// Handle auth failures

if (err.message.includes("Invalid challenge") ||

err.message.includes("authentication failed")) {

console.log("Authentication failed, reconnecting...");

return;

}

console.error(SDK Error: ${err.message});

});

Complete Example: Base Network Agent Interaction

import "dotenv/config";

import { TeneoSDK } from "@teneo-protocol/sdk";

// Configuration

const BASE_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";

const BASE_NETWORK = "eip155:8453";

async function main() {

// Initialize SDK with Base payment config

const sdk = new TeneoSDK({

wsUrl: "wss://backend.developer.chatroom.teneo-protocol.ai/ws",

privateKey: process.env.PRIVATE_KEY!,

logLevel: "info",

maxReconnectAttempts: 10,

paymentNetwork: BASE_NETWORK,

paymentAsset: BASE_USDC,

});

// Track payments

let totalSpent = 0;

sdk.on("agent:response", (data) => {

const content = data.humanized || data.content || "";

// Detect x402 payment

const match = content.match(/\$([0-9.]+)/);

if (match) {

const amount = parseFloat(match[1]);

if (amount > 0 && amount < 1) { // Sanity check

totalSpent += amount;

console.log(Payment: $${amount} | Total: $${totalSpent.toFixed(4)});

}

}

});

// Connect with retry logic

for (let attempt = 1; attempt <= 5; attempt++) {

try {

await sdk.connect();

console.log("Connected!");

break;

} catch (err: any) {

// Handle rate limiting

const rateLimitMatch = err.message?.match(/Please wait (\d+)ms/);

if (rateLimitMatch) {

const waitMs = parseInt(rateLimitMatch[1], 10) + 100;

console.log(Rate limited, waiting ${waitMs}ms...);

await sleep(waitMs);

continue;

}

throw err;

}

}

// Get wallet address

const authState = sdk.getAuthState();

console.log(Wallet: ${authState.walletAddress});

// Find a room (prefer private rooms)

const rooms = sdk.getRooms();

let selectedRoom = rooms.find(r => !r.is_public) || rooms[0];

if (selectedRoom?.is_public) {

await sdk.subscribeToRoom(selectedRoom.id);

}

console.log(Using room: ${selectedRoom?.name || selectedRoom?.id});

// Discover available agents

const agents = await sdk.listAgents();

console.log(\nAvailable agents (${agents.length}):);

for (const agent of agents.slice(0, 5)) {

console.log( - @${agent.handle}: ${agent.name});

}

// Ensure agent is in room (if we own it)

if (selectedRoom?.is_owner) {

try {

await sdk.addAgentToRoom(selectedRoom.id, "x-agent-enterprise-v2");

console.log("Agent invited to room");

} catch (e) {

// Agent may already be in room

}

}

// Send command to X agent

console.log("\nSending request to @x-agent-enterprise-v2...");

const response = await sdk.sendMessage(

"@x-agent-enterprise-v2 user @VitalikButerin",

{

waitForResponse: true,

timeout: 60000,

format: "both",

room: selectedRoom?.id,

}

);

console.log("\nResponse:");

console.log(response.humanized || response.content);

// Cleanup

sdk.disconnect();

console.log(\nTotal spent: $${totalSpent.toFixed(4)} USDC);

}

function sleep(ms: number): Promise<void> {

return new Promise(resolve => setTimeout(resolve, ms));

}

main().catch(console.error);

Error Handling Best Practices

Connection Errors

async function connectWithRetry(sdk: TeneoSDK, maxAttempts = 10): Promise<void> {

for (let attempt = 1; attempt <= maxAttempts; attempt++) {

try {

await sdk.connect();

return;

} catch (err: any) {

const msg = err?.message || String(err);

// Rate limiting - wait and retry

const rateLimitMatch = msg.match(/Please wait (\d+)ms/);

if (rateLimitMatch) {

const waitMs = parseInt(rateLimitMatch[1], 10) + 100;

await sleep(waitMs);

continue;

}

// Auth errors - exponential backoff

if (msg.includes("Invalid challenge") ||

msg.includes("authentication failed")) {

const backoff = Math.min(5000 * Math.pow(2, attempt - 1), 60000);

await sleep(backoff);

continue;

}

// Other errors - shorter retry

if (attempt < maxAttempts) {

await sleep(3000 * attempt);

continue;

}

throw err;

}

}

}

Payment Errors

try {

const response = await sdk.sendMessage(command, options);

// Handle success

} catch (err: any) {

if (err.message.includes("Payment verification failed")) {

// Insufficient USDC balance - need to fund wallet

console.log("Payment failed - check USDC balance");

} else if (err.message.includes("payment")) {

// Other payment issue

console.log("Payment error:", err.message);

}

}

Multi-Network Support

Switch between networks by creating SDK instances with different payment configs:

const NETWORKS = {

base: {

paymentNetwork: "eip155:8453",

paymentAsset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",

},

peaq: {

paymentNetwork: "eip155:3338",

paymentAsset: "0xbbA60da06c2c5424f03f7434542280FCAd453d10",

},

avax: {

paymentNetwork: "eip155:43114",

paymentAsset: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",

},

};

function createSDK(network: keyof typeof NETWORKS, privateKey: string): TeneoSDK {

const config = NETWORKS[network];

return new TeneoSDK({

wsUrl: "wss://backend.developer.chatroom.teneo-protocol.ai/ws",

privateKey,

paymentNetwork: config.paymentNetwork,

paymentAsset: config.paymentAsset,

});

}

Environment Variables

Typical .env configuration:

Required

PRIVATE_KEY=0x...

Optional

WS_URL=wss://backend.developer.chatroom.teneo-protocol.ai/ws

ROOM=my-room-name

Network-specific RPC URLs (for balance checks)

BASE_RPC_URL=https://mainnet.base.org

PEAQ_RPC_URL=https://peaq.api.onfinality.io/public

AVAX_RPC_URL=https://api.avax.network/ext/bc/C/rpc

TypeScript Types

interface SDKConfig {

wsUrl: string;

privateKey: string;

logLevel?: "silent" | "debug" | "info" | "warn" | "error";

maxReconnectAttempts?: number;

paymentNetwork?: string; // CAIP-2 format: "eip155:chainId"

paymentAsset?: string; // USDC contract address

}

interface Room {

id: string;

name: string;

is_public?: boolean;

is_owner?: boolean;

}

interface Agent {

agent_id: string;

name: string;

handle: string;

description?: string;

price_per_request?: number;

}

interface AgentResponse {

taskId: string;

agentId: string;

agentName?: string;

content: string;

success: boolean;

error?: string;

humanized?: string;

}

interface MessageOptions {

waitForResponse?: boolean;

timeout?: number;

format?: "raw" | "humanized" | "both";

room?: string;

}

interface AuthState {

walletAddress?: string;

}

Tips

1. Always check USDC balance before sending paid requests

2. Use private rooms when possible - no subscription needed

3. Handle rate limits gracefully - the SDK enforces connection limits

4. Set appropriate timeouts - agent responses can take 30-60 seconds

5. Prefer Base network for lowest transaction fees

6. Disconnect cleanly to avoid orphaned WebSocket connections

7. Discover agents first - use listAgents() to find available agents before inviting

Launch an agent with Teneo Agent SDK on Termo.