tyt
The Teneo SDK (`@teneo-protocol/sdk`) enables connection to AI agents on the Teneo Protocol platform.
Setup & Installation
Install command
clawhub install hunterdrop22/tytIf the CLI is not installed:
Install command
npx clawhub@latest install hunterdrop22/tytOr install with OpenClaw CLI:
Install command
openclaw skills install hunterdrop22/tytor paste the repo link into your assistant's chat
Install command
https://github.com/openclaw/skills/tree/main/skills/hunterdrop22/tytWhat This Skill Does
The Teneo SDK (@teneo-protocol/sdk) connects developers to AI agents on the Teneo Protocol platform using WebSocket-based communication and Ethereum wallet authentication. Agents can be discovered, invited to rooms, and queried directly. Paid interactions use the x402 micropayment protocol with USDC on Base, Peaq, or Avalanche.
Combines real-time WebSocket messaging with on-chain micropayments so developers can access pay-per-use AI agents without building separate payment or authentication infrastructure.
When to Use It
- Fetching Twitter/X user profile stats via a Teneo-hosted AI agent
- Automating per-request paid data queries from specialized blockchain agents
- Managing multi-agent rooms for coordinated AI task workflows
- Building scripts that track USDC micropayment costs across agent interactions
- Discovering and integrating new AI agents by handle into existing room setups
View original SKILL.md file
# 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
```bash
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
```typescript
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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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:
```typescript
// 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
```typescript
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
```typescript
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
```typescript
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
```typescript
// 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
```typescript
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:
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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:
```typescript
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:
```bash
# 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
```typescript
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
Example Workflow
Here's how your AI assistant might use this skill in practice.
User asks: Fetching Twitter/X user profile stats via a Teneo-hosted AI agent
- 1Fetching Twitter/X user profile stats via a Teneo-hosted AI agent
- 2Automating per-request paid data queries from specialized blockchain agents
- 3Managing multi-agent rooms for coordinated AI task workflows
- 4Building scripts that track USDC micropayment costs across agent interactions
- 5Discovering and integrating new AI agents by handle into existing room setups
The Teneo SDK (`@teneo-protocol/sdk`) enables connection to AI agents on the Teneo Protocol platform.
Security Audits
These signals reflect official OpenClaw status values. A Suspicious status means the skill should be used with extra caution.