Skip to content

feat(messaging): add manifest compiler#4069

Merged
sandl99 merged 2 commits into
u/sdang/messaging-hooks-channels-3993-3994from
u/sdang/messaging-manifest-compiler-3994
May 25, 2026
Merged

feat(messaging): add manifest compiler#4069
sandl99 merged 2 commits into
u/sdang/messaging-hooks-channels-3993-3994from
u/sdang/messaging-manifest-compiler-3994

Conversation

@sandl99
Copy link
Copy Markdown
Contributor

@sandl99 sandl99 commented May 22, 2026

Summary

Adds the phase-1 messaging manifest compiler that converts channel manifests into a serializable sandbox messaging plan. The compiler resolves channel inputs through env keys and interactive enrollment hooks, then delegates credential, policy, render, build-step, state-update, and health-check planning to small pure engines.

Related Issue

Fixes #3994

Changes

  • Add ManifestCompiler with interactive enrollment-hook input resolution and env-key input initialization.
  • Add compiler plan engines for credential bindings, network policy, agent render fragments, build steps, state updates, and health checks.
  • Expand SandboxMessagingPlan and related manifest plan types to the top-level plan shape required by [Messaging] Add pure manifest compiler and plan engines #3994.
  • Add coverage for built-in Telegram/Discord/Slack/WeChat/WhatsApp plans, Hermes WeChat policy aliasing, non-interactive env input behavior, secret-free JSON plans, disabled channels, and a synthetic non-built-in channel.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • make docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Additional verification performed:

  • npm test -- --project cli src/lib/messaging passes.
  • npm run typecheck:cli passes.
  • npm run lint -- src/lib/messaging passes with the existing unrelated warning in src/lib/onboard/child-exit-tracker.test.ts.
  • git diff --check passes.
  • npm run source-shape:check passes.
  • npx prek run --all-files and the normal pre-push hook were attempted and currently fail in unrelated full CLI doctor/debug/snapshot tests outside the messaging compiler changes.

Signed-off-by: San Dang sdang@nvidia.com

Summary by CodeRabbit

  • New Features

    • Added manifest compilation system for messaging channels with support for multiple agents and workflows
    • Implemented credential binding and authentication management
    • Added network policy configuration and agent rendering capabilities
    • Introduced health check and build step planning
    • Added state persistence and hydration management
    • Implemented placeholder resolution for sandbox names and credentials
  • Tests

    • Added comprehensive test suite validating compilation behavior, credential handling, and plan serialization

Review Change Stack

Signed-off-by: San Dang <sdang@nvidia.com>
@sandl99 sandl99 self-assigned this May 22, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

E2E Advisor Recommendation

Required E2E: messaging-providers-e2e, channels-add-remove-e2e, channels-stop-start-e2e
Optional E2E: telegram-injection-e2e, hermes-slack-e2e, network-policy-e2e, messaging-compatible-endpoint-e2e

Dispatch hint: messaging-providers-e2e,channels-add-remove-e2e,channels-stop-start-e2e

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/u/sdang/messaging-hooks-channels-3993-3994
Head: HEAD
Confidence: high

Required E2E

  • messaging-providers-e2e (high): Validates the full messaging provider/placeholder/no-secret-leak chain for Telegram, Discord, Slack, WeChat, and WhatsApp. This is the highest-signal existing E2E for the new credential-binding and setup-applier provider paths.
  • channels-add-remove-e2e (high): Covers messaging channel add/remove lifecycle, provider detach, rebuild survival, and auto-application/removal of messaging network policy presets. This directly exercises the new workflow-planner and policy-resolver behavior for channel lifecycle operations.
  • channels-stop-start-e2e (very high): Exercises OpenClaw and Hermes channel stop/start/remove flows across Telegram, Discord, WeChat, Slack, and WhatsApp. Required because the PR changes generic workflow planning, hook filtering by agent, credentials, and WeChat post-agent-install behavior that can break lifecycle transitions.

Optional E2E

  • telegram-injection-e2e (high): Useful targeted security confidence for Telegram config/message handling after changing the Telegram manifest and adding a reachability hook. Not strictly merge-blocking because the PR does not appear to change Telegram message execution/parsing code.
  • hermes-slack-e2e (high): Provides extra confidence that generic manifest compilation/rendering and credential placeholders still work for Hermes messaging after the new compiler and applier abstractions.
  • network-policy-e2e (high): Broad policy regression coverage for deny-by-default, presets, live policy changes, and hot reload. Helpful because messaging policy key resolution changed, but existing messaging lifecycle tests cover the directly touched messaging presets more specifically.
  • messaging-compatible-endpoint-e2e (high): Additional Telegram plus inference.local confidence for the Telegram provider/config path, especially if maintainers want coverage for Telegram messaging alongside provider-routed inference.

New E2E recommendations

  • WeChat OpenClaw plugin setup via manifest applier (high): Existing messaging E2Es cover WeChat credential/provider and some config surfaces, but there is no dedicated scenario-suite entry for WeChat like messaging-telegram/discord/slack. The PR adds WeChat post-agent-install build-file outputs for openclaw-weixin plugin install/load paths and account files, which deserves direct scenario coverage.
    • Suggested test: Add a messaging-wechat validation suite/scenario that onboards OpenClaw with fake WECHAT_* env values and asserts provider binding, no raw token leak, /sandbox/.openclaw/openclaw-weixin account files, plugin installs/load paths, and the wechat_bridge policy preset.
  • Telegram reachability hook (medium): The PR adds a Telegram reachability-check hook, but existing E2E coverage mostly validates provider/placeholder plumbing and injection safety. A hermetic fake Telegram getMe endpoint would catch regressions in reachability-check routing and failure behavior without requiring a real bot token.
    • Suggested test: Add a Telegram reachability E2E or validation-suite step using a local fake Telegram API/proxy that verifies reachability-check runs on add/onboard and aborts or succeeds according to hook result.
  • Manifest compiler/applier runtime integration (medium): Most changed compiler/applier code is heavily unit-tested, but there is limited existing E2E evidence that the serialized messaging plan env handoff is used end-to-end during real CLI onboarding/channel operations.
    • Suggested test: Add an E2E assertion that captures the manifest-compiled messaging plan path during non-interactive onboard/add-channel, then verifies credentials, policy presets, agent render targets, and hook-applied files were all applied from the serialized plan.

Dispatch hint

  • Workflow: E2E / Nightly
  • jobs input: messaging-providers-e2e,channels-add-remove-e2e,channels-stop-start-e2e

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e85aa396-32db-4734-bba0-5dd7e6830c18

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch u/sdang/messaging-manifest-compiler-3994

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

PR Review Advisor

Findings: 3 needs attention, 5 worth checking, 0 nice ideas
Since last review: 1 prior item resolved, 5 still apply, 3 new items found

Review findings

🛠️ Needs attention

  • Side-effectful setup applier contradicts architecture-only scope (src/lib/messaging/applier/setup-applier.ts:98): The linked issue says this is architecture-only work and lists non-goals including not creating providers and not modifying OpenClaw or Hermes config files. This revision adds MessagingSetupApplier methods that create/update OpenShell providers, apply policy presets, run apply/post-install hooks, and write sandbox agent config/build-file outputs; src/lib/messaging/index.ts also exports the applier as part of the public messaging surface.
    • Recommendation: Split the side-effectful setup applier into a follow-up issue/PR, or keep this PR limited to serializable planning contracts. If the applier is intentionally included now, update the linked acceptance scope and add the missing runtime/security validation before using or exporting it publicly.
    • Evidence: Issue clauses: "This is architecture-only work: no production workflow should use the compiler yet." and non-goals "Do not create providers." / "Do not modify OpenClaw or Hermes config files." Diff evidence: applyAgentConfigAtOpenShell writes sandbox files, applyCredentialsAtOpenShell creates/updates providers, applyPolicyAtOpenShell applies presets, and src/lib/messaging/index.ts exports ./applier.
  • Disabled channel hooks can still run during setup application (src/lib/messaging/applier/setup-applier.ts:218): MessagingSetupApplier builds apply/post-agent-install hook requests from every channel in plan.channels without checking channel.active. The compiler keeps hook references on disabled configured channels even though it excludes their credential/policy/render/build plans. A disabled WeChat channel can therefore still run the post-agent-install hook and write OpenClaw WeChat files when an applier consumes the plan.
    • Recommendation: Filter hook requests to active channels, or strip side-effectful hooks from inactive channel plans. Add a regression test with a disabled configured WeChat channel proving no post-agent-install hook is requested or executed.
    • Evidence: hookRequestsForPhases flatMaps plan.channels and filters only by hook phase. compileChannel always clones manifest.hooks for the channel, while activeManifests is only used for credentialBindings/networkPolicy/agentRender/buildSteps/stateUpdates/healthChecks.
  • Compiler still executes enrollment and reachability hooks (src/lib/messaging/compiler/manifest-compiler.ts:219): The linked issue asks for a pure manifest compiler and pure engines. compile() can execute runMessagingHook for enrollment hooks and reachability checks, so callers that pass a real hook registry can trigger prompting, QR login, credential, filesystem, or network behavior during what should be a planning step.
    • Recommendation: Keep ManifestCompiler pure by accepting already-collected enrollment outputs/reachability facts as compiler inputs, or move hook execution into a separate side-effectful enrollment phase outside the compiler and document/enforce that boundary.
    • Evidence: resolveChannelInputs runs enrollment hooks for interactive onboard/add-channel plans and then runs reachability-check hooks when required inputs are available; runCompilerHook calls runMessagingHook at manifest-compiler.ts:219.

🔎 Worth checking

  • Unknown policy presets pass through to application without an allowlist (src/lib/messaging/compiler/engines/policy-resolver.ts:58): For presets that are not built in or agent aliases, the resolver emits policyKeys: [presetName] with source: manifest. The applier later applies plan.networkPolicy.presets directly. If manifests or serialized plans can be plugin/user-controlled, this lets an arbitrary string request a network policy preset unless a later layer independently validates it.
    • Recommendation: Resolve policy presets against a trusted allowlist/registry and fail closed for unknown presets, or mark custom policy keys as privileged and require explicit approval before application.
    • Evidence: planManifestPolicyEntries uses policyKeys: builtinKeys ?? [presetName] and source: builtinKeys ? 'builtin' : 'manifest'. applyPolicyAtOpenShell applies unique plan.networkPolicy.presets for the sandbox.
  • Hook build-file outputs may target arbitrary absolute sandbox paths (src/lib/messaging/applier/setup-applier.ts:582): resolveHookBuildFileTarget returns any hook-provided absolute path unchanged, and writeSandboxFile then writes that path through sandbox exec. A malicious or buggy hook output could overwrite unexpected files inside the sandbox rather than only approved messaging config/build roots.
    • Recommendation: Reject absolute hook output paths unless they match a small allowlist of approved sandbox config roots, normalize relative paths, and deny traversal or sensitive destinations. Add negative tests for absolute paths and traversal-like paths.
    • Evidence: readHookBuildFile accepts a hook output path string; resolveHookBuildFileTarget immediately returns path when path.startsWith("/"); writeSandboxFile then writes contents to that target.
  • Config env values are copied into plans without validValues or format checks (src/lib/messaging/compiler/manifest-compiler.ts:253): Channel inputs can declare validValues, but readInputEnvValue reads raw process.env strings and resolveChannelInput stores non-secret config values directly in SandboxMessagingInputReference.value. Invalid or injection-shaped config can cross the compiler boundary into render templates, rebuild hydration, hooks, and applier writes.
    • Recommendation: Validate config inputs against manifest validValues plus per-field length/format rules before including them in plans or hook inputs. Add negative tests for invalid validValues, malformed IDs/URLs, and oversized values.
    • Evidence: ChannelInputBaseSpec defines validValues in manifest/types.ts, but readInputEnvValue returns the raw env value and resolveChannelInput stores it for config inputs without checking validValues.
  • Negative tests do not cover disabled hooks, malicious paths, invalid config, or policy denial (src/lib/messaging/applier/setup-applier.test.ts:1): The added unit coverage exercises many happy paths and secret-redaction paths, but the high-risk boundaries introduced here lack negative tests: disabled channels with post-install hooks, unknown policy presets, invalid validValues, malicious build-file paths, malformed plans from env, and applier failure modes.
    • Recommendation: Add focused negative tests for these denial paths and keep them close to the compiler/applier functions that enforce the security boundary.
    • Evidence: Existing tests cover serializable plans, provider upsert arguments, redaction, render writes, hook output writes, policy happy path, and workflow planning. They do not assert rejection or non-execution for the new risky denial cases.
  • Active messaging work overlaps this PR across the same files: The trusted drift context shows overlapping active messaging work on all changed files. The code being patched exists, but the overlap increases the risk that public exports, manifest type shape, applier behavior, and compiler contracts diverge from adjacent work.
    • Recommendation: Coordinate the stack/order with the overlapping messaging PR and re-check the combined diff for type/API drift after rebasing or landing the dependent work.
    • Evidence: openPrOverlaps reports PR feat(messaging): rebuild new manifest-style messaging architecture #4050 with the same 27 changed files, including public exports, manifest types, compiler, applier, channel manifests, hooks, and tests.

