Skip to content

RHIDP-14310: add saved prompts endpoints to intelligent-assistant backend - #4076

Merged
Jdubrick merged 8 commits into
redhat-developer:mainfrom
Jdubrick:add-saved-prompts
Jul 31, 2026
Merged

RHIDP-14310: add saved prompts endpoints to intelligent-assistant backend#4076
Jdubrick merged 8 commits into
redhat-developer:mainfrom
Jdubrick:add-saved-prompts

Conversation

@Jdubrick

Copy link
Copy Markdown
Contributor

Hey, I just made a Pull Request!

https://redhat.atlassian.net/browse/RHIDP-14310

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

Jdubrick added 6 commits July 28, 2026 15:38
Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>
Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>
Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>
Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>
Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>
Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>
@rhdh-gh-app

rhdh-gh-app Bot commented Jul 29, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-intelligent-assistant-backend workspaces/intelligent-assistant/plugins/intelligent-assistant-backend minor v3.1.0
@red-hat-developer-hub/backstage-plugin-intelligent-assistant-common workspaces/intelligent-assistant/plugins/intelligent-assistant-common minor v3.1.0
@red-hat-developer-hub/backstage-plugin-intelligent-assistant workspaces/intelligent-assistant/plugins/intelligent-assistant minor v3.1.0

@rhdh-qodo-merge

Copy link
Copy Markdown

PR Summary by Qodo

Add saved-prompts proxy routes + RBAC permission to intelligent-assistant backend

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add backend routes to proxy LCORE /v1/saved-prompts* APIs via intelligent-assistant.
• Introduce intelligent-assistant.saved-prompts.manage permission and document required RBAC.
• Add MSW fixtures and router/unit tests covering user_id injection and error sanitization.
Diagram

graph TD
  A["Backstage client"] --> B["IA backend router"] --> C["RBAC: saved-prompts.manage"]
  C --> D["apiProxy (+user_id)"] --> G[("Lightspeed Core")]
  C --> E["apiProxy (no user_id)"] --> G
  C --> F["POST handler (sanitized errors)"] --> G
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Proxy POST /v1/saved-prompts through apiProxy (standardize routing)
  • ➕ Single proxy path for all saved-prompts methods (less bespoke code in router.ts)
  • ➕ Centralizes user_id injection and request forwarding behavior
  • ➖ Would require enhancing apiProxy to sanitize/shape upstream error responses consistently (or accepting current proxy error behavior)
2. Introduce a small LCORE client module for saved prompts
  • ➕ Encapsulates URL building, user_id injection, and error sanitization in one reusable place
  • ➕ Makes it easier to evolve contract (e.g., retries, timeouts, typed responses)
  • ➖ Adds a new abstraction layer that may be redundant if apiProxy is the intended standard

Recommendation: Current approach is reasonable given the explicit error-sanitization requirements for POST and the existing apiProxy pattern for simple pass-through routes. If more endpoints need custom error handling, consider promoting that logic into a shared proxy/client layer so POST does not remain a one-off implementation.

Files changed (12) +599 / -2

Enhancement (5) +165 / -1
constant.tsSkip user_id injection for saved-prompts config endpoint +5/-1

Skip user_id injection for saved-prompts config endpoint

• Adds '/v1/saved-prompts/config' to the SKIP_USER_ID_ENDPOINTS set. Ensures the proxy does not append user_id where LCORE expects a global config resource.

workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/constant.ts

router.tsExpose /v1/saved-prompts routes with permission enforcement +65/-0

Expose /v1/saved-prompts routes with permission enforcement

• Adds GET/DELETE saved-prompts routes wired through the existing apiProxy middleware. Implements a custom POST handler that forwards to LCORE with user_id and uses shared error sanitization for non-2xx responses.

workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts

index.tsExport saved-prompts types from common package entrypoint +1/-0

Export saved-prompts types from common package entrypoint

• Re-exports the new savedPrompts module from the package index. Makes saved-prompts interfaces available to downstream consumers.

workspaces/intelligent-assistant/plugins/intelligent-assistant-common/src/index.ts

permissions.tsAdd saved-prompts manage permission and register in permissions list +11/-0

Add saved-prompts manage permission and register in permissions list

• Defines 'lightspeedSavedPromptsManagePermission' ('intelligent-assistant.saved-prompts.manage', action=update). Appends it to the exported 'lightspeedPermissions' array used by permission policies.

workspaces/intelligent-assistant/plugins/intelligent-assistant-common/src/permissions.ts

savedPrompts.tsIntroduce shared Saved Prompts request/response interfaces +83/-0

Introduce shared Saved Prompts request/response interfaces

• Adds typed interfaces for saved prompt entities plus list/config/create/delete payloads. Centralizes the contract for the LCORE saved-prompts API across backend and frontend consumers.

