← Documentation Home

Your First Agent

Step-by-step guide to creating your first OSSA-compliant AI agent using the BuildKit CLI and OSSA specification.

Overview

In this tutorial, you'll create a complete OSSA agent that: - Validates code quality before commits - Integrates with GitLab for issue tracking - Runs autonomously in response to events - Follows the OSSA v0.2.4 specification

Time to complete: 20-30 minutes

Prerequisites


Step 1: Understanding OSSA Agents

What is an OSSA Agent?

OSSA (Open Standard for Scalable AI Agents) defines a vendor-neutral specification for AI agents, similar to how OpenAPI defines REST APIs.

Key components: - Manifest (agent.ossa.yaml) - Declarative agent definition - Capabilities - What the agent can do - Runtime - Where the agent runs (local, Kubernetes, serverless) - Communication - How agents interact with each other

Agent Types

OSSA defines four agent roles:

  1. Worker - Executes tasks (e.g., run tests, deploy code)
  2. Governor - Enforces policies (e.g., TDD compliance, security rules)
  3. Critic - Provides feedback (e.g., code review, performance analysis)
  4. Observer - Monitors and reports (e.g., system health, metrics collection)

We'll build a Governor agent that enforces code quality standards.


Step 2: Generate Agent Scaffold

Create Agent Directory

# Create workspace for agents
mkdir -p ~/Sites/LLM/my-agents
cd ~/Sites/LLM/my-agents

# Generate agent scaffold with OSSA CLI
ossa generate governor \
  --name "Code Quality Enforcer" \
  --id code-quality-enforcer

# This creates:
# code-quality-enforcer/
# ├── agent.ossa.yaml         # OSSA manifest
# ├── src/
# │   ├── index.ts           # Main entry point
# │   └── capabilities/      # Capability implementations
# ├── tests/
# │   └── index.test.ts      # Unit tests
# ├── package.json
# ├── tsconfig.json
# └── README.md

Explore Generated Files

cd code-quality-enforcer
tree -L 2

# View the OSSA manifest
cat agent.ossa.yaml

Generated agent.ossa.yaml:

ossaVersion: "0.2.4"

agent:
  id: code-quality-enforcer
  name: Code Quality Enforcer
  version: "1.0.0"
  role: governor
  description: Enforces code quality standards and best practices

  runtime:
    type: local
    node:
      version: "20.x"
      entrypoint: "dist/index.js"

  capabilities:
    - name: validate_code
      description: Validate code quality before commit
      input_schema:
        type: object
        properties:
          files:
            type: array
            items:
              type: string
            description: List of file paths to validate
          standards:
            type: string
            enum: [drupal, javascript, python, php]
            description: Coding standard to apply
        required: [files]
      output_schema:
        type: object
        properties:
          valid:
            type: boolean
          violations:
            type: array
            items:
              type: object
              properties:
                file:
                  type: string
                line:
                  type: number
                message:
                  type: string
                severity:
                  type: string
                  enum: [error, warning, info]
          summary:
            type: string

  triggers:
    - type: webhook
      event: "gitlab.push"
      endpoint: "/webhooks/gitlab"

  dependencies:
    agents: []
    services:
      - name: gitlab
        type: api
        version: ">=15.0"

  observability:
    metrics:
      enabled: true
      port: 9090
    logging:
      level: info
      format: json
    tracing:
      enabled: true

metadata:
  author: "Your Name"
  license: "Apache-2.0"
  tags: [code-quality, governor, ci-cd]

Step 3: Implement Agent Logic

Install Dependencies

# Install base dependencies
npm install

# Add additional dependencies for code validation
npm install --save \
  @typescript-eslint/eslint-plugin \
  @typescript-eslint/parser \
  eslint \
  phpcs \
  prettier

Implement Main Entry Point

Edit src/index.ts:

import express from 'express';
import { validateCode } from './capabilities/validate-code';
import { createLogger } from './utils/logger';

const app = express();
const logger = createLogger('code-quality-enforcer');

app.use(express.json());

// Health check endpoint
app.get('/health', (req, res) => {
  res.json({ status: 'ok', agent: 'code-quality-enforcer', version: '1.0.0' });
});

// Main capability endpoint
app.post('/capabilities/validate_code', async (req, res) => {
  try {
    const { files, standards = 'javascript' } = req.body;

    if (!files || !Array.isArray(files)) {
      return res.status(400).json({
        error: 'Invalid input: files array required'
      });
    }

    logger.info(`Validating ${files.length} files with ${standards} standards`);

    const result = await validateCode(files, standards);

    res.json(result);
  } catch (error) {
    logger.error('Validation failed', { error: error.message });
    res.status(500).json({ error: 'Validation failed' });
  }
});

// GitLab webhook endpoint
app.post('/webhooks/gitlab', async (req, res) => {
  try {
    const { event_name, commits } = req.body;

    if (event_name !== 'push') {
      return res.status(200).json({ message: 'Event ignored' });
    }

    // Extract changed files from commits
    const changedFiles = commits.flatMap(commit =>
      [...commit.added, ...commit.modified]
    );

    if (changedFiles.length === 0) {
      return res.status(200).json({ message: 'No files to validate' });
    }

    logger.info(`Processing push event with ${changedFiles.length} changed files`);

    // Validate changed files
    const result = await validateCode(changedFiles, 'javascript');

    if (!result.valid) {
      logger.warn('Code quality violations detected', {
        violations: result.violations.length
      });

      // Post comment to GitLab MR/commit
      // (Implementation depends on GitLab API integration)
    }

    res.json({ processed: true, result });
  } catch (error) {
    logger.error('Webhook processing failed', { error: error.message });
    res.status(500).json({ error: 'Processing failed' });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  logger.info(`Agent listening on port ${PORT}`);
});

Implement Validation Capability

Create src/capabilities/validate-code.ts:

import { ESLint } from 'eslint';
import * as fs from 'fs/promises';
import * as path from 'path';

interface ValidationResult {
  valid: boolean;
  violations: Array<{
    file: string;
    line: number;
    message: string;
    severity: 'error' | 'warning' | 'info';
  }>;
  summary: string;
}

export async function validateCode(
  files: string[],
  standards: string
): Promise<ValidationResult> {
  const violations: ValidationResult['violations'] = [];

  // Configure ESLint based on standards
  const eslint = new ESLint({
    overrideConfigFile: getConfigForStandard(standards),
    useEslintrc: false,
  });

  for (const file of files) {
    try {
      // Skip non-code files
      if (!isCodeFile(file)) continue;

      // Read file content
      const content = await fs.readFile(file, 'utf-8');

      // Lint file
      const results = await eslint.lintText(content, { filePath: file });

      // Extract violations
      for (const result of results) {
        for (const message of result.messages) {
          violations.push({
            file: result.filePath,
            line: message.line,
            message: message.message,
            severity: message.severity === 2 ? 'error' : 'warning',
          });
        }
      }
    } catch (error) {
      violations.push({
        file,
        line: 0,
        message: `Failed to validate: ${error.message}`,
        severity: 'error',
      });
    }
  }

  const errorCount = violations.filter(v => v.severity === 'error').length;
  const warningCount = violations.filter(v => v.severity === 'warning').length;

  return {
    valid: errorCount === 0,
    violations,
    summary: `Found ${errorCount} errors and ${warningCount} warnings in ${files.length} files`,
  };
}

function isCodeFile(file: string): boolean {
  const ext = path.extname(file);
  return ['.ts', '.js', '.tsx', '.jsx', '.php', '.py'].includes(ext);
}

function getConfigForStandard(standards: string): string {
  const configs = {
    javascript: path.join(__dirname, '../../config/eslint.config.js'),
    typescript: path.join(__dirname, '../../config/eslint.config.js'),
    drupal: path.join(__dirname, '../../config/phpcs.xml'),
    php: path.join(__dirname, '../../config/phpcs.xml'),
  };

  return configs[standards] || configs.javascript;
}

Create Logger Utility

Create src/utils/logger.ts:

import pino from 'pino';

export function createLogger(name: string) {
  return pino({
    name,
    level: process.env.LOG_LEVEL || 'info',
    formatters: {
      level: (label) => {
        return { level: label };
      },
    },
  });
}

Step 4: Write Tests

Edit tests/index.test.ts:

import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
import request from 'supertest';
import express from 'express';
import { validateCode } from '../src/capabilities/validate-code';

describe('Code Quality Enforcer Agent', () => {
  describe('Health Check', () => {
    it('should return healthy status', async () => {
      // Test implementation
    });
  });

  describe('Validate Code Capability', () => {
    it('should detect ESLint violations', async () => {
      const files = ['./test-fixtures/bad-code.js'];
      const result = await validateCode(files, 'javascript');

      expect(result.valid).toBe(false);
      expect(result.violations.length).toBeGreaterThan(0);
    });

    it('should pass clean code', async () => {
      const files = ['./test-fixtures/good-code.js'];
      const result = await validateCode(files, 'javascript');

      expect(result.valid).toBe(true);
      expect(result.violations.length).toBe(0);
    });
  });

  describe('GitLab Webhook', () => {
    it('should process push events', async () => {
      // Test webhook processing
    });
  });
});

Run Tests

# Run tests
npm test

# Run with coverage
npm test -- --coverage

# Expected output:
# PASS  tests/index.test.ts
#   Code Quality Enforcer Agent
#     ✓ should return healthy status (15 ms)
#     ✓ should detect ESLint violations (82 ms)
#     ✓ should pass clean code (45 ms)
#
# Test Suites: 1 passed, 1 total
# Tests:       3 passed, 3 total

Step 5: Validate OSSA Compliance

# Validate agent manifest
ossa validate agent.ossa.yaml

# Expected output:
# ✓ OSSA manifest is valid
#   Agent ID: code-quality-enforcer
#   Agent Type: governor
#   OSSA Version: 0.2.4
#   Capabilities: 1
#   Runtime: local (Node.js 20.x)

Check for Common Issues

# Use BuildKit to validate
buildkit ossa validate agent.ossa.yaml --strict

# This checks for:
# - Required fields present
# - Schema validation
# - Capability input/output schemas
# - Runtime configuration
# - Best practices compliance

Step 6: Run Agent Locally

Build the Agent

# Compile TypeScript
npm run build

# Output in dist/ directory
ls -la dist/

Start the Agent

# Run locally
npm start

# Or with development auto-reload
npm run dev

# Expected output:
# {"level":"info","time":1699564800000,"name":"code-quality-enforcer","msg":"Agent listening on port 3000"}

Test Agent Endpoints

In another terminal:

# Health check
curl http://localhost:3000/health

# Response:
# {"status":"ok","agent":"code-quality-enforcer","version":"1.0.0"}

# Test validation capability
curl -X POST http://localhost:3000/capabilities/validate_code \
  -H "Content-Type: application/json" \
  -d '{
    "files": ["src/index.ts"],
    "standards": "typescript"
  }'

# Response:
# {
#   "valid": true,
#   "violations": [],
#   "summary": "Found 0 errors and 0 warnings in 1 files"
# }

Step 7: Register Agent with BuildKit

Create BuildKit Configuration

# Initialize BuildKit in project
buildkit init

# Register agent
buildkit agents register \
  --manifest agent.ossa.yaml \
  --local

# Verify registration
buildkit agents list

# Expected output:
# ID                        NAME                    TYPE      STATUS
# code-quality-enforcer     Code Quality Enforcer   governor  running

Configure GitLab Integration

# Set up GitLab webhook
buildkit gitlab webhooks create \
  --project llm/demos/llm-platform-demo \
  --url http://localhost:3000/webhooks/gitlab \
  --events push,merge_request

# This creates a webhook in GitLab that triggers your agent

Step 8: Deploy Agent to Production

Option 1: Deploy to Kubernetes

# Build Docker image
docker build -t yourregistry.com/code-quality-enforcer:1.0.0 .

# Push to registry
docker push yourregistry.com/code-quality-enforcer:1.0.0

# Deploy with BuildKit
buildkit agents deploy \
  --manifest agent.ossa.yaml \
  --namespace agents \
  --image yourregistry.com/code-quality-enforcer:1.0.0

Option 2: Deploy Locally with BuildKit

# Start agent as background service
buildkit agents start code-quality-enforcer --daemon

# Check status
buildkit agents status code-quality-enforcer

# View logs
buildkit agents logs code-quality-enforcer --follow

Step 9: Monitor Agent Activity

View Agent Metrics

# Get agent metrics
curl http://localhost:9090/metrics

# Or use BuildKit CLI
buildkit agents metrics code-quality-enforcer

Check Agent Logs

# View logs
buildkit agents logs code-quality-enforcer

# Filter by severity
buildkit agents logs code-quality-enforcer --level error

# Stream logs in real-time
buildkit agents logs code-quality-enforcer --follow

GitLab Integration

# View agent activity in GitLab
buildkit gitlab agent-activity \
  --agent code-quality-enforcer \
  --project llm/demos/llm-platform-demo

# Shows:
# - Webhook triggers
# - Validation results
# - Comments posted
# - Merge request blocks

Step 10: Create Agent Workflows

Combine your agent with other OSSA agents:

# Create workflow definition
buildkit workflows create code-review-workflow \
  --agents code-quality-enforcer,security-scanner,test-runner \
  --trigger gitlab.merge_request.open

Workflow definition (workflows/code-review.yaml):

name: code-review-workflow
version: "1.0.0"

trigger:
  type: webhook
  event: gitlab.merge_request.open

steps:
  - agent: code-quality-enforcer
    capability: validate_code
    input:
      files: "{{ event.changed_files }}"
      standards: "javascript"
    on_failure: block_merge

  - agent: security-scanner
    capability: scan_vulnerabilities
    input:
      files: "{{ event.changed_files }}"
    on_failure: block_merge

  - agent: test-runner
    capability: run_tests
    input:
      test_suite: "unit"
    on_failure: block_merge

  - agent: code-quality-enforcer
    capability: post_summary
    input:
      merge_request: "{{ event.merge_request.iid }}"

Advanced Features

Add AI-Powered Analysis

Enhance your agent with LLM capabilities:

// src/capabilities/ai-review.ts
import { LLMGateway } from '@bluefly/llm-gateway-client';

const llm = new LLMGateway({
  baseUrl: process.env.LLM_GATEWAY_URL,
});

export async function aiCodeReview(code: string): Promise<string> {
  const response = await llm.chat({
    model: 'claude-3-sonnet',
    messages: [
      {
        role: 'system',
        content: 'You are a code reviewer. Identify potential issues, suggest improvements, and explain best practices.',
      },
      {
        role: 'user',
        content: `Review this code:\n\n${code}`,
      },
    ],
  });

  return response.content;
}

Agent-to-Agent Communication

// src/capabilities/collaborate.ts
import { OSSAClient } from '@bluefly/ossa-client';

const ossa = new OSSAClient({
  registryUrl: process.env.OSSA_REGISTRY_URL,
});

export async function requestSecurityScan(files: string[]) {
  const securityAgent = await ossa.findAgent({
    role: 'critic',
    capabilities: ['scan_vulnerabilities']
  });

  const result = await securityAgent.invoke('scan_vulnerabilities', {
    files,
  });

  return result;
}

Troubleshooting

Agent Won't Start

# Check logs
npm run dev 2>&1 | tee agent.log

# Verify dependencies
npm install

# Check port availability
lsof -i :3000

OSSA Validation Fails

# Get detailed validation errors
ossa validate agent.ossa.yaml --verbose

# Common issues:
# - Missing required fields
# - Invalid schema references
# - Incorrect OSSA version
# - Missing capabilities

GitLab Webhook Not Triggering

# Test webhook manually
curl -X POST http://localhost:3000/webhooks/gitlab \
  -H "Content-Type: application/json" \
  -d @test-fixtures/gitlab-push-event.json

# Check GitLab webhook delivery logs
# GitLab → Project → Settings → Webhooks → Edit → Recent Deliveries

Next Steps

Congratulations! You've created your first OSSA agent. Next:

  1. Build a workflow: Your First Workflow
  2. Deploy to Kubernetes: Deploy to K8s Tutorial
  3. Add monitoring: Monitor Agents
  4. Learn common pitfalls: Common Pitfalls

Additional Resources