Skip to content

Features/graph rule engine#432

Open
iceljc wants to merge 24 commits intoSciSharp:mainfrom
iceljc:features/graph-rule-engine
Open

Features/graph rule engine#432
iceljc wants to merge 24 commits intoSciSharp:mainfrom
iceljc:features/graph-rule-engine

Conversation

@iceljc
Copy link
Collaborator

@iceljc iceljc commented Mar 10, 2026

No description provided.

@qodo-code-review
Copy link

Review Summary by Qodo

Implement graph rule engine with collapsible UI components and MCP tools management

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add agent rule engine with trigger configuration and topology support
• Implement collapsible UI components for rules, utilities, and knowledge bases
• Refactor knowledge base and LLM config components into modular item components
• Add MCP tools management with server configuration and function selection
• Enhance CodeScript editor with line number hiding and editability control
Diagram
flowchart LR
  A["Agent Components"] --> B["Rules Engine"]
  A --> C["Utilities"]
  A --> D["Knowledge Bases"]
  A --> E["MCP Tools"]
  B --> F["Rule Items with Topology"]
  C --> G["Utility Items with Visibility"]
  D --> H["Knowledge Base Items"]
  E --> I["MCP Tool Items with Functions"]
  J["LLM Configs"] --> K["Collapsible Headers"]
  L["CodeScript Editor"] --> M["Line Numbers & Editability Control"]
Loading

Grey Divider

File Changes

1. src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte ✨ Enhancement +472/-0

New agent rule management component with triggers

src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte


2. src/routes/page/agent/[agentId]/agent-components/rules/agent-rule-item.svelte ✨ Enhancement +338/-0

Collapsible rule item with topology and config support

src/routes/page/agent/[agentId]/agent-components/rules/agent-rule-item.svelte


3. src/routes/page/agent/[agentId]/agent-components/utilities/agent-utility.svelte ✨ Enhancement +312/-0

Refactored utility management with item components

src/routes/page/agent/[agentId]/agent-components/utilities/agent-utility.svelte


View more (21)
4. src/routes/page/agent/[agentId]/agent-components/utilities/agent-utility-item.svelte ✨ Enhancement +373/-0

Collapsible utility item with visibility expressions

src/routes/page/agent/[agentId]/agent-components/utilities/agent-utility-item.svelte


5. src/routes/page/agent/[agentId]/agent-components/knowledge-bases/agent-knowledge-base.svelte ✨ Enhancement +48/-103

Refactored knowledge base with item component extraction

src/routes/page/agent/[agentId]/agent-components/knowledge-bases/agent-knowledge-base.svelte


6. src/routes/page/agent/[agentId]/agent-components/knowledge-bases/agent-knowledge-base-item.svelte ✨ Enhancement +161/-0

New collapsible knowledge base item component

src/routes/page/agent/[agentId]/agent-components/knowledge-bases/agent-knowledge-base-item.svelte


7. src/routes/page/agent/[agentId]/agent-components/mcp-tools/agent-mcp-tool.svelte ✨ Enhancement +248/-0

New MCP tools management with server configuration

src/routes/page/agent/[agentId]/agent-components/mcp-tools/agent-mcp-tool.svelte


8. src/routes/page/agent/[agentId]/agent-components/mcp-tools/agent-mcp-tool-item.svelte ✨ Enhancement +229/-0

Collapsible MCP tool item with function selection

src/routes/page/agent/[agentId]/agent-components/mcp-tools/agent-mcp-tool-item.svelte


9. src/routes/page/agent/[agentId]/agent-components/llm-configs/chat-config.svelte ✨ Enhancement +90/-72

Add collapsible header with slide transition

src/routes/page/agent/[agentId]/agent-components/llm-configs/chat-config.svelte


10. src/routes/page/agent/[agentId]/agent-components/llm-configs/llm-basic-config.svelte ✨ Enhancement +49/-30

Add collapsible header with slide transition

src/routes/page/agent/[agentId]/agent-components/llm-configs/llm-basic-config.svelte


11. src/routes/page/agent/[agentId]/agent-tabs.svelte ⚙️ Configuration changes +5/-5

Update import paths and default selected tab

src/routes/page/agent/[agentId]/agent-tabs.svelte


12. src/lib/common/shared/CodeScript.svelte ✨ Enhancement +24/-3

Add line number hiding and editability control

src/lib/common/shared/CodeScript.svelte


13. src/lib/common/modals/PlainModal.svelte ✨ Enhancement +5/-2

Add body styles prop and remove unused import

src/lib/common/modals/PlainModal.svelte


14. src/routes/chat/[agentId]/[conversationId]/chat-box.svelte ✨ Enhancement +1/-0

Set CodeScript editor to read-only mode

src/routes/chat/[agentId]/[conversationId]/chat-box.svelte


15. src/lib/scss/custom/pages/_agent.scss Formatting +39/-0

Add styles for collapsible headers and rule config modal

src/lib/scss/custom/pages/_agent.scss


16. src/lib/scss/custom/common/_common.scss Formatting +8/-0

Add collapse toggle rotation animation styles

src/lib/scss/custom/common/_common.scss


17. src/lib/helpers/types/agentTypes.js ✨ Enhancement +5/-2

Add expanded property to agent type definitions

src/lib/helpers/types/agentTypes.js


18. src/lib/helpers/http.js ⚙️ Configuration changes +2/-0

Add rule trigger and config URLs to skip loader

src/lib/helpers/http.js


19. src/lib/services/api-endpoints.js ⚙️ Configuration changes +1/-0

Add rule config options endpoint

src/lib/services/api-endpoints.js


20. src/lib/services/agent-service.js ✨ Enhancement +10/-0

Add getAgentRuleConfigOptions service function

src/lib/services/agent-service.js


21. package.json Dependencies +1/-0

Add CodeMirror JSON language support dependency

package.json


22. src/routes/page/agent/[agentId]/agent-components/agent-mcp-tool.svelte Additional files +0/-358

...

src/routes/page/agent/[agentId]/agent-components/agent-mcp-tool.svelte


23. src/routes/page/agent/[agentId]/agent-components/agent-rule.svelte Additional files +0/-439

...

src/routes/page/agent/[agentId]/agent-components/agent-rule.svelte


24. src/routes/page/agent/[agentId]/agent-components/agent-utility.svelte Additional files +0/-535

...

src/routes/page/agent/[agentId]/agent-components/agent-utility.svelte


Grey Divider

Qodo Logo

@qodo-code-review
Copy link

qodo-code-review bot commented Mar 10, 2026

Code Review by Qodo

🐞 Bugs (6) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Rule config undefined crash 🐞 Bug ✓ Correctness
Description
openRuleConfigModal dereferences config.customParameters without verifying that
ruleConfigs[topology_provider] exists, so clicking the config gear can throw and break the Rules UI
if the provider key is missing/mismatched.
Code

src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[R384-386]

+        const config = ruleConfigs[found.config.topology_provider];
+        const customParam = config.customParameters || {};
+
Evidence
The modal-open handler reads config.customParameters even though config can be undefined when
ruleConfigs has no entry for the selected topology_provider.

src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[378-386]
src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[123-136]

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

### Issue description
`openRuleConfigModal` assumes `ruleConfigs[topology_provider]` always exists and immediately dereferences `config.customParameters`. If the API doesn’t return a matching key, this throws and breaks the Rules UI.

### Issue Context
This is triggered by clicking the rule config gear icon; it should be safe even when the backend returns partial/mismatched config options.

### Fix Focus Areas
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[378-404]

Suggested direction:
- Add `if (!config) { return; }` (or show a user-facing error) before accessing `config.customParameters`.
- Consider also guarding `customParam.url` and `customParam.htmlTag` existence before constructing the URL.

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


2. Double-encoded iframe params 🐞 Bug ✓ Correctness
Description
openRuleConfigModal passes encodeURIComponent(params) into URLSearchParams.set, which causes
double-encoding and breaks parsing of the JSON parameters in the embedded config page.
Code

src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[R393-395]

+            const url = new URL(customParam.url, window.location.origin);
+            url.searchParams.set(customParam.appendParameterName || 'parameters', encodeURIComponent(params));
+
Evidence
URLSearchParams encodes values when serializing; pre-encoding the JSON string results in %25
sequences (double-encoded) in the final iframe URL.

