Skip to content

feat(agents): external NAT agents — backend (register, chat, eval, health)#763

Draft
marcusds wants to merge 10 commits into
mainfrom
register-external-agents-backend/mschwab
Draft

feat(agents): external NAT agents — backend (register, chat, eval, health)#763
marcusds wants to merge 10 commits into
mainfrom
register-external-agents-backend/mschwab

Conversation

@marcusds

@marcusds marcusds commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Backend for external NAT agents — agents that run outside NeMo Platform, registered by URL and discovered via their A2A agent card, reachable without a NAT deployment. (Studio UI is a separate stacked PR that depends on this.)

What's included

Entity + create

  • Agent gains source (managed|external), endpoint, card.
  • POST /agents takes config XOR url; a url fetches the A2A card (timeout + size-capped; generic error to avoid a port-scan oracle) and stores an external agent. Structural URL validation.

A2A bridge (dependency-free JSON-RPC, a2a.py)

  • OpenAI chat/completionsmessage/send + message/stream (streaming), reachable via the Studio route and the CLI/SDK agent-name proxy (/-/v1/chat/completions).
  • NAT /generate/fullmessage/send, so external agents evaluate through the existing nat eval pipeline with unchanged eval configs.
  • Multi-turn: NAT's A2A executor is message-only, so the OpenAI history is folded into the message (single turn verbatim; multi-turn as a transcript).
  • Hardening: byte-capped reads, A2A failures logged, targets the vetted registered endpoint (never the card's self-reported url).

Lifecycle + gating

  • POST /agents/{name}/refresh (re-fetch card), GET /agents/{name}/reachability (liveness probe).
  • External agents cannot be deployed (400). Single is_external_agent predicate.

Testing

~100 unit tests (respx + real-socket), ruff + ty clean. Live E2E against a real nat a2a serve agent (chat + full nat eval through the bridge, accuracy 0.79).

Follow-ups (not in this PR)

Auth to external agents (would mirror IGW ModelProvider secret ref + header template); SSRF is platform-wide operator-trust (no per-feature guard); Python SDK (Stainless) regen at release; docs.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added external-agent creation via request URL with automatic A2A card fetching/storage and persisted managed-vs-external metadata.
    • Added external-agent reachability (probe) and refresh (re-fetch card) endpoints.
    • Extended the gateway with external-agent chat/completions bridging and NAT-style generate support with SSE streaming.
    • Updated the OpenAPI spec to document managed vs external semantics and request validation rules.
  • Bug Fixes

    • Prevented deploying external agents as managed deployments.
    • Enforced “exactly one of config or url” for agent creation.
    • Improved SSRF/redirect protections and sanitized logged identifiers.
  • Tests

    • Expanded unit coverage for external-agent creation, refresh/reachability, gateway bridging, streaming behavior, and protections.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 703f5480-387e-4b8b-b096-f942ad988901

📥 Commits

Reviewing files that changed from the base of the PR and between 53a9437 and 459338b.

📒 Files selected for processing (2)
  • plugins/nemo-agents/src/nemo_agents_plugin/a2a.py
  • plugins/nemo-agents/tests/unit/test_a2a.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugins/nemo-agents/src/nemo_agents_plugin/a2a.py

📝 Walkthrough

Walkthrough

External-agent support adds managed/external agent contracts, A2A discovery and messaging, lifecycle endpoints, deployment safeguards, gateway translation, and supporting tests.

Changes

External agent support

Layer / File(s) Summary
Agent contracts and validation
plugins/nemo-agents/src/nemo_agents_plugin/entities.py, plugins/nemo-agents/src/nemo_agents_plugin/schema.py, plugins/nemo-agents/openapi/openapi.yaml, plugins/nemo-agents/src/nemo_agents_plugin/log_utils.py, plugins/nemo-agents/tests/unit/test_entities.py, plugins/nemo-agents/tests/unit/test_log_utils.py
Agent models and requests distinguish managed and external agents, enforce config/url exclusivity, document lifecycle APIs, and sanitize logged identifiers.
A2A discovery and messaging
plugins/nemo-agents/src/nemo_agents_plugin/a2a.py, plugins/nemo-agents/tests/unit/test_a2a.py
A2A card discovery, reachability probing, JSON-RPC messaging, SSE parsing, text extraction, size limits, redirect handling, and SSRF checks are implemented and tested.
External agent lifecycle endpoints
plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py, plugins/nemo-agents/tests/unit/test_agents_api.py
Agent creation fetches and stores external cards; refresh updates cards; reachability returns endpoint liveness.
Deployment eligibility
plugins/nemo-agents/src/nemo_agents_plugin/api/v2/deployments.py, plugins/nemo-agents/tests/unit/test_deployments_api.py
External agents are rejected from deployment creation, while managed deployment creation remains tested.
External gateway bridge
plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py, plugins/nemo-agents/tests/unit/test_gateway.py
External agents are bridged through A2A for chat/completions and generate endpoints with JSON and SSE response formats.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Gateway
  participant AgentStore
  participant A2AUtilities
  Client->>Gateway: chat/completions request
  Gateway->>AgentStore: load Agent
  AgentStore-->>Gateway: external endpoint
  Gateway->>A2AUtilities: send_a2a_message or stream_a2a_message
  A2AUtilities-->>Gateway: response text or deltas
  Gateway-->>Client: OpenAI JSON or SSE
Loading

Possibly related PRs

Suggested reviewers: svvarom, mckornfield, benmccown

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.31% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: backend support for external NAT agents with registration, chat, evaluation, and health features.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch register-external-agents-backend/mschwab
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch register-external-agents-backend/mschwab

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/nemo-agents/openapi/openapi.yaml`:
- Around line 2785-2793: Update the CreateAgentRequest schema to encode the
mutually exclusive config/url requirement with oneOf branches, making one branch
require config and the other require url while preserving the existing name
requirement and property definitions.

In `@plugins/nemo-agents/src/nemo_agents_plugin/a2a.py`:
- Around line 142-149: Update the probe loop around the client.get call to use
httpx.AsyncClient.stream with a GET request and an async context manager,
checking only the response status_code before returning. Preserve the existing
HTTPError handling and path iteration while ensuring the response body is never
fully buffered.
- Around line 276-284: Update the response-reading loop in the async stream
handling around resp.aiter_bytes() to preserve UTF-8 characters split across
chunks: keep incoming data as bytes, use an incremental UTF-8 decoder when
producing text, and only then split and parse SSE lines. Retain the existing
total-byte limit and buffering behavior.
- Around line 92-96: Prevent SSRF for external-agent URLs by validating resolved
destinations against loopback, private, link-local, and other disallowed address
ranges before any request, and by preventing or revalidating every redirect at
the HTTP sink. Apply the shared protection to fetch_agent_card at
plugins/nemo-agents/src/nemo_agents_plugin/a2a.py lines 92-96, send_a2a_message
at lines 208-209, and stream_a2a_message at lines 268-269; update
CreateAgentRequest validation in
plugins/nemo-agents/src/nemo_agents_plugin/schema.py lines 54-66 to enforce the
same destination policy rather than syntax-only http(s) validation.

In `@plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py`:
- Around line 520-540: Update _stream_openai_from_a2a to accept the external
agent name, then log a warning with that name and the caught A2AMessageError in
its exception handler before yielding the embedded SSE error content. Update
every call site, including the additional location noted in the review, to pass
name and preserve the existing streaming response behavior.
- Around line 143-148: Sanitize entity-store failures in both lookup paths: in
plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py lines 143-148 and
563-568, log the caught exception through the existing logging mechanism and
replace the raw str(exc) HTTP 500 detail with a fixed, non-sensitive error
message. Preserve the existing 404 handling for NemoEntityNotFoundError.
- Around line 466-490: Update _conversation_prompt to retain system messages in
the conversation context and require the final relevant message to have role
"user"; return an empty string when the last relevant turn is not user. Preserve
existing single-turn handling and transcript formatting while labeling system
turns appropriately.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 43ecfbbc-2a8b-4c0c-bd45-030e2f74b37d

📥 Commits

Reviewing files that changed from the base of the PR and between 44cee38 and 1e75a83.

📒 Files selected for processing (11)
  • plugins/nemo-agents/openapi/openapi.yaml
  • plugins/nemo-agents/src/nemo_agents_plugin/a2a.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/deployments.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py
  • plugins/nemo-agents/src/nemo_agents_plugin/entities.py
  • plugins/nemo-agents/src/nemo_agents_plugin/schema.py
  • plugins/nemo-agents/tests/unit/test_a2a.py
  • plugins/nemo-agents/tests/unit/test_agents_api.py
  • plugins/nemo-agents/tests/unit/test_deployments_api.py
  • plugins/nemo-agents/tests/unit/test_gateway.py

Comment thread plugins/nemo-agents/openapi/openapi.yaml
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Outdated
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Outdated
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Outdated
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Outdated
@marcusds
marcusds marked this pull request as draft July 17, 2026 17:50
…alth)

Add a first-class "external" agent that runs outside NeMo Platform: registered
by URL, discovered via its A2A card, reachable without a NAT deployment.

Entity + create:
- Agent gains source (managed|external), endpoint, card
- POST /agents takes config XOR url; url fetches the A2A card (timeout +
  size-capped, generic error to avoid a port-scan oracle) and stores an
  external agent; structural URL validation

A2A bridge (dependency-free JSON-RPC in a2a.py):
- chat/completions <-> message/send + message/stream (streaming), reached via
  the Studio route and the CLI/SDK agent-name proxy (/-/v1/chat/completions)
- NAT /generate/full <-> message/send, so external agents evaluate through the
  existing `nat eval` pipeline with unchanged eval configs
- multi-turn: NAT's A2A executor is message-only, so the OpenAI history is
  folded into the message (single turn verbatim; multi-turn as a transcript)
- byte-capped reads; A2A failures logged; targets the vetted registered
  endpoint, never the card's self-reported url

Lifecycle + gating:
- POST /agents/{name}/refresh (re-fetch card), GET /agents/{name}/reachability
- external agents cannot be deployed (400); is_external_agent predicate

~100 unit tests (respx + real-socket); ruff + ty clean.

Signed-off-by: mschwab <mschwab@nvidia.com>
marcusds added a commit that referenced this pull request Jul 17, 2026
Studio support for external agents (registered by URL). Depends on the backend
PR (#763): needs the source/endpoint/card Agent fields, config-XOR-url create,
and the refresh/reachability endpoints.

- Register modal: Connect running agent by endpoint URL (structural validation)
- Agents table: External badge; onboarding empty state + NAT docs link
- Agent panel:
  - Workflow tab — managed: config graph + YAML (credentials redacted);
    external: A2A card skills + endpoint
  - Chat playground wired to the external chat bridge (streaming, multi-turn)
  - Details: Source/Endpoint, reachability badge + Refresh card button
  - Deploy/Logs gated for external; Evaluate available (via bridge)
  - resolve the panel agent via GET /agents/{name} (not a page-1 list scan)
- shared isExternalAgent predicate + ExternalAgentNotice

Unit tests for read models + credential redaction; typecheck + lint clean.

Signed-off-by: mschwab <mschwab@nvidia.com>
@marcusds
marcusds force-pushed the register-external-agents-backend/mschwab branch from 1e75a83 to 4915ed8 Compare July 17, 2026 18:27
for path in AGENT_CARD_PATHS:
url = _card_url(base, path)
try:
async with client.stream("GET", url, headers={"Accept": "application/json"}) as resp:
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 27117/34832 77.8% 62.1%
Integration Tests 15892/33443 47.5% 19.9%

- probe_agent_reachable streams and checks status only (no unbounded body read)
- external chat honors the card's capabilities.streaming: a streaming request to
  a non-streaming agent uses message/send wrapped in one SSE chunk
- streaming uses an incremental UTF-8 decoder so a multi-byte char split across
  network chunks isn't corrupted
- streaming primes the first delta so a failure before any token returns 502;
  a mid-stream failure is logged and ends without a normal "stop" finish
- the conversation prompt preserves system messages and anchors on the last user
  turn (drops trailing assistant)
- managed-agent proxy no longer double-fetches the Agent entity

Skipped (P2): full AgentCard schema validation — would require taking a2a-sdk as
a runtime dep; cards stay loosely validated (operator-trust) and incompatible
ones fail at invocation with a clear error.

Signed-off-by: mschwab <mschwab@nvidia.com>
@marcusds
marcusds force-pushed the register-external-agents-backend/mschwab branch from 4915ed8 to e098344 Compare July 17, 2026 19:18
…ize gateway errors

- a2a.py: follow_redirects=False on all httpx sinks so a registered external
  agent URL can't 302 the backend into loopback/metadata endpoints (SSRF).
- gateway.py: entity-store lookup failures no longer leak str(exc); log and
  return a fixed detail, matching the agents.py CRUD routes.
- Regression tests for both.

Signed-off-by: mschwab <mschwab@nvidia.com>
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py Fixed
marcusds added 2 commits July 17, 2026 13:00
Signed-off-by: mschwab <mschwab@nvidia.com>
…bing

- a2a.py: block external-agent URLs that resolve to private/loopback/link-local/
  reserved addresses (e.g. 169.254.169.254 metadata) before any request, in
  fetch/send/stream/probe. Gated by NEMO_AGENTS_ALLOW_PRIVATE_AGENT_HOSTS for
  local dev. (py/full-ssrf #4269)
- log_utils.scrub(): strip CR/LF from user-controlled values before logging;
  applied across gateway/agents/a2a logger calls. (py/log-injection #4270-4279)
- Tests for the guard (blocks private, allows public) and the scrubber.

Signed-off-by: mschwab <mschwab@nvidia.com>
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Fixed
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Fixed
marcusds added 2 commits July 17, 2026 13:24
…tion

_endpoint_host output feeds logger calls whose URL arg is user-controlled;
sanitize at the source so CodeQL clears py/log-injection #4280/#4281.

Signed-off-by: mschwab <mschwab@nvidia.com>
Runtime enforces exactly-one-of config/url via model_validator, but the
generated OpenAPI schema allowed both/neither. Add json_schema_extra oneOf
so generated client contracts match runtime behavior.

Signed-off-by: mschwab <mschwab@nvidia.com>
@marcusds
marcusds marked this pull request as ready for review July 17, 2026 21:58
@marcusds
marcusds requested a review from mckornfield July 17, 2026 21:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/nemo-agents/src/nemo_agents_plugin/a2a.py`:
- Around line 260-261: Remove the endpoint value from the A2AMessageError
messages in both HTTP transport exception handlers, including the handler around
the shown raise and the second handler at the other referenced location.
Preserve the exception type and chained cause while reporting only a generic
connection failure and the safe exception class name, so embedded credentials
cannot reach gateway responses.

In `@plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py`:
- Around line 636-641: Require the request method to be POST before dispatching
to the external agent in the handler around _external_a2a_endpoint. Guard both
the _external_openai_chat and _serve_external_generate_full branches, returning
the existing method-not-allowed response for GET, HEAD, OPTIONS, and other
non-POST methods while preserving current POST behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e90057af-c151-4739-a765-ad924d1c91a3

📥 Commits

Reviewing files that changed from the base of the PR and between 1e75a83 and 11e13f2.

⛔ Files ignored due to path filters (1)
  • sdk/python/nemo-platform/.github/workflows/ci.yml is excluded by !sdk/**
📒 Files selected for processing (14)
  • plugins/nemo-agents/openapi/openapi.yaml
  • plugins/nemo-agents/src/nemo_agents_plugin/a2a.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/deployments.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py
  • plugins/nemo-agents/src/nemo_agents_plugin/entities.py
  • plugins/nemo-agents/src/nemo_agents_plugin/log_utils.py
  • plugins/nemo-agents/src/nemo_agents_plugin/schema.py
  • plugins/nemo-agents/tests/unit/test_a2a.py
  • plugins/nemo-agents/tests/unit/test_agents_api.py
  • plugins/nemo-agents/tests/unit/test_deployments_api.py
  • plugins/nemo-agents/tests/unit/test_entities.py
  • plugins/nemo-agents/tests/unit/test_gateway.py
  • plugins/nemo-agents/tests/unit/test_log_utils.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • plugins/nemo-agents/src/nemo_agents_plugin/schema.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/deployments.py
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py
  • plugins/nemo-agents/src/nemo_agents_plugin/entities.py
  • plugins/nemo-agents/openapi/openapi.yaml

Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Outdated
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/agents.py
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py
Comment thread plugins/nemo-agents/src/nemo_agents_plugin/a2a.py Outdated
},
}
try:
async with httpx.AsyncClient(timeout=_MESSAGE_TIMEOUT_S, follow_redirects=False) as client:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

many these async with nesting bits are so nested lol


assert resp.status_code == 200
assert resp.headers["content-type"].startswith("text/event-stream")
assert '"content": "sun"' in resp.text

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these sorta asserts, woof

marcusds added 3 commits July 22, 2026 10:11
The agent-name proxy read route (GET/HEAD/OPTIONS) reaches _serve_external_agent,
so a bodied GET could invoke an external agent under read scope. Guard the bridge
with a 405 unless the method is POST.

Signed-off-by: mschwab <mschwab@nvidia.com>
Address review nits: rewrite _address_disallowed as any() over a list of flags,
and dispatch OpenAI message content via match/case.

Signed-off-by: mschwab <mschwab@nvidia.com>
The A2AMessageError from transport failures flows into gateway HTTP/SSE
responses; an external agent endpoint can embed credentials. Drop the endpoint
from the message, keeping only the exception class name.

Signed-off-by: mschwab <mschwab@nvidia.com>
@marcusds
marcusds marked this pull request as draft July 23, 2026 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants