> ## Documentation Index
> Fetch the complete documentation index at: https://docs.endorlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Agents Hub

> <Badge color="green">Beta</Badge> <br /> Browse Endor Labs security agents and see how they are used across your tenant.

export const YamlTable = ({children, data: propData, content}) => {
  const KV_RE = /^([A-Za-z][A-Za-z0-9_()/#\s-]+?):\s*(.+)$/;
  const INLINE_MD_RE = /(\[([^\]]+)\]\(([^)]+)\))|(`([^`]+)`)|(\*\*([^*]+)\*\*)|(\*([^*]+)\*)/g;
  const YES_RE = /^-yes-$/i;
  const NO_RE = /^-no-$/i;
  const LIMITED_RE = /^-(limited|partial)-$/i;
  const NA_RE = /^-(na|none)-$/i;
  const NA2_RE = /^-na2-$/i;
  const SIMPLE_TAG_RE = /(<br\s*\/?>)|(<p\s*\/?>)|(-note-)|(-warning-)/gi;
  const tryParseKV = trimmed => {
    const m = KV_RE.exec(trimmed);
    return m ? {
      key: m[1],
      value: m[2].trim()
    } : null;
  };
  const registerKey = (key, seenKeys, orderedKeys) => {
    if (!seenKeys.has(key)) {
      orderedKeys.push(key);
      seenKeys.add(key);
    }
  };
  const flushEntry = (currentEntry, entries) => {
    if (Object.keys(currentEntry).length > 0) entries.push(currentEntry);
  };
  const parseDashPrefixed = (lines, entries, orderedKeys, seenKeys) => {
    let currentEntry = {};
    let inEntry = false;
    for (const line of lines) {
      const trimmed = line.trim();
      if (trimmed.startsWith('- ')) {
        if (inEntry) entries.push(currentEntry);
        currentEntry = {};
        inEntry = true;
        const kv = tryParseKV(trimmed.substring(2).trim());
        if (kv) {
          registerKey(kv.key, seenKeys, orderedKeys);
          currentEntry[kv.key] = kv.value;
        }
      } else if (inEntry && trimmed !== '') {
        const kv = tryParseKV(trimmed);
        if (kv) {
          registerKey(kv.key, seenKeys, orderedKeys);
          currentEntry[kv.key] = kv.value;
        }
      }
    }
    flushEntry(currentEntry, entries);
  };
  const parseBlankSeparated = (lines, entries, orderedKeys, seenKeys) => {
    let currentEntry = {};
    let inEntry = false;
    for (const line of lines) {
      const trimmed = line.trim();
      if (trimmed === '') {
        if (inEntry) {
          flushEntry(currentEntry, entries);
          currentEntry = {};
          inEntry = false;
        }
        continue;
      }
      const kv = tryParseKV(trimmed);
      if (!kv) continue;
      const isNewEntry = !line.startsWith(' ') && !line.startsWith('\t');
      if (isNewEntry && inEntry && Object.keys(currentEntry).length > 0) {
        entries.push(currentEntry);
        currentEntry = {};
      }
      registerKey(kv.key, seenKeys, orderedKeys);
      currentEntry[kv.key] = kv.value;
      inEntry = true;
    }
    flushEntry(currentEntry, entries);
  };
  const normalizeEntries = (entries, orderedKeys) => entries.map(entry => {
    const filled = {};
    for (const key of orderedKeys) filled[key] = entry[key] || '';
    return filled;
  });
  const parseYamlTableContent = contentStr => {
    if (!contentStr) return [];
    const entries = [];
    const orderedKeys = [];
    const seenKeys = new Set();
    const lines = contentStr.split('\n');
    if (lines.some(line => line.trim().startsWith('- '))) {
      parseDashPrefixed(lines, entries, orderedKeys, seenKeys);
    } else {
      parseBlankSeparated(lines, entries, orderedKeys, seenKeys);
    }
    return normalizeEntries(entries, orderedKeys);
  };
  const processText = text => {
    if (!text) return text;
    const parts = [];
    let keyIndex = 0;
    let lastIndex = 0;
    let match;
    while ((match = INLINE_MD_RE.exec(text)) !== null) {
      if (match.index > lastIndex) parts.push(text.slice(lastIndex, match.index));
      if (match[1]) {
        parts.push(<a key={keyIndex++} href={match[3]}>{match[2]}</a>);
      } else if (match[4]) {
        parts.push(<code key={keyIndex++}>{match[5]}</code>);
      } else if (match[6]) {
        parts.push(<strong key={keyIndex++}>{match[7]}</strong>);
      } else if (match[8]) {
        parts.push(<em key={keyIndex++}>{match[9]}</em>);
      }
      lastIndex = match.index + match[0].length;
    }
    if (lastIndex < text.length) parts.push(text.slice(lastIndex));
    if (parts.length === 0) return text;
    const keyRef = {
      current: keyIndex
    };
    return expandHtmlTags(parts, keyRef);
  };
  const processBadges = text => {
    if (!text || typeof text !== 'string') return text;
    if (YES_RE.test(text)) return <span className="yt-badge-yes" role="img" aria-label="Supported" title="Supported">✓</span>;
    if (NO_RE.test(text)) return <span className="yt-badge-no" role="img" aria-label="Not supported" title="Not supported">✗</span>;
    if (LIMITED_RE.test(text)) return <span className="yt-badge-limited" role="img" aria-label="Partially supported" title="Partially supported">◐</span>;
    if (NA_RE.test(text) || NA2_RE.test(text)) return <span className="yt-sr-only" title="Not applicable">Not applicable</span>;
    return processText(text);
  };
  const cellClassName = text => {
    if (!text || typeof text !== 'string') return undefined;
    if (NA_RE.test(text)) return 'yt-cell-na';
    if (NA2_RE.test(text)) return 'yt-cell-na2';
    return undefined;
  };
  const expandSimpleTags = (str, keyRef) => {
    const result = [];
    let last = 0;
    SIMPLE_TAG_RE.lastIndex = 0;
    let m;
    while ((m = SIMPLE_TAG_RE.exec(str)) !== null) {
      if (m.index > last) result.push(str.slice(last, m.index));
      if (m[1]) {
        result.push(<br key={keyRef.current++} />);
      } else if (m[2]) {
        result.push(<br key={keyRef.current++} />, <br key={keyRef.current++} />);
      } else if (m[3]) {
        result.push(<span key={keyRef.current++} className="yt-badge-note" style={{
          fontWeight: 600
        }}>Note: </span>);
      } else if (m[4]) {
        result.push(<span key={keyRef.current++} className="yt-badge-warning" style={{
          fontWeight: 600
        }}>Warning: </span>);
      }
      last = m.index + m[0].length;
    }
    if (last < str.length) result.push(str.slice(last));
    return result;
  };
  const expandHtmlTags = (chunks, keyRef) => {
    const out = [];
    for (const chunk of chunks) {
      if (typeof chunk === 'string') {
        out.push(...expandSimpleTags(chunk, keyRef));
      } else {
        out.push(chunk);
      }
    }
    return out;
  };
  const extractText = node => {
    if (node === null || node === undefined) return '';
    if (typeof node === 'string') return node;
    if (typeof node === 'number') return String(node);
    if (typeof node === 'boolean') return '';
    if (Array.isArray(node)) return node.map(extractText).join('');
    if (node && typeof node === 'object' && node.type) {
      const props = node.props || ({});
      if (typeof props.children === 'string') return props.children;
      if (props.children) return extractText(props.children);
      return '';
    }
    return String(node || '');
  };
  const [mounted, setMounted] = useState(false);
  useEffect(() => {
    setMounted(true);
  }, []);
  const data = useMemo(() => {
    if (propData) return propData;
    if (content && typeof content === 'string') return parseYamlTableContent(content);
    if (!children) return [];
    if (typeof children === 'string') return parseYamlTableContent(children);
    const childrenArray = Array.isArray(children) ? children : [children];
    return parseYamlTableContent(childrenArray.map(extractText).join('').trim());
  }, [children, propData, content]);
  const columns = useMemo(() => {
    if (!data || data.length === 0) return [];
    const firstRow = data[0];
    if (!firstRow || typeof firstRow !== 'object') return [];
    return Object.keys(firstRow);
  }, [data]);
  if (!mounted) return null;
  if (!data || data.length === 0) return null;
  const rowKey = row => columns.map(c => row[c] || '').join('|');
  return <table>
      <thead>
        <tr>
          {columns.map(col => <th key={col}>{col.replaceAll('_', ' ')}</th>)}
        </tr>
      </thead>
      <tbody>
        {data.map(row => <tr key={rowKey(row)}>
            {columns.map(col => <td key={col} className={cellClassName(row[col])}>{processBadges(row[col])}</td>)}
          </tr>)}
      </tbody>
    </table>;
};

The Agents Hub is one place in the Endor Labs user interface to discover the security agents Endor Labs publishes, understand what each one does, and see how your team is using them. Each agent packages a specific Endor Labs workflow, such as triaging findings, remediating vulnerable dependencies, or diagnosing scan failures. You run the agents inside your own AI coding assistant, and the Agents Hub gives you the catalog and the activity view in the product.

## How it relates to the Agent Kit

The Agents Hub and the [Endor Labs Agent Kit](/secure-ai-coding/agent-kit) describe the same agents from two directions.

<YamlTable>
  {`
    - Surface: Endor Labs Agent Kit
    What_you_do_there: Install the agents into a host, such as Claude Code, as a plugin or extension, then run them.
    Where_it_lives: Your AI coding assistant
    - Surface: Agents Hub
    What_you_do_there: Browse those same agents and monitor how your team uses them against your tenant.
    Where_it_lives: The Endor Labs user interface
    `}
</YamlTable>

The agents are read-only against Endor Labs data. When an agent proposes a change, that change lands as a pull request or merge request in your source control provider, never as a direct write to Endor Labs. See [Trust and attribution](/secure-ai-coding/agents-hub/trust-and-attribution) for the full model.

## Open the Agents Hub

Select **Agents Hub** from the left sidebar. The catalog lists every published agent, with an **Active agents** section for the agents your team has used recently. You can filter the catalog by category or search it by name. Select any agent to see what it does and which AI coding assistants it supports.

See [Browse agents](/secure-ai-coding/agents-hub/browse-agents) for a full tour of the catalog, the agent detail view, and the catalog states.

## Run an agent

The agents run inside your AI coding assistant, not in the browser. To start using an agent, install the [Agent Kit](/secure-ai-coding/agent-kit) in your host, then invoke the agent through that host.

When an agent reaches Endor Labs data during a run, it authenticates with a short-lived, read-only credential that Endor Labs attributes to you. You don't hand the agent your API key. See [Delegated agent credentials](/secure-ai-coding/agents-hub/delegated-credentials) for how this works and what to set up.

## Monitor usage

The Agents Hub shows you how your team uses each agent, including its **Tasks** count and **Last run** time. This view is usage metering, not a compliance audit log. See [Agent activity](/secure-ai-coding/agents-hub/activity) for what the numbers mean and how to read them.

## Agents in the catalog

The Agents Hub publishes these agents. Each one shows its category in the catalog, and you can filter on that category to narrow the list.

<YamlTable>
  {`
    - Agent: AI SAST Remediation
    Category: Remediation
    What_it_does: Triages and remediates Endor Labs AI SAST findings with exploit evidence and approval-gated fixes.
    - Agent: SCA Remediation
    Category: Remediation
    What_it_does: Plans and applies approval-gated SCA fixes with upgrade-risk evidence and local validation.
    - Agent: Remediation Planning
    Category: Remediation
    What_it_does: Compares read-only remediation options and recommends the safest evidence-backed next step.
    - Agent: Dependency Reviewer
    Category: Research & Investigate
    What_it_does: Reviews package versions, package risk, or repository dependencies using bounded evidence.
    - Agent: OSS Upgrade Investigator
    Category: Research & Investigate
    What_it_does: Compares Endor Labs upgrade candidates, risk, breaking changes, and code impact.
    - Agent: Findings Browser
    Category: Research & Investigate
    What_it_does: Browses and filters existing Endor Labs findings with clear scope, pagination, and evidence gaps.
    - Agent: Vulnerability Explainer
    Category: Research & Investigate
    What_it_does: Explains vulnerability severity, exploitability, affected versions, and recommended remediation.
    - Agent: CI/CD And Supply Chain Posture
    Category: Compliance
    What_it_does: Scores CI/CD and supply chain posture from read-only Endor Labs and repository evidence.
    - Agent: Configuration Automation
    Category: Troubleshooting
    What_it_does: Finds GitHub-to-Endor Labs onboarding and monitored-branch coverage gaps without making changes.
    - Agent: Troubleshooting
    Category: Troubleshooting
    What_it_does: Diagnoses Endor Labs setup and workflow problems using focused read-only evidence.
    - Agent: Malware Responder
    Category: Incident Response
    What_it_does: Correlates current malware intelligence with Endor Labs inventory to assess tenant exposure.
    `}
</YamlTable>

For how each agent installs into a host, see the [Agent Kit](/secure-ai-coding/agent-kit).

## Availability

Requires `endorctl` v1.7.1088 or later.

Endor Labs authors and signs the catalog. The agents in the Agents Hub are designed to be implemented in your AI SDLC and workflows. You can view the agents available for implementation in the Agents Hub, but you can't yet add a custom agent inside Endor Labs. To see the available agents in more detail, and to learn how to create your own custom agent, see the open source [Endor Labs Agent Kit](https://github.com/endorlabs/endor-labs-agent-kit).

## Explore the Agents Hub

<CardGroup cols={2}>
  <Card title="Browse agents" icon="magnifying-glass" href="/secure-ai-coding/agents-hub/browse-agents">
    Find an agent in the catalog, read what it does, and install it in your AI coding assistant.
  </Card>

  <Card title="Agent activity" icon="chart-line" href="/secure-ai-coding/agents-hub/activity">
    Read the usage metrics the Agents Hub reports for each agent in your tenant.
  </Card>

  <Card title="Delegated credentials" icon="key" href="/secure-ai-coding/agents-hub/delegated-credentials">
    See how an agent reaches Endor Labs data under a short-lived, read-only token attributed to you.
  </Card>

  <Card title="Trust and attribution" icon="shield-check" href="/secure-ai-coding/agents-hub/trust-and-attribution">
    Understand how Endor Labs signs the catalog and attributes every agent action to a person.
  </Card>
</CardGroup>