🌱 Nice ideas

  • None.
Since last review details

Current findings:

  • Side-effectful setup applier contradicts architecture-only scope (src/lib/messaging/applier/setup-applier.ts:98): The linked issue says this is architecture-only work and lists non-goals including not creating providers and not modifying OpenClaw or Hermes config files. This revision adds MessagingSetupApplier methods that create/update OpenShell providers, apply policy presets, run apply/post-install hooks, and write sandbox agent config/build-file outputs; src/lib/messaging/index.ts also exports the applier as part of the public messaging surface.
    • Recommendation: Split the side-effectful setup applier into a follow-up issue/PR, or keep this PR limited to serializable planning contracts. If the applier is intentionally included now, update the linked acceptance scope and add the missing runtime/security validation before using or exporting it publicly.
    • Evidence: Issue clauses: "This is architecture-only work: no production workflow should use the compiler yet." and non-goals "Do not create providers." / "Do not modify OpenClaw or Hermes config files." Diff evidence: applyAgentConfigAtOpenShell writes sandbox files, applyCredentialsAtOpenShell creates/updates providers, applyPolicyAtOpenShell applies presets, and src/lib/messaging/index.ts exports ./applier.
  • Disabled channel hooks can still run during setup application (src/lib/messaging/applier/setup-applier.ts:218): MessagingSetupApplier builds apply/post-agent-install hook requests from every channel in plan.channels without checking channel.active. The compiler keeps hook references on disabled configured channels even though it excludes their credential/policy/render/build plans. A disabled WeChat channel can therefore still run the post-agent-install hook and write OpenClaw WeChat files when an applier consumes the plan.
    • Recommendation: Filter hook requests to active channels, or strip side-effectful hooks from inactive channel plans. Add a regression test with a disabled configured WeChat channel proving no post-agent-install hook is requested or executed.
    • Evidence: hookRequestsForPhases flatMaps plan.channels and filters only by hook phase. compileChannel always clones manifest.hooks for the channel, while activeManifests is only used for credentialBindings/networkPolicy/agentRender/buildSteps/stateUpdates/healthChecks.
  • Compiler still executes enrollment and reachability hooks (src/lib/messaging/compiler/manifest-compiler.ts:219): The linked issue asks for a pure manifest compiler and pure engines. compile() can execute runMessagingHook for enrollment hooks and reachability checks, so callers that pass a real hook registry can trigger prompting, QR login, credential, filesystem, or network behavior during what should be a planning step.
    • Recommendation: Keep ManifestCompiler pure by accepting already-collected enrollment outputs/reachability facts as compiler inputs, or move hook execution into a separate side-effectful enrollment phase outside the compiler and document/enforce that boundary.
    • Evidence: resolveChannelInputs runs enrollment hooks for interactive onboard/add-channel plans and then runs reachability-check hooks when required inputs are available; runCompilerHook calls runMessagingHook at manifest-compiler.ts:219.
  • Unknown policy presets pass through to application without an allowlist (src/lib/messaging/compiler/engines/policy-resolver.ts:58): For presets that are not built in or agent aliases, the resolver emits policyKeys: [presetName] with source: manifest. The applier later applies plan.networkPolicy.presets directly. If manifests or serialized plans can be plugin/user-controlled, this lets an arbitrary string request a network policy preset unless a later layer independently validates it.
    • Recommendation: Resolve policy presets against a trusted allowlist/registry and fail closed for unknown presets, or mark custom policy keys as privileged and require explicit approval before application.
    • Evidence: planManifestPolicyEntries uses policyKeys: builtinKeys ?? [presetName] and source: builtinKeys ? 'builtin' : 'manifest'. applyPolicyAtOpenShell applies unique plan.networkPolicy.presets for the sandbox.
  • Hook build-file outputs may target arbitrary absolute sandbox paths (src/lib/messaging/applier/setup-applier.ts:582): resolveHookBuildFileTarget returns any hook-provided absolute path unchanged, and writeSandboxFile then writes that path through sandbox exec. A malicious or buggy hook output could overwrite unexpected files inside the sandbox rather than only approved messaging config/build roots.
    • Recommendation: Reject absolute hook output paths unless they match a small allowlist of approved sandbox config roots, normalize relative paths, and deny traversal or sensitive destinations. Add negative tests for absolute paths and traversal-like paths.
    • Evidence: readHookBuildFile accepts a hook output path string; resolveHookBuildFileTarget immediately returns path when path.startsWith("/"); writeSandboxFile then writes contents to that target.
  • Config env values are copied into plans without validValues or format checks (src/lib/messaging/compiler/manifest-compiler.ts:253): Channel inputs can declare validValues, but readInputEnvValue reads raw process.env strings and resolveChannelInput stores non-secret config values directly in SandboxMessagingInputReference.value. Invalid or injection-shaped config can cross the compiler boundary into render templates, rebuild hydration, hooks, and applier writes.
    • Recommendation: Validate config inputs against manifest validValues plus per-field length/format rules before including them in plans or hook inputs. Add negative tests for invalid validValues, malformed IDs/URLs, and oversized values.
    • Evidence: ChannelInputBaseSpec defines validValues in manifest/types.ts, but readInputEnvValue returns the raw env value and resolveChannelInput stores it for config inputs without checking validValues.
  • Negative tests do not cover disabled hooks, malicious paths, invalid config, or policy denial (src/lib/messaging/applier/setup-applier.test.ts:1): The added unit coverage exercises many happy paths and secret-redaction paths, but the high-risk boundaries introduced here lack negative tests: disabled channels with post-install hooks, unknown policy presets, invalid validValues, malicious build-file paths, malformed plans from env, and applier failure modes.
    • Recommendation: Add focused negative tests for these denial paths and keep them close to the compiler/applier functions that enforce the security boundary.
    • Evidence: Existing tests cover serializable plans, provider upsert arguments, redaction, render writes, hook output writes, policy happy path, and workflow planning. They do not assert rejection or non-execution for the new risky denial cases.
  • Active messaging work overlaps this PR across the same files: The trusted drift context shows overlapping active messaging work on all changed files. The code being patched exists, but the overlap increases the risk that public exports, manifest type shape, applier behavior, and compiler contracts diverge from adjacent work.
    • Recommendation: Coordinate the stack/order with the overlapping messaging PR and re-check the combined diff for type/API drift after rebasing or landing the dependent work.
    • Evidence: openPrOverlaps reports PR feat(messaging): rebuild new manifest-style messaging architecture #4050 with the same 27 changed files, including public exports, manifest types, compiler, applier, channel manifests, hooks, and tests.

Workflow run details

This is an automated advisory review. A human maintainer must make the final merge decision.

@wscurran
Copy link
Copy Markdown
Contributor

Related open issues:

## Summary
Adds the phase-1 messaging workflow planner for onboard, channel
add/remove/start/stop, and rebuild flows. The planner computes
configured, active, and disabled channel state before delegating to the
manifest compiler, keeping #3995 architecture-only and out of production
CLI paths.

## Related Issue
Fixes #3995

## Changes
- Add `MessagingWorkflowPlanner` with pure `planOnboard`,
`planAddChannel`, `planRemoveChannel`, `planStartChannel`,
`planStopChannel`, and `planRebuild` methods.
- Preserve stopped-but-configured channels, remove channels from
configured and disabled state on remove, and carry registry/session
snapshots through rebuild planning.
- Refine `MessagingCompilerWorkflow` to the planner workflows and
restrict enrollment hooks to selected onboard/add-channel plans.
- Add workflow planner tests for lifecycle state transitions,
deterministic unsupported-channel reporting, rebuild preservation,
secret-free serialization, and avoiding re-enrollment of existing
configured channels.

## Type of Change
- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Verification
- [ ] `npx prek run --all-files` passes
- [ ] `npm test` passes
- [x] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [ ] Docs updated for user-facing behavior changes
- [ ] `make docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

Additional verification performed:
- `npm test -- --project cli src/lib/messaging` passes.
- `npm run typecheck:cli` passes.
- `npm run lint -- src/lib/messaging` passes with the existing unrelated
warning in `src/lib/onboard/child-exit-tracker.test.ts`.
- `npm run source-shape:check` passes.
- `git diff --check` passes.
- Commit and push hooks were bypassed because the full hook path is
currently blocked by unrelated CLI doctor/debug/snapshot failures on
this stack.

---
Signed-off-by: San Dang <sdang@nvidia.com>

---------

Signed-off-by: San Dang <sdang@nvidia.com>
@sandl99 sandl99 merged commit 51e9591 into u/sdang/messaging-hooks-channels-3993-3994 May 25, 2026
16 checks passed
@sandl99 sandl99 deleted the u/sdang/messaging-manifest-compiler-3994 branch May 25, 2026 07:04
@github-actions
Copy link
Copy Markdown
Contributor

E2E Scenario Advisor Recommendation

Required scenario E2E: None
Optional scenario E2E: None

Workflow run

Full scenario advisor summary

E2E Scenario Advisor

Base: origin/u/sdang/messaging-hooks-channels-3993-3994
Head: HEAD
Confidence: high

Required scenario E2E

  • None. No scenario workflow, scenario metadata, scenario runtime, or validation-suite files changed.

Optional scenario E2E

  • None.

Relevant changed files

  • None.

@cv cv added the integration: whatsapp WhatsApp integration or channel behavior label May 30, 2026
@wscurran wscurran added area: messaging Messaging channels, bridges, manifests, or channel lifecycle bug-fix PR fixes a bug or regression feature PR adds or expands user-visible functionality and removed enhancement: feature labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: messaging Messaging channels, bridges, manifests, or channel lifecycle bug-fix PR fixes a bug or regression feature PR adds or expands user-visible functionality integration: whatsapp WhatsApp integration or channel behavior VRDC Issues and PRs submitted by NVIDIA VRDC test team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants