Jesús Pérez 2f76728481
Some checks failed
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (push) Has been cancelled
feat: integrate NatsBridge with real JetStream into A2A server
vapora-agents:
  - Add nats_bridge.rs with real async_nats JetStream (submit_task, durable
    pull consumer, list_agents from live registry)
  - Replace swarm_adapter.rs stubs with real SwarmCoordinator calls
    (select_agent via bidding, report_completion with load update, agent_load
    from fractional profile)
  - Expose SwarmCoordinator::get_agent() for per-agent profile access

  vapora-a2a:
  - CoordinatorBridge: replace raw NatsClient with NatsBridge (JetStream
    at-least-once delivery via durable pull consumer)
  - Add GET /a2a/agents endpoint listing registered agents
  - task_manager::create(): switch .content() to parameterized INSERT INTO
    to avoid SurrealDB serializer failing on adjacently-tagged enums
  - task_manager::get(): explicit field projection, exclude id (Thing),
    cast datetimes with type::string() to fix serde_json::Value deserialization
  - Integration tests: 4/5 pass with SurrealDB + NATS

  vapora-leptos-ui:
  - Set doctest = false in [lib]: Leptos components require WASM reactive
    runtime, incompatible with native cargo test --doc
2026-02-17 22:28:51 +00:00
..

vapora-a2a

Agent-to-Agent (A2A) Protocol Server - Production-ready implementation of the A2A specification for VAPORA.

Features

  • Full A2A Protocol Compliance - JSON-RPC 2.0, Agent Card discovery
  • SurrealDB Persistence - Tasks survive restarts, production-ready storage
  • NATS Async Coordination - Real-time task completion via message queue
  • Prometheus Metrics - Full observability with /metrics endpoint
  • Type-Safe - Rust compile-time guarantees for protocol correctness
  • Integration Tests - 5 comprehensive end-to-end tests

Architecture

┌─────────────────────────────────────────────────┐
│           A2A HTTP Server (Axum)                │
│   /.well-known/agent.json | /a2a | /metrics    │
└────────────────┬────────────────────────────────┘
                 │
         ┌───────┴────────┐
         │                │
    ┌────▼─────┐   ┌──────▼────────┐
    │ Bridge   │   │ TaskManager   │
    │ (NATS)   │   │ (SurrealDB)   │
    └────┬─────┘   └──────┬────────┘
         │                │
         │         ┌──────▼────────┐
         └────────►│ AgentCoord    │
                   └───────────────┘

Components

  1. CoordinatorBridge - Maps A2A tasks to internal agent coordination

    • JetStream durable pull consumer via NatsBridge (at-least-once delivery)
    • DashMap<String, oneshot::Sender> for async result delivery
    • Graceful degradation if NATS unavailable
  2. TaskManager - Persistent task storage and lifecycle

    • SurrealDB integration with Surreal
    • Parameterized queries for security
    • Tasks survive server restarts
  3. Server - HTTP endpoints (Axum)

    • GET /.well-known/agent.json - Agent discovery
    • POST /a2a - Task dispatch
    • GET /a2a/tasks/{task_id} - Status query
    • GET /a2a/agents - List registered agents
    • GET /health - Health check
    • GET /metrics - Prometheus metrics

Quick Start

Prerequisites

# Start SurrealDB
docker run -d -p 8000:8000 \
  surrealdb/surrealdb:latest \
  start --bind 0.0.0.0:8000

# Start NATS with JetStream (optional, graceful degradation)
docker run -d -p 4222:4222 -p 8222:8222 nats:latest -js
# or use the local container: cd /containers/nats && docker compose up -d

# Run migration
surrealdb import --conn ws://localhost:8000 \
  --user root --pass root \
  migrations/007_a2a_tasks_schema.surql

Run Server

cargo run --bin vapora-a2a -- \
  --host 127.0.0.1 \
  --port 8003 \
  --version 1.0.0

Server will start on http://127.0.0.1:8003

Using the Client

use vapora_a2a_client::{A2aClient, RetryPolicy};

#[tokio::main]
async fn main() {
    let client = A2aClient::new("http://localhost:8003");

    // Discover agent capabilities
    let agent_card = client.discover_agent().await?;
    println!("Agent: {} v{}", agent_card.name, agent_card.version);

    // Dispatch task
    let task_id = client.dispatch_task(
        "task-123".to_string(),
        "Write hello world function".to_string(),
        Some("In Rust".to_string()),
        Some("developer".to_string()),
    ).await?;

    // Poll for completion
    loop {
        let status = client.get_task_status(&task_id).await?;
        match status.state.as_str() {
            "completed" => {
                println!("Result: {:?}", status.result);
                break;
            }
            "failed" => {
                eprintln!("Error: {:?}", status.error);
                break;
            }
            _ => tokio::time::sleep(Duration::from_millis(500)).await,
        }
    }
}

Configuration

Environment Variables

# SurrealDB
SURREAL_URL=ws://localhost:8000
SURREAL_USER=root
SURREAL_PASS=root

# NATS (optional)
NATS_URL=nats://localhost:4222

# Server
A2A_HOST=0.0.0.0
A2A_PORT=8003
A2A_VERSION=1.0.0

CLI Arguments

vapora-a2a \
  --host 0.0.0.0 \
  --port 8003 \
  --version 1.0.0

Metrics

Exposed at http://localhost:8003/metrics in Prometheus text format:

  • vapora_a2a_tasks_total{status="waiting|working|completed|failed"} - Task counts
  • vapora_a2a_task_duration_seconds{status="completed|failed"} - Task execution time
  • vapora_a2a_nats_messages_total{subject,result} - NATS message handling
  • vapora_a2a_db_operations_total{operation,result} - Database operations
  • vapora_a2a_coordinator_assignments_total{skill,result} - Coordinator assignments

Testing

Unit Tests

cargo test -p vapora-a2a --lib

Integration Tests

Require SurrealDB + NATS running:

# Start dependencies
docker compose up -d surrealdb nats

# Run tests
cargo test -p vapora-a2a --test integration_test -- --include-ignored

# Tests:
# 1. Task persistence after restart
# 2. NATS completion updates DB
# 3. Task state transitions
# 4. Task failure handling
# 5. End-to-end dispatch with timeout

Protocol Compliance

Implements A2A Protocol Specification:

  • Agent Card (/.well-known/agent.json)
  • Task dispatch (POST /a2a)
  • Status query (GET /a2a/tasks/{id})
  • JSON-RPC 2.0 envelope
  • Task lifecycle (waiting → working → completed|failed)
  • Artifact support
  • Error handling

Production Deployment

See ADR-0030 and ADR-0031.

Kubernetes

kubectl apply -k kubernetes/overlays/prod/

Docker

docker build -t vapora-a2a:latest -f Dockerfile .
docker run -p 8003:8003 \
  -e SURREAL_URL=ws://surrealdb:8000 \
  -e NATS_URL=nats://nats:4222 \
  vapora-a2a:latest

Troubleshooting

Tasks not persisting

Check SurrealDB connection:

# Verify connection
curl http://localhost:8000/health

# Check migration applied
surrealdb sql --conn ws://localhost:8000 \
  --user root --pass root --ns test --db main \
  "SELECT * FROM a2a_tasks LIMIT 1;"

Tasks not completing

Check NATS connection:

# Subscribe to completion events
nats sub "vapora.tasks.completed"

# Server will log warnings if NATS unavailable

Metrics not showing

# Check metrics endpoint
curl http://localhost:8003/metrics | grep vapora_a2a
  • vapora-a2a-client - Client library for calling A2A servers
  • vapora-agents - Agent coordinator and registry
  • vapora-backend - Main VAPORA REST API

License

MIT OR Apache-2.0