workspaces/intelligent-assistant/plugins/intelligent-assistant-common/src/savedPrompts.ts

Tests (3) +385 / -1
lcsHandlers.tsAdd MSW handlers for LCORE saved-prompts endpoints +45/-0

Add MSW handlers for LCORE saved-prompts endpoints

• Extends mock Lightspeed Core handlers with saved-prompts config/list/create/delete endpoints. Provides predictable fixture responses for tests and local development.

workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/fixtures/lcsHandlers.ts

router.test.tsAdd end-to-end router tests for saved-prompts routes +331/-0

Add end-to-end router tests for saved-prompts routes

• Adds comprehensive tests for GET config/list, POST create, and DELETE prompt, including permission-denied behavior. Verifies user_id injection rules and validates that upstream error details are sanitized for clients.

workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.test.ts

utils.test.tsExtend rewriteProxyPath tests for saved-prompts injection rules +9/-1

Extend rewriteProxyPath tests for saved-prompts injection rules

• Updates skip-endpoint test coverage to include '/v1/saved-prompts/config'. Adds explicit assertions that saved-prompts paths get a user_id query parameter appended.

workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/utils.test.ts

Documentation (3) +43 / -0
README.mdDocument RBAC rule for saved-prompts permission +3/-0

Document RBAC rule for saved-prompts permission

• Adds a required policy line granting 'intelligent-assistant.saved-prompts.manage' to an example role. Helps adopters configure authorization for the new endpoints.

workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/README.md

report.api.mdPublish saved-prompts permission + type contracts in API report +37/-0

Publish saved-prompts permission + type contracts in API report

• Updates the public API report to include the new saved-prompts permission export. Adds public interfaces for config/list/create/delete saved prompt payloads.

workspaces/intelligent-assistant/plugins/intelligent-assistant-common/report.api.md

README.mdDocument saved-prompts RBAC requirement for the plugin +3/-0

Document saved-prompts RBAC requirement for the plugin

• Adds the saved-prompts manage permission to the example permission policy snippet. Keeps top-level plugin documentation aligned with the backend README.

workspaces/intelligent-assistant/plugins/intelligent-assistant/README.md

Other (1) +6 / -0
saved-prompts-proxy.mdAdd changeset for saved-prompts proxy + new permission +6/-0

Add changeset for saved-prompts proxy + new permission

• Introduces a minor-version changeset for both the intelligent-assistant backend and common packages. Documents the new saved-prompts proxy routes and 'intelligent-assistant.saved-prompts.manage' permission.

workspaces/intelligent-assistant/.changeset/saved-prompts-proxy.md

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>
@Jdubrick
Jdubrick force-pushed the add-saved-prompts branch from 84eb51e to 80cfb99 Compare July 29, 2026 15:04
@rhdh-qodo-merge

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Context used
⚠️ Tickets: not configured — ticket URL found in PR but could not be fetched — check ticket provider credentials
✅ Compliance rules (platform): 11 rules
✅ Cross-repo context
  Explored: repo: redhat-developer/rhdh-local (sha: 2ae9e8c8)
  Explored: repo: redhat-developer/rhdh (sha: ddfe8d07)
  Not relevant to this PR: redhat-developer/rhdh-chart
  Not relevant to this PR: redhat-developer/rhdh-operator

Grey Divider


Remediation recommended

1. Unsanitized proxy errors 🐞 Bug ⛨ Security
Description
The new saved-prompts GET/DELETE/config routes use apiProxy, which forwards Lightspeed Core error
bodies verbatim and bypasses the existing sanitizeLCSError/handleLCSFetchError protection. This can
expose LCS detail.cause (and similar internal/diagnostic details) to clients, unlike POST
/v1/saved-prompts which explicitly sanitizes errors.
Code

workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts[R639-656]

+  router.get(
+    '/v1/saved-prompts/config',
+    generalRateLimiter,
+    requirePermission(lightspeedSavedPromptsManagePermission),
+    apiProxy, // SKIP_USER_ID_ENDPOINTS prevents user_id injection for this endpoint
+  );
+  router.get(
+    '/v1/saved-prompts',
+    generalRateLimiter,
+    requirePermission(lightspeedSavedPromptsManagePermission),
+    apiProxy,
+  );
+  router.delete(
+    '/v1/saved-prompts/:prompt_id',
+    generalRateLimiter,
+    requirePermission(lightspeedSavedPromptsManagePermission),
+    apiProxy,
+  );
Relevance

●●● Strong

Team has precedent to sanitize/avoid leaking upstream/internal error details;
handleLCSFetchError/sanitizeLCSError pattern is used elsewhere.

PR-#3296
PR-#3521
PR-#3572

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The saved-prompts GET/DELETE/config endpoints are newly registered with apiProxy, while error
sanitization is only applied in explicit fetch-based handlers via handleLCSFetchError. The test
stubs show LCS can return responses containing detail.cause, which would be exposed if proxied
verbatim.

workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts[639-656]
workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts[701-734]
workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts[177-189]
workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/utils.ts[33-55]
workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.test.ts[812-888]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The newly added saved-prompts routes for GET/DELETE/config are wired through `apiProxy`, which does not apply `handleLCSFetchError` / `sanitizeLCSError`. As a result, when LCS returns an error response that contains `detail.cause`, that cause can be returned to the client.

### Issue Context
- `sanitizeLCSError` exists specifically to prevent information disclosure while still logging full upstream details server-side.
- POST `/v1/saved-prompts` already uses `handleLCSFetchError`, but GET/DELETE/config do not.

### Fix approach
Pick one of:
1) Replace these proxy routes with explicit `fetch(...)` handlers (like POST) that call `handleLCSFetchError` on non-OK responses.
2) Keep proxying, but configure a sanitizing proxy (e.g., `selfHandleResponse: true`) and intercept non-2xx responses to replace the body with `{ error: sanitizeLCSError(...) }` while preserving the upstream status code.

### Fix Focus Areas
- workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts[639-656]
- workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts[177-189]
- workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/utils.ts[43-81]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Duplicate user_id forwarded 🐞 Bug ≡ Correctness
Description
For the new saved-prompts GET/DELETE proxy routes, rewriteProxyPath appends user_id but does not
remove/override a client-supplied user_id query parameter, resulting in multiple user_id values
being forwarded upstream. This creates ambiguous user scoping (behavior depends on upstream
duplicate-query parsing) and can cause incorrect results or unexpected failures.
Code

workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts[R645-656]

+  router.get(
+    '/v1/saved-prompts',
+    generalRateLimiter,
+    requirePermission(lightspeedSavedPromptsManagePermission),
+    apiProxy,
+  );
+  router.delete(
+    '/v1/saved-prompts/:prompt_id',
+    generalRateLimiter,
+    requirePermission(lightspeedSavedPromptsManagePermission),
+    apiProxy,
+  );
Relevance

●●● Strong

Deterministic proxy hardening: prevent client-supplied user_id ambiguity/override when injecting
user_id in rewriteProxyPath.

PR-#2324

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The saved-prompts GET/DELETE routes are newly proxied and thus rely on rewriteProxyPath. That
function only appends user_id using string concatenation, so it cannot overwrite an existing
user_id from the incoming request URL, leading to duplicate parameters being forwarded.

workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts[645-656]
workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts[177-189]
workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/utils.ts[83-95]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`rewriteProxyPath` appends `user_id=...` but does not ensure it is the only `user_id` in the URL. If a client calls `/v1/saved-prompts?user_id=someone-else`, the proxy will forward both the client-provided and server-injected `user_id` values.

### Issue Context
- The saved-prompts GET/DELETE routes added in this PR use `apiProxy`, which uses `rewriteProxyPath`.
- `rewriteProxyPath` currently concatenates query strings rather than parsing them and deterministically setting `user_id`.

### Fix approach
Update `rewriteProxyPath` to parse the URL and do `searchParams.set('user_id', userEntityRef)` (which overwrites any existing value), then serialize back to a path+query string. While doing this, also keep the existing `history_length` behavior using `searchParams.set('history_length', ...)`.

### Fix Focus Areas
- workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts[177-189]
- workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/router.ts[645-656]
- workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/utils.ts[83-104]
- workspaces/intelligent-assistant/plugins/intelligent-assistant-backend/src/service/utils.test.ts[177-191]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

@rhdh-qodo-merge rhdh-qodo-merge Bot added documentation Improvements or additions to documentation enhancement New feature or request Tests labels Jul 29, 2026
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 57.91%. Comparing base (e03b281) to head (ef02aa3).
⚠️ Report is 15 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4076      +/-   ##
==========================================
+ Coverage   57.87%   57.91%   +0.04%     
==========================================
  Files        2393     2394       +1     
  Lines       95985    96098     +113     
  Branches    26803    26830      +27     
==========================================
+ Hits        55551    55657     +106     
- Misses      38940    38947       +7     
  Partials     1494     1494              
Flag Coverage Δ *Carryforward flag
adoption-insights 84.54% <ø> (ø) Carriedforward from 80cfb99
ai-integrations 69.06% <ø> (ø) Carriedforward from 80cfb99
app-defaults 69.79% <ø> (ø) Carriedforward from 80cfb99
augment 46.67% <ø> (ø) Carriedforward from 80cfb99
boost 76.77% <ø> (ø) Carriedforward from 80cfb99
bulk-import 72.55% <ø> (ø) Carriedforward from 80cfb99
cost-management 13.55% <ø> (ø) Carriedforward from 80cfb99
dcm 60.72% <ø> (ø) Carriedforward from 80cfb99
extensions 56.48% <ø> (ø) Carriedforward from 80cfb99
global-floating-action-button 71.18% <ø> (ø) Carriedforward from 80cfb99
global-header 62.17% <ø> (ø) Carriedforward from 80cfb99
homepage 47.46% <ø> (ø) Carriedforward from 80cfb99
install-dynamic-plugins 56.77% <ø> (ø) Carriedforward from 80cfb99
intelligent-assistant 74.61% <95.00%> (+0.37%) ⬆️
konflux 91.98% <ø> (ø) Carriedforward from 80cfb99
lightspeed 69.02% <ø> (ø) Carriedforward from 80cfb99
mcp-integrations 83.40% <ø> (ø) Carriedforward from 80cfb99
orchestrator 66.79% <ø> (ø) Carriedforward from 80cfb99
quickstart 65.04% <ø> (ø) Carriedforward from 80cfb99
sandbox 79.56% <ø> (ø) Carriedforward from 80cfb99
scorecard 84.94% <ø> (ø) Carriedforward from 80cfb99
theme 87.90% <ø> (ø) Carriedforward from 80cfb99
translations 5.12% <ø> (ø) Carriedforward from 80cfb99
x2a 79.31% <ø> (ø) Carriedforward from 80cfb99

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e03b281...ef02aa3. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Jdubrick

Copy link
Copy Markdown
Contributor Author

Qodo review calls out potential issues that are not introduced in this PR. The changes in this PR match the conventions used by the pre-existing routes.

@rhdh-qodo-merge

Copy link
Copy Markdown

Qodo review calls out potential issues that are not introduced in this PR. The changes in this PR match the conventions used by the pre-existing routes.

The findings are attached to code introduced by this PR, even though the underlying proxy behavior follows existing conventions:

  • finding 1 concerns the new saved-prompts POST catch block returning the raw exception message.
  • finding 2 concerns the new saved-prompts proxy routes potentially forwarding an existing user_id alongside the injected identity.

If these behaviors are intentional and consistent with the established route design, they can be treated as accepted risks. I have not dismissed them because that requires an explicit dismissal request.

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
5.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@Jdubrick

Copy link
Copy Markdown
Contributor Author

Quality Gate Failed Quality Gate failed

Failed conditions 5.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

The code duplication can be cleaned up / refactored as a separate PR, no need to touch other endpoints in this PR.

@HusneShabbir HusneShabbir left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

/lgtm

Tested locally

@openshift-ci openshift-ci Bot added the lgtm label Jul 30, 2026

@JslYoon JslYoon 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.

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

Summary: Adds /v1/saved-prompts CRUD endpoints (GET config, GET list, POST create, DELETE) to the intelligent-assistant backend, proxying to LCORE. Includes a new lightspeedSavedPromptsManagePermission, shared type definitions in the common package, MSW fixtures, and 9 router tests covering success paths and error relay (403, 409, 422, 400, 500). README updated with RBAC example. Changeset included.

The implementation closely follows the existing /v1/feedback and /v2/conversations patterns — apiProxy for GET/DELETE, custom handler for POST with user_id injection, handleLCSFetchError for upstream error relay. SKIP_USER_ID_ENDPOINTS correctly excludes only the config endpoint. Test coverage is thorough.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@JslYoon

JslYoon commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

/lgtm

@Jdubrick
Jdubrick merged commit 81b7ed2 into redhat-developer:main Jul 31, 2026
45 of 46 checks passed
JslYoon pushed a commit to JslYoon/rhdh-plugins that referenced this pull request Aug 3, 2026
…kend (redhat-developer#4076)

* add types for saved prompts

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* add saved prompts config to user id skip set

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* prepare msw for saved prompts

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* add saved prompts endpoints to router

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* add router tests for saved prompts

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* update readme to include new saved prompts rbac

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* add changeset

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* add test for catching 5xx on saved-prompts

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

---------

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>
JslYoon pushed a commit to JslYoon/rhdh-plugins that referenced this pull request Aug 3, 2026
…kend (redhat-developer#4076)

* add types for saved prompts

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* add saved prompts config to user id skip set

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* prepare msw for saved prompts

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* add saved prompts endpoints to router

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* add router tests for saved prompts

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* update readme to include new saved prompts rbac

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* add changeset

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

* add test for catching 5xx on saved-prompts

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>

---------

Signed-off-by: Jordan Dubrick <jdubrick@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request lgtm Tests workspace/intelligent-assistant

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants