Skill: kryptopaid/build-warden-agent (version 1.0.0)

--- name: warden-agent-builder description: "Build original LangGraph agents for Warden Protocol and prepare them for publishing in Warden Studio. Use this skill when users want to: (1) Create new Warden agents (not community examples), (2) Build LangGraph-based crypto/Web3 agents, (3) Deploy agents via LangSmith Deployments or custom infra, (4) Participate in the Warden Agent Builder Incentive Programme (open to OpenClaw agents), or (5) Integrate with Warden Studio for Agent Hub publishing." --- # Warden Agent Builder Build and deploy LangGraph agents for Warden Protocol's Agentic Wallet ecosystem. ## ⚠️ IMPORTANT: About Example Agents The Warden community repository contains **example agents for learning**, not templates to recreate: - **Weather Agent** - Study this to learn simple data fetching patterns - **CoinGecko Agent** - Study this to learn Schema-Guided Reasoning (SGR) - **Portfolio Agent** - Study this to learn complex multi-source integration **DO NOT BUILD THESE AGENTS** - they already exist. Instead: 1. **Study** their code to understand patterns 2. **Learn** from their architecture and workflows 3. **Build** something NEW and original for the incentive programme Your agent must be **unique and solve a different problem** to be eligible for the incentive programme. ## Overview Warden Protocol is an "Agentic Wallet for the Do-It-For-Me economy" with an active Agent Builder Incentive Programme open to OpenClaw agents that deploy to Warden. All agents must be LangGraph-based and API-accessible. **Key Resources:** - Community Agents Repository: https://github.com/warden-protocol/community-agents - Documentation: https://docs.wardenprotocol.org - Discord: #developers channel for support ## Requirements Checklist Before building, ensure your agent meets these mandatory requirements: ✓ **Framework**: Built with LangGraph (TypeScript or Python) ✓ **Deployment**: LangSmith Deployments OR custom infrastructure ✓ **Access**: API-accessible (no UI required - Warden provides UI) ✓ **Isolation**: One agent per LangGraph instance ✓ **Security Limitations** (Phase 1): - Cannot access user wallets - Cannot store data on Warden infrastructure ✓ **Functionality**: Can implement any workflow: - Web3/Web2 automation - API integrations - Database connections - External tool interactions ## Understanding the Example Agents The community-agents repository contains **reference examples** to learn from, NOT templates to recreate: ### Example Agent 1: LangGraph Quick Start (Study for Basics) **Location**: `agents/langgraph-quick-start` (TypeScript) or `agents/langgraph-quick-start-py` (Python) **Learn**: LangGraph fundamentals, minimal agent structure **Study**: Single-node chatbot with OpenAI integration ```bash git clone https://github.com/warden-protocol/community-agents.git cd community-agents/agents/langgraph-quick-start ``` ### Example Agent 2: Weather Agent (Study for Structure) **Location**: `agents/weather-agent` **Learn**: Simple data fetching, API integration, user-friendly responses **Study**: - How to fetch data from external APIs (WeatherAPI) - Processing and formatting results - Clear scope and structure **⚠️ DO NOT BUILD**: This already exists. Study it, then build something NEW. ### Example Agent 3: CoinGecko Agent (Study for SGR Pattern) **Location**: `agents/coingecko-agent` **Learn**: Schema-Guided Reasoning, complex workflows **Study**: - 5-step SGR workflow: Validate → Extract → Fetch → Validate → Analyze - Comparative analysis patterns - Error handling and data validation **⚠️ DO NOT BUILD**: This already exists. Study the pattern, apply to new use cases. ### Example Agent 4: Portfolio Analysis Agent (Study for Advanced Patterns) **Location**: `agents/portfolio-agent` **Learn**: Multi-source data synthesis, production architecture **Study**: - Integrating multiple APIs (CoinGecko + Alchemy) - Multi-chain support (EVM and Solana) - Complex SGR workflows - Comprehensive reporting **⚠️ DO NOT BUILD**: This already exists. Study the architecture for your own complex agent. ## IMPORTANT: Build Something NEW These examples exist to teach patterns and best practices. For the incentive programme, you MUST create an **original, unique agent** that solves a different problem. Do NOT simply recreate the Weather Agent, CoinGecko Agent, or Portfolio Agent. ## Building Your Original Agent ### Step 1: Study Examples and Choose Your Approach **DO NOT clone an example to modify it.** Instead: 1. **Study the examples** to understand patterns: - Simple data fetching → Study Weather Agent - Complex analysis → Study CoinGecko Agent - Multi-source synthesis → Study Portfolio Agent 2. **Identify YOUR unique use case**: - What problem will your agent solve? - What APIs or data sources will it use? - What makes it different from existing agents? 3. **Plan your agent's workflow**: - Simple request-response? - Schema-Guided Reasoning (SGR)? - Multi-step analysis? ### Step 2: Initialize Your NEW Agent Use the initialization script to create a fresh project: ```bash # Create your unique agent python scripts/init-agent.py my-unique-agent \ --template typescript \ --description "Description of what YOUR agent does" # Navigate to project cd my-unique-agent # Install dependencies npm install # TypeScript # OR pip install -r requirements.txt # Python ``` This creates a clean starting point, not a copy of existing agents. ### Step 3: Understand LangGraph Agent Structure Every LangGraph agent follows this basic structure: ``` your-agent/ ├── src/ │ ├── agent.ts/py # Main agent logic (YOUR CODE) │ ├── graph.ts/py # LangGraph workflow definition (YOUR CODE) │ └── tools.ts/py # Tool implementations (YOUR CODE) ├── package.json / requirements.txt ├── langgraph.json # LangGraph configuration └── README.md ``` **Key files to implement:** - `graph.ts/py` - Define your workflow (validate → process → respond) - `agent.ts/py` - Implement your core logic - `tools.ts/py` - Integrate external APIs specific to YOUR agent's purpose ### Step 4: Implement Your Custom Agent Logic **Study patterns from examples, apply to YOUR use case:** **If building a simple data fetcher** (like Weather Agent pattern): ```typescript // Define workflow const workflow = new StateGraph({ channels: agentState }) .addNode("fetch", fetchYourData) // YOUR API .addNode("process", processYourData) // YOUR logic .addNode("respond", generateResponse); workflow .addEdge(START, "fetch") .addEdge("fetch", "process") .addEdge("process", "respond") .addEdge("respond", END); ``` **If building complex analysis** (like CoinGecko Agent pattern - SGR): ```typescript // Define 5-step SGR workflow const workflow = new StateGraph({ channels: agentState }) .addNode("validate", validateYourInput) // YOUR validation .addNode("extract", extractYourParams) // YOUR extraction .addNode("fetch", fetchYourData) // YOUR APIs .addNode("analyze", analyzeYourData) // YOUR analysis .addNode("generate", generateYourResponse); // YOUR formatting workflow .addEdge(START, "validate") .addEdge("validate", "extract") .addEdge("extract", "fetch") .addEdge("fetch", "analyze") .addEdge("analyze", "generate") .addEdge("generate", END); ``` **Key Principles:** 1. Keep workflows linear and predictable 2. Validate inputs at each stage 3. Handle errors gracefully 4. Use OpenAI for natural language generation 5. Structure responses consistently **CRITICAL**: This should be YOUR implementation solving YOUR problem, not a copy of the example agents. ### Step 5: Configure Environment Create `.env` file: ```bash # Required OPENAI_API_KEY=your_openai_key # Required for LangSmith Deployments (cloud) LANGSMITH_API_KEY=your_langsmith_key # Optional - based on your tools WEATHER_API_KEY=your_weather_key COINGECKO_API_KEY=your_coingecko_key ALCHEMY_API_KEY=your_alchemy_key ``` **Getting LangSmith API Key:** 1. Create account at https://smith.langchain.com 2. Navigate to Settings → API Keys 3. Create new API key 4. Add to `.env` file Update `langgraph.json`: ```json { "agent_id": "[YOUR-AGENT-NAME]", "python_version": "3.11", // or omit for TypeScript "dependencies": ["."], "graphs": { "agent": "./src/graph.ts" // or .py }, "env": ".env" } ``` ### Step 6: Test Locally ```bash # TypeScript npm run dev # Python langgraph dev ``` Test your agent's API: ```bash curl -X POST http://localhost:8000/invoke \ -H "Content-Type: application/json" \ -d '{"input": "test query"}' ``` ## Deployment Options ### Option 1: LangSmith Deployments (Recommended) **Pros**: Fastest, simplest, managed infrastructure **Requirements**: LangSmith API key **Steps**: ```bash 1. Push your agent repository to GitHub. 2. Create a new deployment in LangSmith Deployments. 3. Connect the repo, set environment variables, and deploy. ``` Your agent receives: - API endpoint URL - Automatic authentication (uses your LangSmith API key) - Automatic scaling and monitoring **Authentication for API calls:** When calling your deployed agent, include your LangSmith API key: ```bash curl AGENT_URL/runs/wait \ --request POST \ --header 'Content-Type: application/json' \ --header 'x-[REDACTED_SECRET]' \ --data '{ "assistant_id": "[YOUR-AGENT-ID]", "input": { "messages": [{"role": "user", "content": "test query"}] } }' ``` ### Option 2: Self-Hosted Infrastructure **Pros**: Full control over runtime **Requirements**: - Docker container hosting - Exposed API endpoint - SSL certificate (HTTPS) - Monitoring and logging **Basic Docker Setup**: ```dockerfile FROM node:18 WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 8000 CMD ["npm", "start"] ``` Deploy and note your: - API URL: `https://your-domain.com/agent` - API Key: Generated for authentication ## Register with Warden Studio Once your agent is deployed and reachable via HTTPS, register it in Warden Studio: 1. **Provide API Details**: - API URL - API key 2. **Add Metadata**: - Agent name - Description - Skills/capabilities list - Avatar image 3. **Publish**: Agent appears in Warden's Agent Hub for millions of users No additional setup required - your API-accessible agent is ready! **Next step (separate skill):** If the user asks to publish in Warden Studio or needs guided UI steps, switch to the OpenClaw skill **"Deploy Agent on Warden Studio"**: https://www.clawhub.ai/Kryptopaid/warden-studio-deploy ## Best Practices ### 1. Agent Design - Study the Weather Agent structure to learn patterns - Use Schema-Guided Reasoning for complex workflows - Keep responses concise and actionable - Handle API failures gracefully - Validate all inputs ### 2. API Integration - Use environment variables for API keys - Implement rate limiting - Cache responses when appropriate - Log errors for debugging - Return structured JSON responses ### 3. Testing - Test locally before deploying - Verify all API endpoints work - Test edge cases and errors - Ensure responses are user-friendly - Validate against Warden requirements ### 4. Documentation - Write clear README with: - Agent purpose and capabilities - Required API keys - Setup instructions - Example queries - Known limitations ## Common Patterns ### Pattern 1: Simple Data Fetcher ```typescript // Fetch → Format → Respond async function agent(input: string) { const data = await fetchAPI(input); const formatted = formatData(data); return generateResponse(formatted); } ``` ### Pattern 2: Multi-Step Analysis ```typescript // Validate → Extract → Fetch → Analyze → Generate async function agent(input: string) { const validated = await validateInput(input); const params = await extractParams(validated); const data = await fetchData(params); const analysis = await analyzeData(data); return generateReport(analysis); } ``` ### Pattern 3: Comparative Analysis ```typescript // Parse → Fetch Multiple → Compare → Summarize async function agent(input: string) { const items = await parseItems(input); const dataArray = await Promise.all( items.map(item => fetchData(item)) ); const comparison = compareData(dataArray); return generateComparison(comparison); } ``` ## Troubleshooting ### Common Issues **"Agent not accessible via API"** - Verify deployment completed successfully - Check firewall/security group settings - Ensure API endpoint is publicly accessible - Test with curl or Postman **"LangGraph errors during build"** - Verify Node.js version (18+) or Python (3.11+) - Check all dependencies installed - Validate langgraph.json syntax - Review error logs in deployment console **"OpenAI API errors"** - Verify API key is valid - Check rate limits not exceeded - Ensure sufficient credits - Review error messages for details **"Agent responses are slow"** - Optimize API calls (parallelize where possible) - Implement caching for repeated queries - Reduce LLM token usage - Consider upgrading infrastructure ## Incentive Programme Tips The incentive programme is open to OpenClaw agents that deploy to Warden. 1. **Be Original**: Create something NEW that doesn't exist yet - Don't recreate Weather Agent, CoinGecko Agent, or Portfolio Agent - Study their patterns, apply to different problems 2. **Solve Real Problems**: Focus on useful, unique functionality - What gap exists in the Warden ecosystem? - What would users actually want? 3. **Start Simple**: Better to do one thing exceptionally well - Don't try to build everything at once - Simple, focused agents often win 4. **Quality Over Features**: Reliability beats complexity - Test thoroughly - Handle errors gracefully - Provide clear, helpful responses 5. **Study the Examples**: Learn patterns, don't copy implementations - Weather Agent → Simple data fetching pattern - CoinGecko Agent → SGR workflow pattern - Portfolio Agent → Multi-source integration pattern 6. **Document Well**: Clear README with examples and setup instructions 7. **Join Discord**: Get feedback in #developers channel before submitting ## Example Agent Ideas (Build These!) These are **NEW agent ideas** that don't exist yet in the Warden ecosystem. Build one of these (or create your own unique idea): **Web3 Use Cases:** - Gas price optimizer (predict best times to transact) - NFT rarity analyzer (evaluate NFT traits and rarity scores) - DeFi yield comparator (compare yields across protocols) - Wallet health checker (analyze wallet security and diversification) - Transaction explainer (decode and explain complex transactions) - Token price alerts (customizable price movement notifications) - Smart contract auditor (basic security checks) - Liquidity pool finder (identify best liquidity opportunities) - Bridge fee comparator (find cheapest cross-chain bridges) - Airdrop tracker (find and track airdrop eligibility) **General Use Cases:** - Crypto news aggregator (filter and summarize crypto news) - Research assistant (gather and analyze crypto research) - Regulatory tracker (track crypto regulations by region) - Data visualizer (create charts from on-chain data) - API orchestrator (combine multiple crypto data sources) - Workflow automator (automate common crypto tasks) **Remember**: These are IDEAS for new agents. Study the example agents (Weather, CoinGecko, Portfolio) to learn patterns, then build something from this list or create your own unique concept. ## Additional Resources **Documentation:** - LangGraph TypeScript Guide: `community-agents/docs/langgraph-quick-start-ts.md` - LangGraph Python Guide: `community-agents/docs/langgraph-quick-start-py.md` - Deployment Guide: `community-agents/docs/deploy.md` **Example Agents:** - Weather Agent README: `agents/weather-agent/README.md` - CoinGecko Agent README: `agents/coingecko-agent/README.md` - Portfolio Agent README: `agents/portfolio-agent/README.md` **Support:** - Discord: #developers channel - GitHub Issues: https://github.com/warden-protocol/community-agents/issues - Documentation: https://docs.wardenprotocol.org ## Quick Reference Commands ```bash # Study example agents (DON'T BUILD THESE) git clone https://github.com/warden-protocol/community-agents.git cd community-agents/agents/weather-agent # Study the code cd community-agents/agents/coingecko-agent # Study the patterns # Create YOUR new agent python scripts/init-agent.py my-unique-agent \ --template typescript \ --description "YOUR unique agent description" # Install dependencies (TypeScript) npm install # Install dependencies (Python) pip install -r requirements.txt # Test locally npm run dev # or: langgraph dev # Deploy (LangSmith Deployments) # Use the LangSmith Deployments UI after pushing to GitHub # Build Docker image (for self-hosting) docker build -t my-warden-agent . # Run Docker container docker run -p 8000:8000 my-warden-agent ``` ## Success Checklist Before submitting to incentive programme: - [ ] Agent built with LangGraph - [ ] API accessible and tested - [ ] One agent per LangGraph instance - [ ] No wallet access or data storage (Phase 1) - [ ] Clear documentation in README - [ ] Environment variables properly configured - [ ] Error handling implemented - [ ] Tested with various inputs - [ ] Unique and useful functionality - [ ] Ready for Warden Studio registration

path: references/deployment-guide.md
content: # API Integration & Deployment Guide

Detailed guide for integrating APIs and deploying Warden agents to production.

## API Integration Patterns

### Environment Configuration

```typescript
// config.ts
import dotenv from 'dotenv';

dotenv.config();

export const config = {
  openai: {
    [REDACTED_SECRET],
    model: process.env.OPENAI_MODEL || 'gpt-4',
    temperature: parseFloat(process.env.OPENAI_TEMPERATURE || '0.7')
  },
  apis: {
    coingecko: {
      key: process.env.COINGECKO_API_KEY,
      baseUrl: 'https://api.coingecko.com/api/v3'
    },
    alchemy: {
      key: process.env.ALCHEMY_API_KEY!,
      network: process.env.ALCHEMY_NETWORK || 'mainnet'
    },
    weather: {
      key: process.env.WEATHER_API_KEY!,
      baseUrl: 'https://api.weatherapi.com/v1'
    }
  },
  server: {
    port: parseInt(process.env.PORT || '8000'),
    host: process.env.HOST || '0.0.0.0'
  }
};

// Validation
function validateConfig() {
  const required = ['OPENAI_API_KEY'];
  const missing = required.filter(key => !process.env[key]);
  
  if (missing.length > 0) {
    throw new Error(`Missing required env vars: ${missing.join(', ')}`);
  }
}

validateConfig();
```

### API Client Pattern

```typescript
// api-client.ts
import axios, { AxiosInstance, AxiosError } from 'axios';

export class APIClient {
  private client: AxiosInstance;
  private retryCount = 3;
  private retryDelay = 1000;
  
  constructor(baseURL: string, apiKey?: string) {
    this.client = axios.create({
      baseURL,
      timeout: 10000,
      headers: apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {}
    });
    
    this.setupInterceptors();
  }
  
  private setupInterceptors() {
    // Request interceptor
    this.client.interceptors.request.use(
      (config) => {
        console.log(`[API] ${config.method?.toUpperCase()} ${config.url}`);
        return config;
      },
      (error) => Promise.reject(error)
    );
    
    // Response interceptor
    this.client.interceptors.response.use(
      (response) => response,
      async (error: AxiosError) => {
        return this.handleError(error);
      }
    );
  }
  
  private async handleError(error: AxiosError) {
    const config = error.config!;
    
    // Retry on rate limit
    if (error.response?.status === 429) {
      const retryAfter = parseInt(
        error.response.headers['retry-after'] || '60'
      );
      
      await this.delay(retryAfter * 1000);
      return this.client.request(config);
    }
    
    // Retry on server errors
    if (error.response?.status && error.response.status >= 500) {
      const retries = (config as any).__retries || 0;
      
      if (retries < this.retryCount) {
        (config as any).__retries = retries + 1;
        await this.delay(this.retryDelay * (retries + 1));
        return this.client.request(config);
      }
    }
    
    throw this.formatError(error);
  }
  
  private formatError(error: AxiosError): Error {
    if (error.response) {
      return new Error(
        `API Error ${error.response.status}: ${error.response.data}`
      );
    } else if (error.request) {
      return new Error('No response from API');
    } else {
      return new Error(`Request failed: ${error.message}`);
    }
  }
  
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
  
  async get<T>(url: string, params?: any): Promise<T> {
    const response = await this.client.get<T>(url, { params });
    return response.data;
  }
  
  async post<T>(url: string, data?: any): Promise<T> {
    const response = await this.client.post<T>(url, data);
    return response.data;
  }
}
```

### Rate Limiting

```typescript
// rate-limiter.ts
export class RateLimiter {
  private requests: number[] = [];
  private maxRequests: number;
  private windowMs: number;
  
  constructor(maxRequests: number, windowMs: number) {
    this.maxRequests = maxRequests;
    this.windowMs = windowMs;
  }
  
  async acquire(): Promise<void> {
    const now = Date.now();
    
    // Remove old requests outside window
    this.requests = this.requests.filter(
      time => now - time < this.windowMs
    );
    
    // Check if we're at limit
    if (this.requests.length >= this.maxRequests) {
      const oldestRequest = this.requests[0];
      const waitTime = this.windowMs - (now - oldestRequest);
      await this.delay(waitTime);
      return this.acquire();
    }
    
    this.requests.push(now);
  }
  
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// Usage
const limiter = new RateLimiter(5, 1000); // 5 requests per second

async function fetchData(url: string) {
  await limiter.acquire();
  return fetch(url);
}
```

### Caching Layer

```typescript
// cache.ts
import NodeCache from 'node-cache';

export class Cache {
  private cache: NodeCache;
  
  constructor(ttlSeconds: number = 300) {
    this.cache = new NodeCache({
      stdTTL: ttlSeconds,
      checkperiod: ttlSeconds * 0.2
    });
  }
  
  get<T>(key: string): T | undefined {
    return this.cache.get<T>(key);
  }
  
  set<T>(key: string, value: T, ttl?: number): boolean {
    return this.cache.set(key, value, ttl || 0);
  }
  
  del(key: string): number {
    return this.cache.del(key);
  }
  
  flush(): void {
    this.cache.flushAll();
  }
  
  async wrap<T>(
    key: string,
    fetcher: () => Promise<T>,
    ttl?: number
  ): Promise<T> {
    const cached = this.get<T>(key);
    if (cached !== undefined) {
      return cached;
    }
    
    const fresh = await fetcher();
    this.set(key, fresh, ttl);
    return fresh;
  }
}

// Usage
const cache = new Cache(300); // 5 minute TTL

async function getCoinPrice(coinId: string): Promise<number> {
  return cache.wrap(
    `price:${coinId}`,
    async () => {
      const data = await api.get(`/coins/${coinId}`);
      return data.market_data.current_price.usd;
    },
    60 // Override: 1 minute TTL for prices
  );
}
```