src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[388-399]

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 code uses `encodeURIComponent(params)` before `url.searchParams.set(...)`, which leads to double-encoding of the JSON payload.

### Issue Context
The embedded iframe page expects to receive a query parameter containing JSON (or at least a once-encoded JSON string).

### Fix Focus Areas
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[388-395]

Suggested direction:
- Change to: `url.searchParams.set(name, params)`.
- Ensure the receiving page decodes exactly once.

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


3. Untrusted iframe URL embed 🐞 Bug ⛨ Security
Description
The code embeds a server-provided URL into an iframe without any origin/allowlist validation,
enabling arbitrary pages to be framed and receiving agentId/trigger data via query parameters.
Code

src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[R422-431]

+{#if isOpenConfigModal}
+    <PlainModal
+        containerClasses={'rule-config-modal'}
+        title={selectedRuleConfig?.rule?.trigger_name || ''}
+        size={'xl'}
+        isOpen={isOpenConfigModal}
+        toggleModal={() => isOpenConfigModal = !isOpenConfigModal}
+    >
+        <iframe src={selectedRuleConfig.url} title={selectedRuleConfig.title} width="100%" height="100%" />
+    </PlainModal>
Evidence
The iframe src comes from customParam.url in ruleConfigs (API-driven) and is rendered directly;
the URL construction includes agent identifiers in query parameters.

src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[387-403]
src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[422-431]

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

### Issue description
A server-driven `customParam.url` is framed directly. Without validating `url.origin` (or restricting to known safe paths), this can embed arbitrary origins and leak agent metadata via query parameters.

### Issue Context
Even if only admins can open the modal, the embedded content can still perform unwanted actions (tracking, phishing UI, data exfil) within the iframe context.

### Fix Focus Areas
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[387-404]
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[422-431]

Suggested direction:
- Reject URLs whose `origin` is not `window.location.origin` (or not in an allowlist).
- Add `sandbox` attributes to the iframe (tightest possible), e.g. start with `sandbox` and selectively allow what’s needed.
- Consider not passing `agentId`/`trigger` to third-party origins.

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


View more (1)
4. Rule config shape mismatch 🐞 Bug ✓ Correctness
Description
getAgentRuleConfigOptions is documented as returning an array, but the rules UI treats it as an
object keyed by provider name; if the API actually returns an array, configOptions/ruleConfigs will
be built incorrectly and rule config modal cannot resolve provider configs.
Code

src/lib/services/agent-service.js[R118-126]

+/**
+ * Get agent rule criteria providers
+ * @returns {Promise<any[]>}
+ */
+export async function getAgentRuleConfigOptions() {
+    const url = endpoints.agentRuleConfigOptionsUrl;
+    const response = await axios.get(url);
+    return response.data;
+}
Evidence
The service JSDoc indicates an array response, while the UI assigns ruleConfigs = data and uses
ruleConfigs[providerKey] indexing, which will not work with an array unless providerKey is a
numeric index.

src/lib/services/agent-service.js[118-126]
src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[123-135]
src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[384-386]

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 UI logic requires `ruleConfigs` to be a map keyed by provider name, but the service is documented to return an array. If the backend response is an array, `ruleConfigs[providerName]` will be `undefined` and the config modal flow breaks.

### Issue Context
This is a contract mismatch between `getAgentRuleConfigOptions()` and `agent-rule.svelte`.

### Fix Focus Areas
- src/lib/services/agent-service.js[118-126]
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[123-136]
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[378-386]

Suggested direction:
- If backend returns a map: update JSDoc to `Promise&lt;Record&lt;string, any&gt;&gt;` (or a typed interface).
- If backend returns an array: convert it in `loadAgentRuleConfigOptions()` into `{ [name]: config }` and build `configOptions` from those names.

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



Remediation recommended

5. Rule options load no catch 🐞 Bug ⛯ Reliability
Description
loadAgentRuleOptions/loadAgentRuleConfigOptions never catch or reject on axios failures, so network
errors produce unhandled rejections and leave ruleOptions/configOptions uninitialized with no
fallback behavior.
Code

src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[R83-136]

+    onMount(async () =>{
+        resizeWindow();
+        Promise.all([
+            loadAgentRuleOptions(),
+            loadAgentRuleConfigOptions()
+        ]);
+    });
+
+    function loadAgentRuleOptions() {
+        return new Promise((resolve) => {
+            getAgentRuleOptions().then(data => {
+                const list = data?.map(x => {
+                    return {
+                        name: x.trigger_name,
+                        displayName: "",
+                        output_args: x.output_args,
+                        json_args: x.json_args,
+                        statement: x.statement
+                    };
+                }) || [];
+                ruleOptions = [{
+                    name: "",
+                    displayName: ""
+                }, ...list];
+                init();
+                resolve('done');
+            });
+        });
+    }
+
+    function init() {
+        const list = agent.rules?.map(x => {
+            return {
+                ...x,
+                displayName: ""
+            };
+        }) || [];
+        innerRefresh(list);
+    }
+
+    function loadAgentRuleConfigOptions() {
+        return new Promise((resolve) => {
+            getAgentRuleConfigOptions().then(data => {
+                ruleConfigs = data || {};
+                const keys = Object.keys(data || {});
+                const list = keys?.map(x => ({ name: x })) || [];
+                configOptions = [
+                    { name: '' },
+                    ...list
+                ];
+                resolve('done');
+            });
+        });
+    }
Evidence
Both loaders wrap axios calls in a Promise but only resolve on success; errors are not handled, and
the Promise never settles on failure.

src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[91-111]
src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[123-136]

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 rule option loaders do not handle rejected requests, which can lead to unhandled promise rejections and a broken/empty rules UI.

### Issue Context
This component depends on two remote calls; failures should not leave the UI in an undefined state.

### Fix Focus Areas
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[83-136]

Suggested direction:
- Replace the `new Promise` wrappers with `async function load...(){ try { ... } catch(e) { ruleOptions=[]; configOptions=[]; /* optional toast */ } }`.
- Consider awaiting the loads in `onMount` to control initialization order and error handling.

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


6. Confidence edit keys blocked 🐞 Bug ✓ Correctness
Description
validateConfidenceInput prevents all keys except digits/dot and Backspace, blocking Delete, arrows,
Tab, Enter, and paste shortcuts, which makes the confidence field difficult to edit and breaks
keyboard interaction.
Code

src/routes/page/agent/[agentId]/agent-components/knowledge-bases/agent-knowledge-base-item.svelte[R23-29]

+    /** @param {any} e */
+    function validateConfidenceInput(e) {
+        const reg = new RegExp(DECIMAL_REGEX, 'g');
+        if (e.key !== 'Backspace' && !reg.test(e.key)) {
+            e.preventDefault();
+        }
+    }
Evidence
The keydown handler calls preventDefault for most non-character/navigation keys; DECIMAL_REGEX only
matches digits/dot so navigation/clipboard keys are blocked.

src/routes/page/agent/[agentId]/agent-components/knowledge-bases/agent-knowledge-base-item.svelte[23-29]
src/lib/helpers/constants.js[49-51]

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 confidence input keydown filter blocks essential editing/navigation keys and paste shortcuts.

### Issue Context
This causes poor UX and may prevent some users from editing the field using keyboard-only workflows.

### Fix Focus Areas
- src/routes/page/agent/[agentId]/agent-components/knowledge-bases/agent-knowledge-base-item.svelte[23-29]

Suggested direction:
- Whitelist keys like: Backspace, Delete, ArrowLeft/Right/Up/Down, Tab, Enter, Home, End.
- Allow Ctrl/Meta shortcuts (e.ctrlKey/e.metaKey).
- Alternatively remove keydown blocking and sanitize/validate on blur (`changeConfidence`) using the existing `validateConfidenceNumber` logic in the parent.

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


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +384 to +386
const config = ruleConfigs[found.config.topology_provider];
const customParam = config.customParameters || {};

Choose a reason for hiding this comment

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

Action required

1. Rule config undefined crash 🐞 Bug ✓ Correctness

openRuleConfigModal dereferences config.customParameters without verifying that
ruleConfigs[topology_provider] exists, so clicking the config gear can throw and break the Rules UI
if the provider key is missing/mismatched.
Agent Prompt
### Issue description
`openRuleConfigModal` assumes `ruleConfigs[topology_provider]` always exists and immediately dereferences `config.customParameters`. If the API doesn’t return a matching key, this throws and breaks the Rules UI.

### Issue Context
This is triggered by clicking the rule config gear icon; it should be safe even when the backend returns partial/mismatched config options.

### Fix Focus Areas
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[378-404]

Suggested direction:
- Add `if (!config) { return; }` (or show a user-facing error) before accessing `config.customParameters`.
- Consider also guarding `customParam.url` and `customParam.htmlTag` existence before constructing the URL.

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

Comment on lines +393 to +395
const url = new URL(customParam.url, window.location.origin);
url.searchParams.set(customParam.appendParameterName || 'parameters', encodeURIComponent(params));

Choose a reason for hiding this comment

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

Action required

2. Double-encoded iframe params 🐞 Bug ✓ Correctness

openRuleConfigModal passes encodeURIComponent(params) into URLSearchParams.set, which causes
double-encoding and breaks parsing of the JSON parameters in the embedded config page.
Agent Prompt
### Issue description
The code uses `encodeURIComponent(params)` before `url.searchParams.set(...)`, which leads to double-encoding of the JSON payload.

### Issue Context
The embedded iframe page expects to receive a query parameter containing JSON (or at least a once-encoded JSON string).

### Fix Focus Areas
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[388-395]

Suggested direction:
- Change to: `url.searchParams.set(name, params)`.
- Ensure the receiving page decodes exactly once.

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

Comment on lines +422 to +431
{#if isOpenConfigModal}
<PlainModal
containerClasses={'rule-config-modal'}
title={selectedRuleConfig?.rule?.trigger_name || ''}
size={'xl'}
isOpen={isOpenConfigModal}
toggleModal={() => isOpenConfigModal = !isOpenConfigModal}
>
<iframe src={selectedRuleConfig.url} title={selectedRuleConfig.title} width="100%" height="100%" />
</PlainModal>

Choose a reason for hiding this comment

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

Action required

3. Untrusted iframe url embed 🐞 Bug ⛨ Security

The code embeds a server-provided URL into an iframe without any origin/allowlist validation,
enabling arbitrary pages to be framed and receiving agentId/trigger data via query parameters.
Agent Prompt
### Issue description
A server-driven `customParam.url` is framed directly. Without validating `url.origin` (or restricting to known safe paths), this can embed arbitrary origins and leak agent metadata via query parameters.

### Issue Context
Even if only admins can open the modal, the embedded content can still perform unwanted actions (tracking, phishing UI, data exfil) within the iframe context.

### Fix Focus Areas
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[387-404]
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[422-431]

Suggested direction:
- Reject URLs whose `origin` is not `window.location.origin` (or not in an allowlist).
- Add `sandbox` attributes to the iframe (tightest possible), e.g. start with `sandbox` and selectively allow what’s needed.
- Consider not passing `agentId`/`trigger` to third-party origins.

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

Comment on lines +118 to +126
/**
* Get agent rule criteria providers
* @returns {Promise<any[]>}
*/
export async function getAgentRuleConfigOptions() {
const url = endpoints.agentRuleConfigOptionsUrl;
const response = await axios.get(url);
return response.data;
}

Choose a reason for hiding this comment

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

Action required

4. Rule config shape mismatch 🐞 Bug ✓ Correctness

getAgentRuleConfigOptions is documented as returning an array, but the rules UI treats it as an
object keyed by provider name; if the API actually returns an array, configOptions/ruleConfigs will
be built incorrectly and rule config modal cannot resolve provider configs.
Agent Prompt
### Issue description
The UI logic requires `ruleConfigs` to be a map keyed by provider name, but the service is documented to return an array. If the backend response is an array, `ruleConfigs[providerName]` will be `undefined` and the config modal flow breaks.

### Issue Context
This is a contract mismatch between `getAgentRuleConfigOptions()` and `agent-rule.svelte`.

### Fix Focus Areas
- src/lib/services/agent-service.js[118-126]
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[123-136]
- src/routes/page/agent/[agentId]/agent-components/rules/agent-rule.svelte[378-386]

Suggested direction:
- If backend returns a map: update JSDoc to `Promise<Record<string, any>>` (or a typed interface).
- If backend returns an array: convert it in `loadAgentRuleConfigOptions()` into `{ [name]: config }` and build `configOptions` from those names.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant