integrations / autogen

AutoGen Integration

Use Attestd as an AutoGen FunctionTool to give agents real-time CVE risk and supply chain integrity data. These patterns target autogen-agentchat 0.7.5, the current stable release.

In multi-agent workflows, a compromised dependency approved by one agent propagates to every downstream agent that trusts its output. The most important correctness concern: outside_coverage is not a safety signal. Catch AttestdUnsupportedProductError and return an explicit unknown-risk response.

installation

Install

Pin autogen-agentchat to 0.7.5. You also need a model client from autogen-ext (OpenAI shown below).

bash
pip install "autogen-agentchat==0.7.5" autogen-core autogen-ext[openai] attestd
tool definition

FunctionTool definition

Wrap the Attestd check in FunctionTool from autogen_core.tools. Instantiate attestd.Client once at module level and capture it in the closure.

attestd_tool.py
import os

import attestd
from autogen_core.tools import FunctionTool
from attestd import AttestdUnsupportedProductError

# Instantiate once — captured in closure. Never instantiate inside the tool function.
_client = attestd.Client(api_key=os.environ["ATTESTD_API_KEY"])


def check_package_vulnerability(product: str, version: str) -> dict:
    """Check whether a software package version has known CVE vulnerabilities
    or supply chain compromise. Use before deploying or recommending any
    software dependency. outside_coverage=True means no data — treat as
    unknown risk, not safe. typosquat_detected=True means the name itself
    is suspect; use resembles to recover the intended package."""
    try:
        result = _client.check(product, version)
        sc = result.supply_chain
        return {
            "outside_coverage": False,
            "risk_state": result.risk_state,
            "remote_exploitable": result.remote_exploitable,
            "actively_exploited": result.actively_exploited,
            "patch_available": result.patch_available,
            "fixed_version": result.fixed_version,
            "supply_chain_compromised": sc.compromised if sc is not None else False,
            "supply_chain_malware_type": sc.malware_type if sc is not None else None,
            "supply_chain_still_live": (
                (sc.removed_at is None) if (sc is not None and sc.compromised) else None
            ),
        }
    except AttestdUnsupportedProductError as e:
        if e.typosquat and e.typosquat.detected:
            return {
                "outside_coverage": True,
                "typosquat_detected": True,
                "typosquat_kind": e.typosquat.kind,
                "resembles": e.typosquat.resembles,
                "likely_intended": list(e.typosquat.likely_intended),
                "message": (
                    f"'{product}' fails package name integrity "
                    f"(kind={e.typosquat.kind}). "
                    f"Likely intended: {e.typosquat.resembles}. "
                    f"Do not install this package."
                ),
            }
        return {
            "outside_coverage": True,
            "typosquat_detected": False,
            "risk_state": None,
            "message": f"No Attestd coverage for '{product}'. Treat as unknown risk.",
        }


attestd_tool = FunctionTool(
    check_package_vulnerability,
    description=(
        "Check CVE risk and supply chain integrity for a software dependency. "
        "outside_coverage=True means unknown risk, not safe."
    ),
)
single agent

Single-agent pattern

One AssistantAgent with the Attestd tool in a RoundRobinGroupChat. Use TextMentionTermination with sources= so termination only fires on agent output, not the initial user task.

single_agent.py
import asyncio

from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.conditions import TextMentionTermination
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient

from attestd_tool import attestd_tool

model_client = OpenAIChatCompletionClient(model="gpt-4o-mini")

security_agent = AssistantAgent(
    name="security_agent",
    model_client=model_client,
    tools=[attestd_tool],
    system_message=(
        "You are a security-aware deployment assistant. "
        "Before approving any software dependency, call check_package_vulnerability. "
        "Block if risk_state is 'critical' or 'high', supply_chain_compromised is True, "
        "or typosquat_detected is True. "
        "If outside_coverage is True, state that explicitly. Do not treat it as safe. "
        "End with APPROVED or BLOCKED and your reasoning."
    ),
)

termination = TextMentionTermination("APPROVED", sources=["security_agent"]) | TextMentionTermination(
    "BLOCKED", sources=["security_agent"]
)
team = RoundRobinGroupChat([security_agent], termination_condition=termination)


async def main() -> None:
    await Console(
        team.run_stream(
            task="Is it safe to deploy with runc 1.0.0 and litellm 1.82.7?"
        )
    )


asyncio.run(main())
multi-agent pattern

Multi-agent safety gate

A dedicated security_gate agent runs Attestd checks before a deployment_agent can proceed. Combine termination conditions with | so the team stops on either APPROVED or BLOCKED from the security gate.

multi_agent.py
import asyncio

from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.conditions import TextMentionTermination
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient

from attestd_tool import attestd_tool

model_client = OpenAIChatCompletionClient(model="gpt-4o-mini")

security_gate = AssistantAgent(
    name="security_gate",
    model_client=model_client,
    tools=[attestd_tool],
    system_message=(
        "You are a security gate agent. Before any software dependency is "
        "deployed or recommended, check it with check_package_vulnerability. "
        "Block if risk_state is 'critical' or 'high', supply_chain_compromised is True, "
        "or typosquat_detected is True. "
        "If outside_coverage is True, state that explicitly. Do not treat it as safe. "
        "Respond with APPROVED or BLOCKED with your reasoning."
    ),
)

deployment_agent = AssistantAgent(
    name="deployment_agent",
    model_client=model_client,
    system_message=(
        "You handle deployment tasks. Wait for security_gate to approve "
        "all dependencies before proceeding."
    ),
)

termination = TextMentionTermination("APPROVED", sources=["security_gate"]) | TextMentionTermination(
    "BLOCKED", sources=["security_gate"]
)

team = RoundRobinGroupChat(
    [security_gate, deployment_agent],
    termination_condition=termination,
)


async def main() -> None:
    await Console(
        team.run_stream(
            task=(
                "Deploy a stack with runc 1.0.0, litellm 1.82.7, "
                "node-ipc 9.1.6, and nginx 1.27.4."
            )
        )
    )


asyncio.run(main())
fields

Return fields

The tool returns these fields. Design agent branching logic around them.

FieldSemantics
outside_coveragetrue if Attestd has no data for this product. Not a safety signal — treat as unknown risk. Check typosquat_detected on this path.
risk_state"critical" | "high" | "elevated" | "low" | "none" | null (when outside_coverage). Block on critical or high.
remote_exploitabletrue if the vulnerability has a network attack vector. Drives the high vs elevated split in risk_state.
actively_exploitedtrue if in the CISA KEV catalog. Block regardless of risk_state if true.
patch_availabletrue if a fixed version is known. Use with fixed_version to tell the agent what to recommend.
fixed_versionThe earliest clean version, or null if no patch exists yet.
supply_chain_compromisedtrue if a malicious publish was detected on npm or PyPI. Block immediately.
supply_chain_malware_typeMalware classification if compromised, else null. e.g. "malware", "keylogger", "cryptominer".
supply_chain_still_livetrue if the compromised version has not been removed from the registry. null when not compromised. A still-live compromise is more urgent.
typosquat_detectedOnly present on outside_coverage responses. true means the package name fails integrity — do not install.
typosquat_kind"hallucination" (AI-invented or conflated name) or "typosquat" (misspelling of a real package).
resembles / likely_intendedThe canonical package(s) the requested name most closely matches. Use to recover the intended dependency.
see also