## Deployment Strategies

### LangSmith Deployments (Cloud)

**Prerequisites:**
- LangSmith account (https://smith.langchain.com)
- LangSmith API key
- OpenAI API key

**Getting LangSmith API Key:**
```bash
# 1. Sign up at https://smith.langchain.com
# 2. Navigate to Settings → API Keys
# 3. Click "Create API Key"
# 4. Copy the key (starts with [YOUR-LANGSMITH-API-KEY])
# 5. Store securely - it won't be shown again
```

**Deployment Steps:**

1. Push your agent repository to GitHub.
2. Create a new deployment in LangSmith Deployments.
3. Connect the repo, set environment variables, and deploy.

**Environment Variables for Deployment:**

In LangSmith deployment settings, add:
```bash
OPENAI_API_KEY=[YOUR-OPENAI-API-KEY]
# Other API keys your agent needs
COINGECKO_API_KEY=...
ALCHEMY_API_KEY=...
```

**⚠️ Important**: Never commit API keys to Git. The LangSmith API key is used for:
- Authentication when calling your deployed agent
- Tracing and monitoring (optional)
- Access control

**Using Your Deployed Agent:**

All API calls require the `x-api-key` header with your LangSmith API key:

```bash
curl YOUR_AGENT_URL/runs/wait \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'x-[REDACTED_SECRET]' \
  --data '{
    "assistant_id": "[YOUR-AGENT-ID]",
    "input": {
      "messages": [
        {"role": "user", "content": "What can you do?"}
      ]
    }
  }'
```

### Docker Deployment

```dockerfile
# Dockerfile
FROM node:18-alpine

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci --only=production

# Copy source
COPY . .

# Build TypeScript
RUN npm run build

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js

EXPOSE 8000

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

```javascript
// healthcheck.js
const http = require('http');

const options = {
  hostname: 'localhost',
  port: 8000,
  path: '/health',
  method: 'GET',
  timeout: 2000
};

const req = http.request(options, (res) => {
  if (res.statusCode === 200) {
    process.exit(0);
  } else {
    process.exit(1);
  }
});

req.on('error', () => {
  process.exit(1);
});

req.on('timeout', () => {
  req.destroy();
  process.exit(1);
});

req.end();
```

### Docker Compose Setup

```yaml
# docker-compose.yml
version: '3.8'

services:
  agent:
    build: .
    ports:
      - "8000:8000"
    environment:
      - NODE_ENV=production
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - COINGECKO_API_KEY=${COINGECKO_API_KEY}
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    healthcheck:
      test: ["CMD", "node", "healthcheck.js"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 40s
```

### Kubernetes Deployment

```yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: warden-agent
spec:
  replicas: 3
  selector:
    matchLabels:
      app: warden-agent
  template:
    metadata:
      labels:
        app: warden-agent
    spec:
      containers:
      - name: agent
        image: your-registry/warden-agent:latest
        ports:
        - containerPort: 8000
        env:
        - name: OPENAI_API_KEY
          valueFrom:
            secretKeyRef:
              name: agent-secrets
              key: openai-api-key
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8000
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: warden-agent
spec:
  selector:
    app: warden-agent
  ports:
  - port: 80
    targetPort: 8000
  type: LoadBalancer
```

## Production Server Setup

### Express Server with Endpoints

```typescript
// server.ts
import express from 'express';
import helmet from 'helmet';
import cors from 'cors';
import { agent } from './agent';
import { config } from './config';

const app = express();

// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json());

// Request logging
app.use((req, res, next) => {
  console.log(`${new Date().toISOString()} ${req.method} ${req.path}`);
  next();
});

// Health check
app.get('/health', (req, res) => {
  res.json({ status: 'healthy', timestamp: new Date().toISOString() });
});

// Readiness check
app.get('/ready', async (req, res) => {
  try {
    // Check dependencies
    await checkDependencies();
    res.json({ status: 'ready' });
  } catch (error) {
    res.status(503).json({ status: 'not ready', error: error.message });
  }
});

// Main agent endpoint
app.post('/invoke', async (req, res) => {
  try {
    const { input } = req.body;
    
    if (!input) {
      return res.status(400).json({ error: 'Input required' });
    }
    
    const result = await agent.invoke({ input });
    res.json(result);
    
  } catch (error) {
    console.error('Agent error:', error);
    res.status(500).json({ 
      error: 'Internal error',
      message: error.message 
    });
  }
});

// Streaming endpoint
app.post('/stream', async (req, res) => {
  try {
    const { input } = req.body;
    
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');
    
    const stream = await agent.stream({ input });
    
    for await (const chunk of stream) {
      res.write(`data: ${JSON.stringify(chunk)}\n\n`);
    }
    
    res.end();
    
  } catch (error) {
    console.error('Streaming error:', error);
    res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
    res.end();
  }
});

// Error handler
app.use((error, req, res, next) => {
  console.error('Unhandled error:', error);
  res.status(500).json({ error: 'Internal server error' });
});

// Start server
const port = config.server.port;
app.listen(port, () => {
  console.log(`Agent server running on port ${port}`);
});

async function checkDependencies() {
  // Check external services
  const checks = [
    checkOpenAI(),
    checkExternalAPIs()
  ];
  
  await Promise.all(checks);
}

async function checkOpenAI() {
  // Verify OpenAI API key works
}

async function checkExternalAPIs() {
  // Verify external APIs accessible
}
```

## Monitoring & Logging

### Structured Logging

```typescript
// logger.ts
import winston from 'winston';

export const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  defaultMeta: { service: 'warden-agent' },
  transports: [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple()
      )
    }),
    new winston.transports.File({ 
      filename: 'logs/error.log', 
      level: 'error' 
    }),
    new winston.transports.File({ 
      filename: 'logs/combined.log' 
    })
  ]
});

// Usage
logger.info('Agent started', { port: 8000 });
logger.error('API call failed', { error: err.message, url });
logger.debug('Processing request', { input });
```

### Metrics Collection

```typescript
// metrics.ts
import { Counter, Histogram, register } from 'prom-client';

export const metrics = {
  requests: new Counter({
    name: 'agent_requests_total',
    help: 'Total number of agent requests',
    labelNames: ['status']
  }),
  
  duration: new Histogram({
    name: 'agent_request_duration_seconds',
    help: 'Agent request duration',
    buckets: [0.1, 0.5, 1, 2, 5, 10]
  }),
  
  errors: new Counter({
    name: 'agent_errors_total',
    help: 'Total number of errors',
    labelNames: ['type']
  })
};

// Metrics endpoint
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', register.contentType);
  res.end(await register.metrics());
});

// Usage
const end = metrics.duration.startTimer();
try {
  const result = await processRequest(input);
  metrics.requests.inc({ status: 'success' });
  return result;
} catch (error) {
  metrics.requests.inc({ status: 'error' });
  metrics.errors.inc({ type: error.name });
  throw error;
} finally {
  end();
}
```

## Security Best Practices

### API Key Management

```typescript
// Never commit API keys
// Use environment variables
// Rotate keys regularly
// Use different keys for dev/prod

// secrets-manager.ts
import { SecretsManager } from 'aws-sdk';

export class SecretManager {
  private client = new SecretsManager();
  
  async getSecret(secretName: string): Promise<string> {
    const result = await this.client
      .getSecretValue({ SecretId: secretName })
      .promise();
    
    return result.SecretString!;
  }
}

// Usage
const secretManager = new SecretManager();
const apiKey = await secretManager.getSecret('openai-api-key');
```

### Input Validation

```typescript
// validate.ts
import { z } from 'zod';

const requestSchema = z.object({
  input: z.string().min(1).max(1000),
  options: z.object({
    temperature: z.number().min(0).max(1).optional(),
    maxTokens: z.number().positive().optional()
  }).optional()
});

export function validateRequest(data: unknown) {
  return requestSchema.parse(data);
}

// Usage in endpoint
app.post('/invoke', async (req, res) => {
  try {
    const validated = validateRequest(req.body);
    const result = await agent.invoke(validated.input);
    res.json(result);
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({ errors: error.errors });
    } else {
      res.status(500).json({ error: 'Internal error' });
    }
  }
});
```

### Rate Limiting Middleware

```typescript
// rate-limit.ts
import rateLimit from 'express-rate-limit';

export const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // 100 requests per window
  message: 'Too many requests, please try again later',
  standardHeaders: true,
  legacyHeaders: false
});

// Apply to routes
app.use('/invoke', limiter);
app.use('/stream', limiter);
```

## CI/CD Pipeline

### GitHub Actions Workflow

```yaml
# .github/workflows/deploy.yml
name: Deploy Agent

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test
      
      - name: Lint
        run: npm run lint
  
  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Deploy to LangSmith Deployments
        run: |
          echo \"Deployments are created in the LangSmith Deployments UI\"
```

This comprehensive guide covers all aspects of API integration and deployment for Warden Protocol agents. Use these patterns as reference when building production-ready agents.


path: references/quick-reference.md
content: # Warden Agent Builder - Quick Reference

## 🎯 What This Skill Does

Helps build and deploy LangGraph agents for Warden Protocol's incentive programme (open to OpenClaw agents).

## ⚡ Quick Commands

```bash
# Study example agents (for learning only)
git clone https://github.com/warden-protocol/community-agents.git
cd community-agents/agents/weather-agent  # Study the code

# Initialize YOUR new agent
python scripts/init-agent.py my-unique-agent --template typescript

# Test YOUR deployed agent  
python scripts/test-agent.py https://api.example.com --api-key [YOUR-API-KEY]

# Deploy YOUR agent
# Use the LangSmith Deployments UI after pushing to GitHub
```

## 📋 Requirements Checklist

Must have:
- ✅ LangGraph framework (TypeScript or Python)
- ✅ API-accessible endpoint
- ✅ One agent per LangGraph instance
- ❌ No wallet access (Phase 1)
- ❌ No data storage on Warden (Phase 1)

## 🎨 Example Agents (Study These!)

| Agent | Level | Study For | ⚠️ Warning |
|----------|-------|---------|-----------|
| **Quick Start** | Beginner | LangGraph basics | OK to use as starting point |
| **Weather Agent** | Starter | Simple data fetching | DON'T REBUILD - already exists |
| **CoinGecko** | Intermediate | SGR pattern | DON'T REBUILD - study pattern only |
| **Portfolio** | Advanced | Multi-source integration | DON'T REBUILD - study architecture |

**These are REFERENCES, not templates. Build something NEW!**

## 🗂️ Skill Files Overview

```
warden-agent-builder/
├── SKILL.md                      # 👈 START HERE
├── references/
│   ├── langgraph-patterns.md     # Code patterns & examples
│   └── deployment-guide.md       # API & deployment details
├── scripts/
│   ├── init-agent.py            # Create new agent
│   └── test-agent.py            # Test agent API
└── assets/
    └── example-configs.md        # Config templates
```

## 🚀 Deployment Options

### LangSmith Deployments (Easiest)
- Push your repo to GitHub
- Create a deployment in LangSmith Deployments

### Self-Hosted
- Docker container
- HTTPS endpoint
- API key authentication

## 💡 Agent Ideas

**Web3:**
- Gas optimizer
- NFT analyzer
- DeFi yield finder
- Wallet health checker

**General:**
- News aggregator
- Research assistant
- Data visualizer

## 🔑 Key Patterns

### Simple Agent
```typescript
input → process → output
```

### Schema-Guided Reasoning (SGR)
```typescript
validate → extract → fetch → analyze → generate
```

### Comparative Analysis
```typescript
parse → fetch_all → compare → summarize
```

## 📊 Warden Studio Registration

Need to provide:
- Agent name & description
- API URL (HTTPS)
- API key
- Avatar image (512x512px)
- Capabilities list (3-5 items)

## 🎁 Incentive Programme

- **Requirements**: LangGraph, API access, unique functionality
- **Tip**: Study the Weather Agent structure (do not rebuild)
- **Support**: Discord #developers channel

## 📚 Resources

- **Repository**: github.com/warden-protocol/community-agents
- **Docs**: docs.wardenprotocol.org
- **Discord**: Join #developers channel
- **Examples**: All templates include READMEs

## ➡️ Next Step: Publish on Warden Studio

Use the OpenClaw skill:
https://www.clawhub.ai/Kryptopaid/warden-studio-deploy

## ⚙️ Environment Setup

```bash
# Required
OPENAI_API_KEY=[YOUR-OPENAI-API-KEY]
OPENAI_MODEL=gpt-4

# Required for LangSmith Deployments
LANGSMITH_API_KEY=[YOUR-LANGSMITH-API-KEY]

# Optional (based on agent)
COINGECKO_API_KEY=...
ALCHEMY_API_KEY=...
WEATHER_API_KEY=...

# Server
PORT=8000
NODE_ENV=production
```

**Get LangSmith API Key:**
1. Sign up at https://smith.langchain.com
2. Go to Settings → API Keys
3. Create new key

## 🧪 Testing Endpoints

```bash
# Health check
curl http://localhost:8000/health

# Invoke agent
curl -X POST http://localhost:8000/invoke \
  -H "Content-Type: application/json" \
  -d '{"input": "test query"}'

# Stream response
curl -X POST http://localhost:8000/stream \
  -H "Content-Type: application/json" \
  -d '{"input": "test query"}'
```

## 🐛 Common Issues

| Problem | Solution |
|---------|----------|
| Agent not accessible | Check firewall, verify HTTPS |
| LangGraph errors | Check Node 18+ or Python 3.11+ |
| OpenAI errors | Verify API key, check rate limits |
| Slow responses | Parallelize API calls, add caching |

## 📝 Project Structure

```typescript
my-agent/
├── package.json          # Dependencies
├── tsconfig.json         # TypeScript config
├── langgraph.json        # LangGraph config
├── .env                  # Environment variables
├── src/
│   ├── graph.ts          # Agent workflow
│   ├── agent.ts          # Main logic
│   └── tools.ts          # External APIs
└── README.md            # Documentation
```

## 🎓 Learning Path

1. **Study**: LangGraph Quick Start (understand basics)
2. **Study**: Weather Agent (learn simple patterns)  
3. **Build**: YOUR unique agent (original idea)
4. **Submit**: To incentive programme
5. **Iterate**: Get feedback, improve
6. **Advanced Study**: CoinGecko/Portfolio for complex patterns

**Remember**: Study the examples, then build something NEW!

## 🔗 Important Links

- Clone agents: `git clone https://github.com/warden-protocol/community-agents.git`
- Deploy guide: `references/deployment-guide.md`
- Code patterns: `references/langgraph-patterns.md`
- Examples: `assets/example-configs.md`

## 💬 Getting Help

1. Check SKILL.md for full guide
2. Read relevant reference files
3. Review example configs
4. Join Discord #developers
5. Check GitHub issues

---

**Quick Tip**: Study the example agents to learn patterns, then build something completely NEW and unique!

**Remember**: Example agents (Weather, CoinGecko, Portfolio) exist to teach you. Your submission MUST be original to win the incentive programme.


path: references/langgraph-patterns.md
content: # LangGraph Agent Patterns for Warden

Detailed patterns and examples for building production-ready LangGraph agents.

## Agent Architecture Patterns

### Pattern 1: Simple Request-Response Agent

**Use for**: Basic queries, data fetching, simple transformations

```typescript
import { StateGraph, END } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";

// Define state
interface AgentState {
  input: string;
  output?: string;
  error?: string;
}

// Single node function
async function processRequest(state: AgentState) {
  try {
    const llm = new ChatOpenAI({ modelName: "gpt-4" });
    const response = await llm.invoke(state.input);
    return { output: response.content };
  } catch (error) {
    return { error: error.message };
  }
}

// Build graph
const workflow = new StateGraph<AgentState>({
  channels: {
    input: null,
    output: null,
    error: null
  }
});

workflow.addNode("process", processRequest);
workflow.setEntryPoint("process");
workflow.addEdge("process", END);

export const agent = workflow.compile();
```

### Pattern 2: Schema-Guided Reasoning (SGR)

**Use for**: Complex analysis, multi-step workflows, data validation

```typescript
import { StateGraph, END } from "@langchain/langgraph";
import { z } from "zod";

// Define structured state
interface AgentState {
  // Input
  rawInput: string;
  
  // Validation
  isValid: boolean;
  validationError?: string;
  
  // Extraction
  extractedParams?: {
    token?: string;
    timeframe?: string;
    metric?: string;
  };
  
  // Data fetching
  rawData?: any;
  fetchError?: string;
  
  // Analysis
  analysis?: {
    summary: string;
    metrics: Record<string, number>;
    insights: string[];
  };
  
  // Output
  finalResponse?: string;
}

// Step 1: Validate input
async function validateInput(state: AgentState) {
  const schema = z.object({
    [REDACTED_SECRET]).min(1),
    timeframe: z.enum(["1d", "7d", "30d"]).optional()
  });
  
  try {
    // Use LLM to extract structured data
    const llm = new ChatOpenAI({ modelName: "gpt-4" });
    const prompt = `Extract token and timeframe from: "${state.rawInput}"
    Return JSON: {"token": "...", "timeframe": "..."}`;
    
    const response = await llm.invoke(prompt);
    const parsed = JSON.parse(response.content);
    schema.parse(parsed);
    
    return { isValid: true };
  } catch (error) {
    return { 
      isValid: false, 
      validationError: error.message 
    };
  }
}

// Step 2: Extract parameters
async function extractParameters(state: AgentState) {
  if (!state.isValid) return state;
  
  const llm = new ChatOpenAI({ modelName: "gpt-4" });
  const prompt = `Extract structured parameters from: "${state.rawInput}"
  Return JSON with: token, timeframe, metric`;
  
  const response = await llm.invoke(prompt);
  const params = JSON.parse(response.content);
  
  return { extractedParams: params };
}

// Step 3: Fetch data
async function fetchData(state: AgentState) {
  if (!state.extractedParams) return state;
  
  try {
    const { token } = state.extractedParams;
    const response = await fetch(
      `https://api.coingecko.com/api/v3/coins/${token}`
    );
    const data = await response.json();
    
    return { rawData: data };
  } catch (error) {
    return { fetchError: error.message };
  }
}

// Step 4: Analyze data
async function analyzeData(state: AgentState) {
  if (!state.rawData) return state;
  
  const analysis = {
    summary: `Analysis of ${state.extractedParams?.token}`,
    metrics: {
      price: state.rawData.market_data.current_price.usd,
      volume: state.rawData.market_data.total_volume.usd,
      marketCap: state.rawData.market_data.market_cap.usd
    },
    insights: [
      "Price trend analysis",
      "Volume pattern",
      "Market sentiment"
    ]
  };
  
  return { analysis };
}

// Step 5: Generate response
async function generateResponse(state: AgentState) {
  if (!state.analysis) return state;
  
  const llm = new ChatOpenAI({ modelName: "gpt-4" });
  const prompt = `Generate user-friendly response from analysis:
  ${JSON.stringify(state.analysis, null, 2)}`;
  
  const response = await llm.invoke(prompt);
  
  return { finalResponse: response.content };
}

// Build workflow
const workflow = new StateGraph<AgentState>({
  channels: {
    rawInput: null,
    isValid: null,
    validationError: null,
    extractedParams: null,
    rawData: null,
    fetchError: null,
    analysis: null,
    finalResponse: null
  }
});

workflow.addNode("validate", validateInput);
workflow.addNode("extract", extractParameters);
workflow.addNode("fetch", fetchData);
workflow.addNode("analyze", analyzeData);
workflow.addNode("generate", generateResponse);

workflow.setEntryPoint("validate");
workflow.addEdge("validate", "extract");
workflow.addEdge("extract", "fetch");
workflow.addEdge("fetch", "analyze");
workflow.addEdge("analyze", "generate");
workflow.addEdge("generate", END);

export const agent = workflow.compile();
```

### Pattern 3: Parallel Data Fetching

**Use for**: Comparing multiple items, aggregating data from multiple sources

```typescript
import { StateGraph, END } from "@langchain/langgraph";

interface AgentState {
  input: string;
  items: string[];
  results: Array<{
    item: string;
    data: any;
    error?: string;
  }>;
  comparison?: string;
}

// Parse items to compare
async function parseItems(state: AgentState) {
  const llm = new ChatOpenAI({ modelName: "gpt-4" });
  const prompt = `Extract items to compare from: "${state.input}"
  Return JSON array: ["item1", "item2"]`;
  
  const response = await llm.invoke(prompt);
  const items = JSON.parse(response.content);
  
  return { items };
}

// Fetch data for all items in parallel
async function fetchAllData(state: AgentState) {
  const results = await Promise.all(
    state.items.map(async (item) => {
      try {
        const response = await fetch(`https://api.example.com/${item}`);
        const data = await response.json();
        return { item, data };
      } catch (error) {
        return { item, data: null, error: error.message };
      }
    })
  );
  
  return { results };
}

// Compare and generate report
async function compareResults(state: AgentState) {
  const llm = new ChatOpenAI({ modelName: "gpt-4" });
  const prompt = `Compare these items and provide insights:
  ${JSON.stringify(state.results, null, 2)}`;
  
  const response = await llm.invoke(prompt);
  
  return { comparison: response.content };
}

const workflow = new StateGraph<AgentState>({
  channels: {
    input: null,
    items: null,
    results: null,
    comparison: null
  }
});

workflow.addNode("parse", parseItems);
workflow.addNode("fetch", fetchAllData);
workflow.addNode("compare", compareResults);

workflow.setEntryPoint("parse");
workflow.addEdge("parse", "fetch");
workflow.addEdge("fetch", "compare");
workflow.addEdge("compare", END);

export const agent = workflow.compile();
```

### Pattern 4: Conditional Routing

**Use for**: Different paths based on input, error handling, feature flags

```typescript
import { StateGraph, END } from "@langchain/langgraph";

interface AgentState {
  input: string;
  requestType?: "query" | "analysis" | "comparison";
  result?: string;
}

// Classify request type
async function classifyRequest(state: AgentState) {
  const llm = new ChatOpenAI({ modelName: "gpt-4" });
  const prompt = `Classify this request: "${state.input}"
  Return one of: query, analysis, comparison`;
  
  const response = await llm.invoke(prompt);
  const type = response.content.trim() as AgentState["requestType"];
  
  return { requestType: type };
}

// Routing function
function routeByType(state: AgentState) {
  return state.requestType || "query";
}

// Handler for each type
async function handleQuery(state: AgentState) {
  // Simple query logic
  return { result: "Query result" };
}

async function handleAnalysis(state: AgentState) {
  // Complex analysis logic
  return { result: "Analysis result" };
}

async function handleComparison(state: AgentState) {
  // Comparison logic
  return { result: "Comparison result" };
}

const workflow = new StateGraph<AgentState>({
  channels: {
    input: null,
    requestType: null,
    result: null
  }
});

workflow.addNode("classify", classifyRequest);
workflow.addNode("query", handleQuery);
workflow.addNode("analysis", handleAnalysis);
workflow.addNode("comparison", handleComparison);

workflow.setEntryPoint("classify");
workflow.addConditionalEdges(
  "classify",
  routeByType,
  {
    query: "query",
    analysis: "analysis",
    comparison: "comparison"
  }
);

workflow.addEdge("query", END);
workflow.addEdge("analysis", END);
workflow.addEdge("comparison", END);

export const agent = workflow.compile();
```

## Python Examples

### Basic Python Agent

```python
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict

class AgentState(TypedDict):
    input: str
    output: str | None
    error: str | None

async def process_request(state: AgentState) -> AgentState:
    try:
        llm = ChatOpenAI(model="gpt-4")
        response = await llm.ainvoke(state["input"])
        return {"output": response.content}
    except Exception as e:
        return {"error": str(e)}

workflow = StateGraph(AgentState)
workflow.add_node("process", process_request)
workflow.set_entry_point("process")
workflow.add_edge("process", END)

agent = workflow.compile()
```

### SGR Pattern in Python

```python
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict, Optional, Dict, List, Any
import json

class AgentState(TypedDict):
    raw_input: str
    is_valid: bool
    validation_error: Optional[str]
    extracted_params: Optional[Dict[str, Any]]
    raw_data: Optional[Any]
    fetch_error: Optional[str]
    analysis: Optional[Dict[str, Any]]
    final_response: Optional[str]

async def validate_input(state: AgentState) -> AgentState:
    llm = ChatOpenAI(model="gpt-4")
    prompt = f"""Extract token and timeframe from: "{state['raw_input']}"
    Return JSON: {{"token": "...", "timeframe": "..."}}"""
    
    try:
        response = await llm.ainvoke(prompt)
        parsed = json.loads(response.content)
        return {"is_valid": True}
    except Exception as e:
        return {"is_valid": False, "validation_error": str(e)}

async def extract_parameters(state: AgentState) -> AgentState:
    if not state.get("is_valid"):
        return state
    
    llm = ChatOpenAI(model="gpt-4")
    prompt = f"""Extract parameters from: "{state['raw_input']}"
    Return JSON with: token, timeframe, metric"""
    
    response = await llm.ainvoke(prompt)
    params = json.loads(response.content)
    
    return {"extracted_params": params}

async def fetch_data(state: AgentState) -> AgentState:
    if not state.get("extracted_params"):
        return state
    
    # Fetch data logic
    return {"raw_data": {}}

async def analyze_data(state: AgentState) -> AgentState:
    if not state.get("raw_data"):
        return state
    
    analysis = {
        "summary": "Analysis summary",
        "metrics": {},
        "insights": []
    }
    
    return {"analysis": analysis}

async def generate_response(state: AgentState) -> AgentState:
    if not state.get("analysis"):
        return state
    
    llm = ChatOpenAI(model="gpt-4")
    prompt = f"""Generate response from analysis:
    {json.dumps(state['analysis'], indent=2)}"""
    
    response = await llm.ainvoke(prompt)
    return {"final_response": response.content}

# Build workflow
workflow = StateGraph(AgentState)
workflow.add_node("validate", validate_input)
workflow.add_node("extract", extract_parameters)
workflow.add_node("fetch", fetch_data)
workflow.add_node("analyze", analyze_data)
workflow.add_node("generate", generate_response)

workflow.set_entry_point("validate")
workflow.add_edge("validate", "extract")
workflow.add_edge("extract", "fetch")
workflow.add_edge("fetch", "analyze")
workflow.add_edge("analyze", "generate")
workflow.add_edge("generate", END)

agent = workflow.compile()
```

## Error Handling Best Practices

### Graceful Degradation

```typescript
async function fetchWithFallback(state: AgentState) {
  try {
    // Try primary API
    const data = await primaryAPI.fetch(state.query);
    return { data, source: "primary" };
  } catch (error) {
    try {
      // Fallback to secondary API
      const data = await secondaryAPI.fetch(state.query);
      return { data, source: "secondary" };
    } catch (fallbackError) {
      // Return cached data or error
      return { 
        error: "Both APIs failed",
        cached: getCachedData(state.query)
      };
    }
  }
}
```

### User-Friendly Error Messages

```typescript
function formatError(error: Error, context: string): string {
  const errorMessages = {
    RATE_LIMIT: "I'm getting too many requests. Please try again in a moment.",
    INVALID_TOKEN: "I couldn't find information about that token. Please check the name.",
    API_DOWN: "The data service is temporarily unavailable. Please try again later.",
    NETWORK_ERROR: "I'm having trouble connecting. Please check your internet connection."
  };
  
  // Match error type and return friendly message
  const errorType = identifyErrorType(error);
  return errorMessages[errorType] || "Something went wrong. Please try again.";
}
```

## Testing Patterns

### Unit Testing Agent Nodes

```typescript
import { describe, it, expect } from "vitest";

describe("validateInput", () => {
  it("should accept valid token query", async () => {
    const state = { rawInput: "What's the price of Bitcoin?" };
    const result = await validateInput(state);
    expect(result.isValid).toBe(true);
  });
  
  it("should reject empty query", async () => {
    const state = { rawInput: "" };
    const result = await validateInput(state);
    expect(result.isValid).toBe(false);
    expect(result.validationError).toBeDefined();
  });
});
```

### Integration Testing Agent Flow

```typescript
describe("Agent End-to-End", () => {
  it("should process valid query", async () => {
    const input = { rawInput: "Compare Bitcoin and Ethereum" };
    const result = await agent.invoke(input);
    
    expect(result.finalResponse).toBeDefined();
    expect(result.finalResponse).toContain("Bitcoin");
    expect(result.finalResponse).toContain("Ethereum");
  });
});
```

## Performance Optimization

### Caching Strategies

```typescript
import NodeCache from "node-cache";

const cache = new NodeCache({ stdTTL: 300 }); // 5 minute cache

async function fetchWithCache(key: string) {
  const cached = cache.get(key);
  if (cached) return cached;
  
  const fresh = await fetch(apiUrl);
  cache.set(key, fresh);
  return fresh;
}
```

### Parallel Processing

```typescript
// Bad: Sequential
const btcData = await fetchToken("bitcoin");
const ethData = await fetchToken("ethereum");

// Good: Parallel
const [btcData, ethData] = await Promise.all([
  fetchToken("bitcoin"),
  fetchToken("ethereum")
]);
```

### Token Optimization

```typescript
// Minimize tokens in LLM calls
const prompt = `Analyze: ${compactData}`;  // Not: ${JSON.stringify(allData, null, 2)}

// Use streaming for long responses
const stream = await llm.stream(prompt);
for await (const chunk of stream) {
  // Process incrementally
}
```


path: references/installation-guide.md
content: # Installation Guide for OpenClaw

## Quick Setup

1. **Install into OpenClaw**
   - Place the entire `warden-agent-builder` folder in your OpenClaw `skills/` directory
   - OpenClaw will detect the skill from the `SKILL.md` frontmatter

2. **Verify Installation**
   - Check that OpenClaw can see the skill in its available skills
   - The skill should trigger on keywords like "Warden", "LangGraph agent", "Warden Studio", etc.

3. **Test the Skill**
   - Try a command like: "Build me a Warden Protocol agent for tracking crypto prices"
   - OpenClaw should automatically read the SKILL.md and start helping

## Skill Description (for OpenClaw)

This will be automatically extracted from SKILL.md frontmatter:

```yaml
name: warden-agent-builder
description: "Build and deploy LangGraph agents for Warden Protocol's Agentic Wallet ecosystem. Use this skill when users want to: (1) Create agents for Warden Protocol, (2) Build LangGraph-based crypto/Web3 agents, (3) Deploy agents to Warden's Agent Hub, (4) Participate in Warden's Agent Builder Incentive Programme (up to $10k rewards), (5) Integrate with Warden Studio, or (6) Build agents using Warden's community templates (weather, CoinGecko, portfolio analysis)"
```

## File Structure Explanation

```
warden-agent-builder/
│
├── SKILL.md                    ← OpenClaw reads this FIRST
│   └── Contains: Main instructions, requirements, quick start guide
│
├── references/                 ← OpenClaw reads these WHEN NEEDED
│   ├── langgraph-patterns.md   (Detailed code patterns & examples)
│   └── deployment-guide.md     (API integration & deployment)
│
├── scripts/                    ← Helper scripts OpenClaw can use
│   ├── init-agent.py           (Create new agent from template)
│   └── test-agent.py           (Test deployed agents)
│
└── assets/                     ← Example configurations
    └── example-configs.md      (Templates & config examples)
```

## How OpenClaw Uses the Skill

### Automatic Triggering

The skill automatically triggers when users mention:
- "Warden" + "agent"
- "LangGraph agent"
- "Warden Protocol"
- "Agent Builder Incentive Programme"
- Template names: "Weather Agent", "CoinGecko Agent", etc.

### Progressive Loading

1. **Always loads**: SKILL.md frontmatter (name + description)
2. **When triggered**: Full SKILL.md body
3. **When needed**: Reference files and scripts

### Example Usage Flow

```
User: "Help me build a Warden agent for crypto analysis"
      ↓
OpenClaw triggers warden-agent-builder skill
      ↓
OpenClaw reads SKILL.md
      ↓
OpenClaw identifies: User wants CoinGecko-style agent
      ↓
OpenClaw reads references/langgraph-patterns.md for code examples
      ↓
OpenClaw can execute scripts/init-agent.py to create project
      ↓
OpenClaw provides complete implementation with deployment guide
```

## Testing the Installation

### Test 1: Simple Query
```
User: "What's the Warden Protocol?"
Expected: OpenClaw explains Warden and mentions agent building capability
```

### Test 2: Agent Creation
```
User: "Build me a Warden agent that checks Bitcoin prices"
Expected: OpenClaw:
1. Reads SKILL.md
2. Reminds user to study community examples (not rebuild)
3. Provides implementation steps
4. References deployment guide
```

### Test 3: Deployment Help
```
User: "How do I deploy my Warden agent?"
Expected: OpenClaw:
1. Reads SKILL.md deployment section
2. Reads deployment-guide.md if needed
3. Provides step-by-step deployment instructions
```

## Troubleshooting

### Skill Not Triggering
- Check that SKILL.md has proper YAML frontmatter
- Verify the description field includes trigger keywords
- Ensure the skill folder is in the correct directory

### Missing Reference Files
- Verify all files are present in the skill folder
- Check file permissions (scripts should be executable)
- Ensure SKILL.md references point to correct file paths

### Scripts Not Working
- Make scripts executable: `chmod +x scripts/*.py`
- Verify Python is available in OpenClaw environment
- Check required packages (requests, etc.) are installed

## Best Practices for OpenClaw

1. **Always Start with SKILL.md**
   - Contains the core instructions and requirements
   - Provides the roadmap for helping users

2. **Load References Selectively**
   - Only read reference files when detailed examples are needed
   - Saves context window space

3. **Use Scripts When Appropriate**
   - init-agent.py: When user wants to create a new agent
   - test-agent.py: When user needs to test a deployed agent

4. **Refer to Assets**
   - example-configs.md: When user needs configuration templates
   - Show specific examples rather than explaining from scratch

## Advanced Usage

### Custom Templates
Users can add their own agent templates to the assets/ folder, and OpenClaw will be able to reference them.

### Integration with Other Skills
This skill can work alongside other OpenClaw skills for:
- Database integration
- Frontend development
- DevOps automation

### Extending the Skill
Add new reference files for:
- Advanced LangGraph patterns
- Specific use case examples
- Integration guides for other protocols

## Support

For issues with the skill itself:
1. Check this installation guide
2. Review README.md in the skill folder
3. Verify all files are present and properly formatted
4. Test with simple queries first

For Warden Protocol questions:
- Discord: #developers channel
- GitHub: https://github.com/warden-protocol/community-agents
- Documentation: https://docs.wardenprotocol.org

---

**Version**: 1.0.0
**Last Updated**: December 2024
**Compatible with**: OpenClaw ClawHub skill system


path: README.md
content: # Warden Agent Builder Skill for OpenClaw

This skill enables OpenClaw bots to build and deploy LangGraph agents for the Warden Protocol's Agentic Wallet ecosystem.

## Overview

**Warden Protocol** is an "Agentic Wallet for the Do-It-For-Me economy" with an active **Agent Builder Incentive Programme** offering up to **$10,000** for top agents. This skill provides everything needed to:

- Build LangGraph-based agents (TypeScript or Python)
- Follow Warden's requirements and best practices
- Deploy to LangSmith Deployments or custom infrastructure
- Participate in the incentive programme
- Register with Warden Studio

## Skill Structure

```
warden-agent-builder/
├── SKILL.md                          # Main skill file (always read first)
├── references/
│   ├── langgraph-patterns.md         # Detailed LangGraph patterns & examples
│   └── deployment-guide.md           # API integration & deployment guide
├── scripts/
│   ├── init-agent.py                 # Quick agent initialization script
│   └── test-agent.py                 # Agent testing script
└── assets/
    └── example-configs.md            # Example configurations & templates
```

## How OpenClaw Should Use This Skill

### 1. When to Trigger This Skill

The skill should be used when users mention:
- "Build a Warden agent"
- "Create an agent for Warden Protocol"
- "Warden incentive programme"
- "LangGraph agent"
- "Deploy to Warden"
- Specific Warden templates (Weather Agent, CoinGecko Agent, Portfolio Agent)

### 2. Step-by-Step Usage

**Step 1: Read SKILL.md**
Always start by reading the main SKILL.md file which contains:
- Requirements checklist
- Template options (quick start, weather, CoinGecko, portfolio)
- Building instructions
- Deployment options
- Best practices

**Step 2: Use Reference Files When Needed**

Read `references/langgraph-patterns.md` when:
- User needs detailed code examples
- Implementing Schema-Guided Reasoning (SGR)
- Setting up complex workflows
- Handling errors and edge cases

Read `references/deployment-guide.md` when:
- Setting up API integrations
- Deploying to production
- Configuring Docker/Kubernetes
- Implementing monitoring

**Step 3: Use Scripts**

The skill includes two helper scripts:

**`scripts/init-agent.py`** - Initialize new agent projects
```bash
python scripts/init-agent.py my-agent --template typescript --description "My Warden agent"
```

**`scripts/test-agent.py`** - Test deployed agents
```bash
python scripts/test-agent.py https://api.example.com --api-key [YOUR-API-KEY]
```

**Step 4: Reference Assets**

Use `assets/example-configs.md` for:
- Sample agent configurations
- Environment variable templates
- Docker Compose examples
- Warden Studio registration format

## Example OpenClaw Workflows

### Example 1: Building a New Agent

```
User: "Build me a Warden agent that tracks Bitcoin prices"

OpenClaw Actions:
1. Read SKILL.md to understand requirements
2. Identify: User wants ORIGINAL agent (not CoinGecko clone)
3. Suggest studying CoinGecko agent pattern, then building unique features
4. Use init-agent.py script to create NEW project structure
5. Read langgraph-patterns.md for SGR implementation pattern
6. Implement CUSTOM price tracking logic with unique features
7. Read deployment-guide.md for API setup
8. Generate code files for NEW agent
9. Provide deployment instructions
10. Emphasize: "This should be different from CoinGecko Agent"
```

### Example 2: Deploy Existing Agent

```
User: "Deploy my agent to Warden"

OpenClaw Actions:
1. Read SKILL.md deployment section
2. Check if LangSmith Deployments or self-hosted
3. Read deployment-guide.md for specific instructions
4. Verify requirements checklist
5. Provide deployment commands
6. Explain Warden Studio registration
```

### Example 3: Debug Agent

```
User: "My Warden agent has errors"

OpenClaw Actions:
1. Use test-agent.py script to diagnose
2. Read langgraph-patterns.md for error handling patterns
3. Check deployment-guide.md for common issues
4. Suggest fixes based on error type
```

## Key Warden Requirements

Agents MUST meet these requirements to be eligible for the incentive programme:

✓ **Framework**: LangGraph only (TypeScript or Python)
✓ **Access**: API-accessible (no UI needed)
✓ **Deployment**: LangSmith Deployments OR custom infrastructure
✓ **API Keys Required**:
  - OpenAI API key (for LLM calls)
  - LangSmith API key (for LangSmith Deployments deployment)
  - Additional keys based on your agent's functionality
✓ **Isolation**: One agent per LangGraph instance
✓ **Phase 1 Restrictions**:
  - Cannot access user wallets
  - Cannot store data on Warden infrastructure
  - (Confirm current limitations in the latest Warden docs)

## Available Templates

The Warden repository contains **example agents to study**, not templates to recreate:

1. **LangGraph Quick Start** - Minimal chatbot for learning basics
2. **Weather Agent** ⚠️ - Study for simple data fetching patterns (don't rebuild)
3. **CoinGecko Agent** ⚠️ - Study for intermediate SGR patterns (don't rebuild)
4. **Portfolio Analysis Agent** ⚠️ - Study for advanced architecture (don't rebuild)

**These exist to teach patterns. Build something NEW for the incentive programme!**

## Example Agent Ideas

**Web3 Use Cases:**
- Gas price optimizer
- NFT rarity analyzer
- DeFi yield comparator
- Wallet health checker
- Transaction explainer
- Token price alerts

**General Use Cases:**
- News aggregator
- Research assistant
- Data visualizer
- API orchestrator
- Workflow automator

## Installation for OpenClaw

To use this skill with OpenClaw:

1. Place the `warden-agent-builder` folder in your OpenClaw `skills/` directory
2. OpenClaw will automatically detect the skill from SKILL.md frontmatter
3. The skill triggers when users mention Warden or LangGraph agents

## Resources

- **Community Repository**: https://github.com/warden-protocol/community-agents
- **Documentation**: https://docs.wardenprotocol.org
- **Discord Support**: #developers channel
- **Incentive Programme**: Open to OpenClaw agents that deploy to Warden

## Quick Start Commands

```bash
# Clone Warden community agents
git clone https://github.com/warden-protocol/community-agents.git

# Initialize new agent (TypeScript)
python scripts/init-agent.py my-agent --template typescript

# Initialize new agent (Python)
python scripts/init-agent.py my-agent --template python

# Test deployed agent
python scripts/test-agent.py https://your-agent.com --api-key [YOUR-API-KEY]

# Deploy to LangSmith Deployments
# Use the LangSmith Deployments UI after pushing to GitHub
```

## Support

For questions or issues with this skill:
- Review the SKILL.md file first
- Check references/ for detailed guides
- Consult assets/example-configs.md for templates
- Join Warden Discord #developers channel

## Next Step: Publish on Warden Studio

If the user needs guided UI steps to publish in Warden Studio, switch to the OpenClaw skill:
https://www.clawhub.ai/Kryptopaid/warden-studio-deploy

## License

This skill is provided for use with OpenClaw bots to build Warden Protocol agents. See individual file headers for specific licensing.

---

**Built for OpenClaw by Claude**
**Compatible with Warden Protocol Agent Builder Incentive Programme**


path: scripts/init-agent.py
content: #!/usr/bin/env python3
"""
Warden Agent Initializer
Quickly scaffold a new Warden Protocol agent from templates.
"""

import os
import sys
import json
import argparse
from pathlib import Path

TEMPLATES = {
    "typescript": {
        "name": "TypeScript Agent",
        "base": "langgraph-quick-start",
        "files": {
            "package.json": """{
  "name": "{agent_name}",
  "version": "1.0.0",
  "description": "{description}",
  "main": "dist/index.js",
  "scripts": {
    "dev": "langgraph dev",
    "build": "tsc",
    "start": "node dist/index.js",
    "test": "vitest"
  },
  "dependencies": {
    "@langchain/langgraph": "^0.0.19",
    "@langchain/openai": "^0.0.19",
    "dotenv": "^16.0.3",
    "express": "^4.18.2"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0",
    "vitest": "^1.0.0"
  }
}""",
            "tsconfig.json": """{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}""",
            "langgraph.json": """{
  "agent_id": "{agent_name}",
  "dependencies": ["."],
  "graphs": {
    "agent": "./src/graph.ts"
  },
  "env": ".env"
}""",
            ".env.example": """# OpenAI Configuration
OPENAI_API_KEY=your_openai_key_here
OPENAI_MODEL=gpt-4

# LangSmith Configuration (required for LangSmith Deployments deployment)
LANGSMITH_API_KEY=your_langsmith_key_here

# Agent Configuration
PORT=8000
NODE_ENV=development

# Add your API keys here
# COINGECKO_API_KEY=
# ALCHEMY_API_KEY=
# WEATHER_API_KEY=""",
            ".gitignore": """node_modules/
dist/
.env
*.log
.DS_Store""",
            "src/graph.ts": """import { StateGraph, END } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";

// Define the agent state
interface AgentState {
  input: string;
  output?: string;
  error?: string;
}

// Main agent logic
async function processRequest(state: AgentState): Promise<Partial<AgentState>> {
  try {
    const llm = new ChatOpenAI({
      modelName: process.env.OPENAI_MODEL || "gpt-4",
      temperature: 0.7
    });
    
    // TODO: Implement your agent logic here
    const response = await llm.invoke(state.input);
    
    return { output: response.content as string };
  } catch (error) {
    return { error: (error as Error).message };
  }
}

// Build the graph
const workflow = new StateGraph<AgentState>({
  channels: {
    input: null,
    output: null,
    error: null
  }
});

workflow.addNode("process", processRequest);
workflow.setEntryPoint("process");
workflow.addEdge("process", END);

export const agent = workflow.compile();
""",
            "README.md": """# {agent_name}

{description}

## Setup

1. Install dependencies:
```bash
npm install
```

2. Copy `.env.example` to `.env` and add your API keys:
```bash
cp .env.example .env
```

3. Run development server:
```bash
npm run dev
```

## Testing

Test the agent API:
```bash
curl -X POST http://localhost:8000/invoke \\
  -H "Content-Type: application/json" \\
  -d '{{"input": "test query"}}'
```

## Deployment

Deploy to LangSmith Deployments:
1. Push your repo to GitHub
2. Create a deployment in the LangSmith Deployments UI
3. Set environment variables and deploy

## Requirements

- Node.js 18+
- OpenAI API key
- LangGraph CLI (optional, for local dev)

## License

MIT
"""
        }
    },
    "python": {
        "name": "Python Agent",
        "base": "langgraph-quick-start-py",
        "files": {
            "requirements.txt": """langgraph>=0.0.19
langchain-openai>=0.0.19
python-dotenv>=1.0.0
fastapi>=0.100.0
uvicorn>=0.23.0""",
            "langgraph.json": """{
  "agent_id": "{agent_name}",
  "python_version": "3.11",
  "dependencies": ["."],
  "graphs": {
    "agent": "./src/graph.py"
  },
  "env": ".env"
}""",
            ".env.example": """# OpenAI Configuration
OPENAI_API_KEY=your_openai_key_here
OPENAI_MODEL=gpt-4

# LangSmith Configuration (required for LangSmith Deployments deployment)
LANGSMITH_API_KEY=your_langsmith_key_here

# Agent Configuration
PORT=8000

# Add your API keys here
# COINGECKO_API_KEY=
# ALCHEMY_API_KEY=
# WEATHER_API_KEY=""",
            ".gitignore": """__pycache__/
*.py[cod]
.env
*.log
.DS_Store
venv/""",
            "src/__init__.py": "",
            "src/graph.py": """from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict, Optional
import os

# Define the agent state
class AgentState(TypedDict):
    input: str
    output: Optional[str]
    error: Optional[str]

# Main agent logic
async def process_request(state: AgentState) -> AgentState:
    try:
        llm = ChatOpenAI(
            model=os.getenv("OPENAI_MODEL", "gpt-4"),
            temperature=0.7
        )
        
        # TODO: Implement your agent logic here
        response = await llm.ainvoke(state["input"])
        
        return {"output": response.content}
    except Exception as e:
        return {"error": str(e)}

# Build the graph
workflow = StateGraph(AgentState)
workflow.add_node("process", process_request)
workflow.set_entry_point("process")
workflow.add_edge("process", END)

agent = workflow.compile()
""",
            "README.md": """# {agent_name}

{description}

## Setup

1. Create virtual environment:
```bash
python -m venv venv
source venv/bin/activate  # On Windows: venv\\Scripts\\activate
```

2. Install dependencies:
```bash
pip install -r requirements.txt
```

3. Copy `.env.example` to `.env` and add your API keys:
```bash
cp .env.example .env
```

4. Run development server:
```bash
langgraph dev
```

## Testing

Test the agent API:
```bash
curl -X POST http://localhost:8000/invoke \\
  -H "Content-Type: application/json" \\
  -d '{{"input": "test query"}}'
```

## Deployment

Deploy to LangSmith Deployments:
1. Push your repo to GitHub
2. Create a deployment in the LangSmith Deployments UI
3. Set environment variables and deploy

## Requirements

- Python 3.11+
- OpenAI API key
- LangGraph CLI (optional, for local dev)

## License

MIT
"""
        }
    }
}


def create_agent(agent_name: str, description: str, template: str, output_dir: str):
    """Create a new Warden agent from template."""
    
    # Validate template
    if template not in TEMPLATES:
        print(f"Error: Unknown template '{template}'")
        print(f"Available templates: {', '.join(TEMPLATES.keys())}")
        sys.exit(1)
    
    # Create output directory
    agent_dir = Path(output_dir) / agent_name
    if agent_dir.exists():
        print(f"Error: Directory '{agent_dir}' already exists")
        sys.exit(1)
    
    agent_dir.mkdir(parents=True)
    
    # Get template
    template_data = TEMPLATES[template]
    
    print(f"Creating {template_data['name']}...")
    print(f"Name: {agent_name}")
    print(f"Description: {description}")
    print(f"Location: {agent_dir}")
    print()
    
    # Create files
    for filename, content in template_data["files"].items():
        filepath = agent_dir / filename
        
        # Create parent directories if needed
        filepath.parent.mkdir(parents=True, exist_ok=True)
        
        # Format content with variables
        formatted_content = content.format(
            agent_name=agent_name,
            description=description
        )
        
        # Write file
        filepath.write_text(formatted_content)
        print(f"✓ Created {filename}")
    
    print()
    print("Agent created successfully!")
    print()
    print("Next steps:")
    print(f"1. cd {agent_dir}")
    
    if template == "typescript":
        print("2. npm install")
    else:
        print("2. python -m venv venv && source venv/bin/activate")
        print("3. pip install -r requirements.txt")
    
    print(f"{3 if template == 'typescript' else 4}. Copy .env.example to .env and add your API keys")
    print(f"{4 if template == 'typescript' else 5}. npm run dev (or langgraph dev)")
    print()


def main():
    parser = argparse.ArgumentParser(
        description="Initialize a new Warden Protocol agent"
    )
    parser.add_argument(
        "name",
        help="Agent name (e.g., 'my-warden-agent')"
    )
    parser.add_argument(
        "--description",
        "-d",
        default="A Warden Protocol agent",
        help="Agent description"
    )
    parser.add_argument(
        "--template",
        "-t",
        choices=list(TEMPLATES.keys()),
        default="typescript",
        help="Template to use (default: typescript)"
    )
    parser.add_argument(
        "--output",
        "-o",
        default=".",
        help="Output directory (default: current directory)"
    )
    
    args = parser.parse_args()
    
    create_agent(
        agent_name=args.name,
        description=args.description,
        template=args.template,
        output_dir=args.output
    )


if __name__ == "__main__":
    main()


path: scripts/test-agent.py
content: #!/usr/bin/env python3
"""
Warden Agent Tester
Test your Warden Protocol agent's API endpoints.
"""

import argparse
import requests
import json
import sys
import time
from typing import Dict, Any


class AgentTester:
    def __init__(self, url: str, api_key: str = None):
        self.url = url.rstrip('/')
        self.headers = {
            'Content-Type': 'application/json'
        }
        if [REDACTED_SECRET]'Authorization'] = f'Bearer {api_key}'
    
    def test_health(self) -> bool:
        """Test health endpoint."""
        print("Testing /health endpoint...")
        try:
            response = requests.get(f"{self.url}/health", timeout=5)
            if response.status_code == 200:
                print("✓ Health check passed")
                print(f"  Response: {response.json()}")
                return True
            else:
                print(f"✗ Health check failed: {response.status_code}")
                return False
        except Exception as e:
            print(f"✗ Health check error: {e}")
            return False
    
    def test_invoke(self, input_text: str) -> Dict[str, Any]:
        """Test invoke endpoint."""
        print(f"\nTesting /invoke with input: '{input_text}'...")
        try:
            payload = {"input": input_text}
            start_time = time.time()
            
            response = requests.post(
                f"{self.url}/invoke",
                json=payload,
                headers=self.headers,
                timeout=30
            )
            
            elapsed_time = time.time() - start_time
            
            if response.status_code == 200:
                result = response.json()
                print("✓ Invoke successful")
                print(f"  Duration: {elapsed_time:.2f}s")
                print(f"  Response:")
                print(json.dumps(result, indent=2))
                return result
            else:
                print(f"✗ Invoke failed: {response.status_code}")
                print(f"  Error: {response.text}")
                return None
        except Exception as e:
            print(f"✗ Invoke error: {e}")
            return None
    
    def test_stream(self, input_text: str) -> None:
        """Test streaming endpoint."""
        print(f"\nTesting /stream with input: '{input_text}'...")
        try:
            payload = {"input": input_text}
            
            response = requests.post(
                f"{self.url}/stream",
                json=payload,
                headers=self.headers,
                stream=True,
                timeout=30
            )
            
            if response.status_code == 200:
                print("✓ Stream started")
                print("  Receiving chunks:")
                
                for line in response.iter_lines():
                    if line:
                        decoded = line.decode('utf-8')
                        if decoded.startswith('data: '):
                            data = decoded[6:]  # Remove 'data: ' prefix
                            try:
                                chunk = json.loads(data)
                                print(f"    {chunk}")
                            except json.JSONDecodeError:
                                print(f"    {data}")
            else:
                print(f"✗ Stream failed: {response.status_code}")
                print(f"  Error: {response.text}")
        except Exception as e:
            print(f"✗ Stream error: {e}")
    
    def run_test_suite(self, test_inputs: list = None):
        """Run complete test suite."""
        print("=" * 60)
        print("Warden Agent Test Suite")
        print("=" * 60)
        print(f"URL: {self.url}")
        print()
        
        # Test health
        if not self.test_health():
            print("\n⚠ Health check failed, continuing with other tests...")
        
        # Default test inputs if none provided
        if not test_inputs:
            test_inputs = [
                "Hello, can you help me?",
                "What's the weather like?",
                "Analyze Bitcoin price"
            ]
        
        # Test invoke
        print("\n" + "-" * 60)
        print("Testing Invoke Endpoint")
        print("-" * 60)
        
        for input_text in test_inputs:
            self.test_invoke(input_text)
        
        # Test stream
        print("\n" + "-" * 60)
        print("Testing Stream Endpoint")
        print("-" * 60)
        
        if len(test_inputs) > 0:
            self.test_stream(test_inputs[0])
        
        print("\n" + "=" * 60)
        print("Test Suite Complete")
        print("=" * 60)


def main():
    parser = argparse.ArgumentParser(
        description="Test Warden Protocol agent API"
    )
    parser.add_argument(
        "url",
        help="Agent API URL (e.g., https://api.example.com)"
    )
    parser.add_argument(
        "--api-key",
        "-k",
        help="API key for authentication"
    )
    parser.add_argument(
        "--input",
        "-i",
        action="append",
        help="Test input (can be specified multiple times)"
    )
    parser.add_argument(
        "--health-only",
        action="store_true",
        help="Only test health endpoint"
    )
    parser.add_argument(
        "--invoke-only",
        action="store_true",
        help="Only test invoke endpoint"
    )
    parser.add_argument(
        "--stream-only",
        action="store_true",
        help="Only test stream endpoint"
    )
    
    args = parser.parse_args()
    
    # Validate URL
    if not args.url.startswith(('http://', 'https://')):
        print("Error: URL must start with http:// or https://")
        sys.exit(1)
    
    # Create tester
    tester = AgentTester(args.url, args.api_key)
    
    # Run specific tests if requested
    if args.health_only:
        tester.test_health()
    elif args.invoke_only:
        inputs = args.input or ["Test query"]
        for input_text in inputs:
            tester.test_invoke(input_text)
    elif args.stream_only:
        inputs = args.input or ["Test query"]
        tester.test_stream(inputs[0])
    else:
        # Run full test suite
        tester.run_test_suite(args.input)


if __name__ == "__main__":
    main()


path: assets/example-configs.md
content: # Example Warden Agent Configurations

This file contains example configurations for common Warden agent patterns.

## 1. Simple Weather Agent Configuration

```json
{
  "agent_id": "weather-agent",
  "name": "Weather Agent",
  "description": "Provides current weather information for any location",
  "capabilities": [
    "Current weather lookup",
    "Temperature conversion",
    "Weather forecasts"
  ],
  "required_apis": {
    "WEATHER_API_KEY": "https://www.weatherapi.com"
  },
  "deployment": {
    "type": "langgraph-cloud",
    "runtime": "typescript",
    "node_version": "18"
  }
}
```

## 2. Crypto Price Analyzer Configuration

```json
{
  "agent_id": "crypto-analyzer",
  "name": "Crypto Price Analyzer",
  "description": "Analyzes cryptocurrency prices and market trends",
  "capabilities": [
    "Real-time price lookup",
    "Historical price analysis",
    "Market trend identification",
    "Comparative analysis"
  ],
  "required_apis": {
    "COINGECKO_API_KEY": "https://www.coingecko.com/api",
    "OPENAI_API_KEY": "https://platform.openai.com"
  },
  "deployment": {
    "type": "langgraph-cloud",
    "runtime": "typescript",
    "node_version": "18"
  }
}
```

## 3. Portfolio Tracker Configuration

```json
{
  "agent_id": "portfolio-tracker",
  "name": "Portfolio Tracker",
  "description": "Tracks and analyzes cryptocurrency portfolios across multiple chains",
  "capabilities": [
    "Multi-chain balance tracking",
    "Portfolio value calculation",
    "Performance analysis",
    "Risk assessment"
  ],
  "required_apis": {
    "ALCHEMY_API_KEY": "https://www.alchemy.com",
    "COINGECKO_API_KEY": "https://www.coingecko.com/api",
    "OPENAI_API_KEY": "https://platform.openai.com"
  },
  "supported_chains": [
    "ethereum",
    "polygon",
    "solana",
    "arbitrum"
  ],
  "deployment": {
    "type": "langgraph-cloud",
    "runtime": "typescript",
    "node_version": "18"
  }
}
```

## 4. DeFi Yield Finder Configuration

```json
{
  "agent_id": "defi-yield-finder",
  "name": "DeFi Yield Finder",
  "description": "Finds and compares DeFi yield opportunities",
  "capabilities": [
    "Yield rate comparison",
    "Protocol safety analysis",
    "APY calculations",
    "Historical performance"
  ],
  "required_apis": {
    "DEFILLAMA_API": "https://api.llama.fi",
    "COINGECKO_API_KEY": "https://www.coingecko.com/api",
    "OPENAI_API_KEY": "https://platform.openai.com"
  },
  "protocols": [
    "aave",
    "compound",
    "uniswap",
    "curve"
  ],
  "deployment": {
    "type": "self-hosted",
    "runtime": "python",
    "python_version": "3.11"
  }
}
```

## Environment Variable Template

```bash
# ===========================
# Core Configuration
# ===========================
NODE_ENV=production
PORT=8000
LOG_LEVEL=info

# ===========================
# OpenAI Configuration
# ===========================
OPENAI_API_KEY=[YOUR-OPENAI-API-KEY]
OPENAI_MODEL=gpt-4
OPENAI_TEMPERATURE=0.7
OPENAI_MAX_TOKENS=1000

# ===========================
# LangSmith Configuration
# ===========================
# Required for LangSmith Deployments deployment
LANGSMITH_API_KEY=[YOUR-LANGSMITH-API-KEY]
# Optional: Enable tracing
LANGSMITH_TRACING=true
LANGSMITH_PROJECT=your-project-name

# ===========================
# Web3 APIs
# ===========================
# Alchemy - Ethereum/Polygon
ALCHEMY_API_KEY=...
ALCHEMY_NETWORK=mainnet

# CoinGecko - Price Data
COINGECKO_API_KEY=...

# Etherscan - Ethereum Explorer
ETHERSCAN_API_KEY=...

# ===========================
# Traditional APIs
# ===========================
# Weather API
WEATHER_API_KEY=...

# News API
NEWS_API_KEY=...

# ===========================
# Database (if needed)
# ===========================
DATABASE_URL=postgresql://user:pass@host:5432/db

# Redis (if using caching)
REDIS_URL=redis://localhost:6379

# ===========================
# Monitoring
# ===========================
SENTRY_DSN=...
```

## LangGraph Configuration Templates

### TypeScript Agent

```json
{
  "agent_id": "my-agent",
  "dependencies": ["."],
  "graphs": {
    "agent": "./src/graph.ts"
  },
  "env": ".env",
  "python_version": null
}
```

### Python Agent

```json
{
  "agent_id": "my-agent",
  "python_version": "3.11",
  "dependencies": ["."],
  "graphs": {
    "agent": "./src/graph.py"
  },
  "env": ".env"
}
```

## Docker Compose Examples

### Simple Agent

```yaml
version: '3.8'

services:
  agent:
    build: .
    ports:
      - "8000:8000"
    env_file:
      - .env
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 3s
      retries: 3
```

### Agent with Redis Cache

```yaml
version: '3.8'

services:
  agent:
    build: .
    ports:
      - "8000:8000"
    env_file:
      - .env
    environment:
      - REDIS_URL=redis://redis:6379
    depends_on:
      - redis
    restart: unless-stopped
  
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    restart: unless-stopped

volumes:
  redis-data:
```

### Agent with PostgreSQL

```yaml
version: '3.8'

services:
  agent:
    build: .
    ports:
      - "8000:8000"
    env_file:
      - .env
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/agent
    depends_on:
      - db
    restart: unless-stopped
  
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: agent
    volumes:
      - postgres-data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  postgres-data:
```

## Warden Studio Registration Checklist

When registering your agent with Warden Studio:

### Required Information
- [ ] Agent name (user-facing)
- [ ] Clear description (2-3 sentences)
- [ ] API URL (must be HTTPS)
- [ ] API key for authentication
- [ ] Avatar/logo image (recommended: 512x512px PNG)

### Capabilities List
List 3-5 specific capabilities:
- [ ] Capability 1 (e.g., "Real-time price lookup")
- [ ] Capability 2 (e.g., "Historical analysis")
- [ ] Capability 3 (e.g., "Trend prediction")

### Testing Checklist
Before registration:
- [ ] Agent responds to health checks
- [ ] All API endpoints work correctly
- [ ] Error handling is implemented
- [ ] Response times are acceptable (<5s)
- [ ] API authentication works
- [ ] Documentation is complete

### Example Registration Data

```json
{
  "agent": {
    "name": "Crypto Analyzer Pro",
    "description": "Advanced cryptocurrency analysis tool that provides real-time price data, technical analysis, and market insights across 1000+ tokens.",
    "api_url": "https://api.myagent.com",
    "api_key": "warden_xxxxxxxxxxxxx",
    "avatar_url": "https://cdn.myagent.com/avatar.png",
    "capabilities": [
      "Real-time price lookup for 1000+ tokens",
      "Technical analysis with 20+ indicators",
      "Market sentiment analysis",
      "Historical price charts",
      "Portfolio tracking"
    ],
    "categories": [
      "defi",
      "analytics",
      "trading"
    ],
    "supported_chains": [
      "ethereum",
      "bsc",
      "polygon",
      "solana"
    ],
    "pricing": {
      "model": "free",
      "rate_limits": {
        "requests_per_minute": 60,
        "requests_per_day": 1000
      }
    }
  }
}
```

## API Response Format Standards

### Success Response

```json
{
  "success": true,
  "data": {
    "output": "Agent response here...",
    "metadata": {
      "tokens_used": 150,
      "processing_time_ms": 1234,
      "model": "gpt-4"
    }
  }
}
```

### Error Response

```json
{
  "success": false,
  "error": {
    "code": "INVALID_INPUT",
    "message": "User-friendly error message",
    "details": "Technical details for debugging"
  }
}
```

### Streaming Response

```
data: {"type": "start", "timestamp": "2025-01-15T10:00:00Z"}

data: {"type": "chunk", "content": "Analyzing data..."}

data: {"type": "chunk", "content": "Results show..."}

data: {"type": "end", "metadata": {"tokens": 150}}
```


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)