Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion src/agent/registry.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
"""Agent Registry — Manages agent lifecycle and metadata."""

import json
import logging
import time
import uuid
from enum import Enum
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)

# Allowed fields in agent configuration
ALLOWED_CONFIG_FIELDS = {
"image", "command", "args", "env", "resources",
"volumes", "network", "labels", "secrets",
"schedule", "retries", "timeout", "description",
}

# Required fields for agent class
REQUIRED_AGENT_FIELDS = {"name", "type"}
ALLOWED_AGENT_FIELDS = {"name", "type", "config", "description", "version"}


class AgentStatus(Enum):
PENDING = "pending"
Expand All @@ -16,21 +30,41 @@ class AgentStatus(Enum):
TERMINATED = "terminated"


class UnknownRegistryFieldError(ValueError):
"""Raised when an unknown field is provided to the registry."""
pass


class AgentRegistry:
def __init__(self, storage_backend: str = "memory"):
self.storage_backend = storage_backend
self._agents: Dict[str, Dict[str, Any]] = {}
self._index: Dict[str, List[str]] = {}

def _validate_config(self, config: Dict) -> Dict:
"""Validate config fields, rejecting unknown keys."""
if not isinstance(config, dict):
raise UnknownRegistryFieldError("Config must be a dictionary")
unknown = set(config.keys()) - ALLOWED_CONFIG_FIELDS
if unknown:
logger.warning("Rejecting unknown config fields: %s", unknown)
raise UnknownRegistryFieldError(
f"Unknown config fields: {unknown}. "
f"Allowed fields: {ALLOWED_CONFIG_FIELDS}"
)
return config

def register(self, name: str, agent_type: str, config: Optional[Dict] = None) -> str:
# Validate config fields to prevent configuration drift
validated_config = self._validate_config(config or {})
agent_id = str(uuid.uuid4())
timestamp = time.time()
self._agents[agent_id] = {
"id": agent_id,
"name": name,
"type": agent_type,
"status": AgentStatus.PENDING.value,
"config": config or {},
"config": validated_config,
"created_at": timestamp,
"updated_at": timestamp,
"version": "1.0.0",
Expand Down
67 changes: 67 additions & 0 deletions tests/test_registry_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Tests for registry unknown field rejection."""

import pytest
from src.agent.registry import AgentRegistry, UnknownRegistryFieldError, ALLOWED_CONFIG_FIELDS


class TestRegistryRejectUnknownFields:
"""Regression tests for #4055 - Reject unknown registry fields."""

def setup_method(self):
self.registry = AgentRegistry()

def test_register_with_valid_config(self):
"""Registering with valid config fields should succeed."""
agent_id = self.registry.register(
"test-agent", "worker",
config={"image": "python:3.11", "command": "run"}
)
agent = self.registry.get(agent_id)
assert agent is not None
assert agent["config"]["image"] == "python:3.11"

def test_register_with_empty_config(self):
"""Registering with empty config should succeed."""
agent_id = self.registry.register("test-agent", "worker")
agent = self.registry.get(agent_id)
assert agent is not None
assert agent["config"] == {}

def test_register_rejects_unknown_config_field(self):
"""Registering with an unknown config field should raise an error."""
with pytest.raises(UnknownRegistryFieldError, match="Unknown config fields"):
self.registry.register(
"test-agent", "worker",
config={"image": "python:3.11", "bogus_field_xyz": "value"}
)

def test_register_rejects_multiple_unknown_fields(self):
"""Registering with multiple unknown fields should list all of them."""
with pytest.raises(UnknownRegistryFieldError, match="bogus1"):
self.registry.register(
"test-agent", "worker",
config={"bogus1": 1, "bogus2": 2}
)

def test_register_rejects_stale_field(self):
"""A stale/deprecated config field should be rejected (configuration drift)."""
with pytest.raises(UnknownRegistryFieldError):
self.registry.register(
"test-agent", "worker",
config={"image": "python:3.11", "deprecated_key": "old_value"}
)

def test_config_not_mutated(self):
"""Original config dict should not be mutated."""
config = {"image": "python:3.11"}
original = config.copy()
self.registry.register("test-agent", "worker", config=config)
assert config == original

def test_all_allowed_fields_accepted(self):
"""All fields in ALLOWED_CONFIG_FIELDS should be accepted."""
config = {field: f"val_{field}" for field in ALLOWED_CONFIG_FIELDS}
agent_id = self.registry.register("test-agent", "worker", config=config)
agent = self.registry.get(agent_id)
assert agent is not None
assert set(agent["config"].keys()) == ALLOWED_CONFIG_FIELDS