Skip to content

feat(api-service): per-agent bridge URL with dev/production toggle fixes NV-7373#10740

Merged
ChmaraX merged 16 commits intonextfrom
nv-7373-per-agent-bridge-url-with-devproduction-toggle
Apr 16, 2026
Merged

feat(api-service): per-agent bridge URL with dev/production toggle fixes NV-7373#10740
ChmaraX merged 16 commits intonextfrom
nv-7373-per-agent-bridge-url-with-devproduction-toggle

Conversation

@ChmaraX
Copy link
Copy Markdown
Contributor

@ChmaraX ChmaraX commented Apr 15, 2026

Summary

Adds per-agent bridge URLs with a dev/production toggle, replacing the single environment-level bridge URL. Each agent now stores its own bridgeUrl (production), devBridgeUrl (dev tunnel), and devBridgeActive (boolean toggle), enabling independent local development without affecting other agents or production traffic.

Changes by layer

  • Data model (libs/dal): Added bridgeUrl, devBridgeUrl, devBridgeActive fields to AgentEntity and schema
  • API (apps/api): Extended update DTO/command/usecase, added PUT /v1/agents/:identifier/bridge endpoint, production environment guard (403 if devBridgeActive or devBridgeUrl on prod)
  • Framework (packages/framework): Client.discover() now includes registered agents; ctx.reply() handles JSX Card elements via isJSX()/toCardElement() conversion
  • CLI (packages/novu):
    • npx novu dev auto-discovers agents and activates dev bridges; --no-studio flag to skip Studio server
    • npx novu sync sets per-agent production bridgeUrl
    • npx novu init --template agent --agent-identifier <id> injects the correct agent identifier into the scaffolded template
    • .env.local is now loaded alongside .env so NOVU_SECRET_KEY set by novu init is picked up by novu dev
  • Dashboard (apps/dashboard): Bridge URL config section in agent sidebar with dev override toggle, DEV badge on agent detail header (removed from agent list)
  • Tests: Framework discover tests + API E2E tests for bridge management and production guard

Bridge resolution order

flowchart TD
    A[Inbound message] --> B{devBridgeActive && devBridgeUrl?}
    B -- Yes --> C[Use devBridgeUrl]
    B -- No --> D{bridgeUrl set?}
    D -- Yes --> E[Use bridgeUrl]
    D -- No --> F[No bridge — skip]
Loading

Safety

  • API-level guard prevents devBridgeActive: true or devBridgeUrl from being set on production environments (returns 403)
  • No environment-level fallback — bridge URL must be configured per-agent

Test plan

  • Framework unit tests pass (Client.discover() includes agents)
  • API E2E tests for bridge URL CRUD via PATCH and PUT endpoints
  • API E2E tests for production environment guard (403 on prod, 200 on dev)
  • Manual: npx novu dev discovers agents and sets dev bridge
  • Manual: npx novu sync sets production bridge URL per agent
  • Manual: Dashboard shows DEV badge and bridge config in sidebar
  • Manual: npx novu init --template agent --agent-identifier <id> scaffolds app with correct identifier
  • Manual: JSX Card elements render correctly via ctx.reply()

Made with Cursor

What changed

Agents now support per-agent bridge configuration: each agent stores a production bridgeUrl, an optional devBridgeUrl, and a devBridgeActive flag. At runtime the system uses the devBridgeUrl when devBridgeActive is true, otherwise falls back to the agent’s production bridgeUrl; there is no environment-level fallback. The API prohibits enabling dev bridges or setting devBridgeUrl in production to protect live traffic. This enables independent local agent development without impacting other agents or production.

Affected areas

dal: Added bridgeUrl, devBridgeUrl, and devBridgeActive to AgentEntity and the Mongo schema.
api-service: DTOs, command, and usecase accept bridge fields; added PUT /v1/agents/:identifier/bridge; UpdateAgent enforces a PROD guard (returns 403) for dev overrides; BridgeExecutorService resolves bridge URL from per-agent config instead of environment lookup.
framework: Client.discover() now returns registered agents; added a jsx-dev-runtime entry; ctx.reply() converts JSX Card elements via isJSX()/toCardElement() so JSX replies serialize to Card payloads.
dashboard: Agent sidebar includes a Bridge URL section with truncated tooltip and a Dev override switch; agent detail header shows a DEV badge when devBridgeActive is true.
cli (novu): npx novu dev discovers agents and activates per-agent dev bridges (supports --no-studio); npx novu sync writes per-agent production bridgeUrl via the API; npx novu init accepts --agent-identifier and .env.local is loaded before .env so NOVU_API_URL/NOVU_SECRET_KEY from init are honored.

Key technical decisions

  • Agent-scoped bridge resolution with no environment-level fallback to avoid global overrides.
  • Production safety enforced in UpdateAgent: setting devBridgeActive=true or providing devBridgeUrl in PROD is forbidden.
  • CLI and sync use the bridge discovery endpoint and ApiKey-authenticated PUTs to set per-agent bridge URLs.
  • New small exports/entrypoints in framework to support JSX-in-dev semantics; no new runtime dependencies added.

Testing

Added framework unit tests for discover() and JSX reply serialization; API E2E tests for bridge CRUD and the production guard are included (some checks pending); the PR documents manual verification steps.

ChmaraX added 7 commits April 15, 2026 20:03
…n NV-7373

Add bridgeUrl, devBridgeUrl, and devBridgeActive fields to AgentEntity
and schema. Thread them through ResolvedAgentConfig and replace the
environment-level bridge lookup in BridgeExecutorService with agent-level
two-tier resolution (dev bridge -> production bridge -> null).

Made-with: Cursor
…uard NV-7373

Extend agent CRUD DTOs, command, and usecase with bridgeUrl, devBridgeUrl,
and devBridgeActive fields. Add dedicated PUT /v1/agents/:identifier/bridge
endpoint (ExternalApiAccessible for CLI) that reuses the update usecase.
The usecase refuses to activate dev bridges on production environments (403).

Made-with: Cursor
@linear
Copy link
Copy Markdown

linear Bot commented Apr 15, 2026

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 15, 2026

Deploy preview added

Name Link
🔨 Latest commit ad91c11
🔍 Latest deploy log https://app.netlify.com/projects/dashboard-v2-novu-staging/deploys/69e0a3221a090300084f5679
😎 Deploy Preview https://deploy-preview-10740.dashboard-v2.novu-staging.co
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Adds per-agent bridge configuration (production and dev URLs + dev override), new PUT bridge endpoint, persistence and resolver changes, executor URL selection, dashboard UI/toggle, CLI dev/init/sync discovery & activation flows, framework JSX/runtime additions, and tests; blocks dev-override changes in production environments.

Changes

Cohort / File(s) Summary
Git Submodule
\.source
Updated Git submodule pointer commit.
API: Controller, DTOs, Mapper, Usecase
apps/api/src/app/agents/agents.controller.ts, apps/api/src/app/agents/dtos/update-agent-bridge-request.dto.ts, apps/api/src/app/agents/dtos/update-agent-request.dto.ts, apps/api/src/app/agents/dtos/agent-response.dto.ts, apps/api/src/app/agents/dtos/index.ts, apps/api/src/app/agents/mappers/agent-response.mapper.ts, apps/api/src/app/agents/usecases/update-agent/update-agent.command.ts, apps/api/src/app/agents/usecases/update-agent/update-agent.usecase.ts
Added PUT /agents/:identifier/bridge endpoint; extended PATCH /agents/:identifier to accept bridgeUrl, devBridgeUrl, devBridgeActive; added DTO/command fields; usecase validates input and forbids dev overrides in PROD; persists bridge fields.
API: Services & Executor
apps/api/src/app/agents/services/agent-config-resolver.service.ts, apps/api/src/app/agents/services/bridge-executor.service.ts
ResolvedAgentConfig includes bridge fields; BridgeExecutorService removed environment DB lookup and resolves bridge URL synchronously from config, preferring dev override when active.
DAL: Entity & Schema
libs/dal/src/repositories/agent/agent.entity.ts, libs/dal/src/repositories/agent/agent.schema.ts
Added optional bridgeUrl, devBridgeUrl, devBridgeActive to AgentEntity and Mongo schema (devBridgeActive default false).
Dashboard: API types & UI
apps/dashboard/src/api/agents.ts, apps/dashboard/src/components/agents/agent-details-header.tsx, apps/dashboard/src/components/agents/agent-sidebar-widget.tsx
Added bridge fields to client types; UI shows DEV badge when override active, displays/truncates bridge URL, and exposes dev-override switch wired to agent update.
E2E / Tests
apps/api/src/app/agents/e2e/agents.e2e.ts, apps/api/src/app/agents/e2e/mock-agent-handler.ts
Added E2E suites covering bridge management and production guard; removed onReaction from mock agent handler.
Framework: runtime, client discovery, tests, types, build
packages/framework/src/jsx-dev-runtime.ts, packages/framework/jsx-dev-runtime/package.json, packages/framework/package.json, packages/framework/tsup.config.ts, packages/framework/src/client.ts, packages/framework/src/types/discover.types.ts, packages/framework/src/resources/agent/agent.context.ts, packages/framework/src/resources/agent/agent.test.ts
Added jsx-dev-runtime entry and package exports; Client.discover() now returns registered agents; added DiscoverAgentOutput type; agent context serializes JSX Cards; added tests.
CLI: dev / init / sync
packages/novu/src/commands/dev/dev.ts, packages/novu/src/commands/dev/types.ts, packages/novu/src/commands/init/create-app.ts, packages/novu/src/commands/init/index.ts, packages/novu/src/commands/init/templates/index.ts, packages/novu/src/commands/init/templates/types.ts, packages/novu/src/commands/sync.ts, packages/novu/src/index.ts
Dev: added --no-studio, discovers agents from dev bridge and activates devBridgeUrl via API (uses NOVU_SECRET_KEY); Init: added --agent-identifier and threads apiUrl/agentIdentifier into templates; Sync: discovers agents from bridge and PUTs bridgeUrl to agents.
Constants / Env
packages/novu/src/constants/constants.ts
Load .env.local explicitly before default dotenv to affect runtime precedence.
Build / Package manifests
packages/framework/jsx-dev-runtime/package.json, packages/framework/package.json
Added package.json and package exports for new jsx-dev-runtime module.

Sequence Diagram(s)

sequenceDiagram
  participant DevCLI as Dev CLI
  participant Tunnel as Local Tunnel
  participant Bridge as Dev Bridge Endpoint
  participant API as Novu API
  participant DB as MongoDB

  DevCLI->>Tunnel: start tunnel, compute bridgeUrl
  DevCLI->>Bridge: GET {bridgeUrl}?action=discover
  Bridge-->>DevCLI: { agents: [{agentId}] }
  DevCLI->>API: PUT /v1/agents/{agentId}/bridge { devBridgeUrl, devBridgeActive: true } (Authorization: ApiKey)
  API->>DB: Update agent.bridgeUrl / devBridgeUrl / devBridgeActive
  DB-->>API: ack
  API-->>DevCLI: 200 OK or 403 Forbidden
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • scopsy
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits format with type 'feat' and scope 'api-service', uses lowercase imperative description, and ends with Linear ticket 'fixes NV-7373'.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 16, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@novu/framework@10740
npm i https://pkg.pr.new/novu@10740

commit: ad91c11

ChmaraX added 3 commits April 16, 2026 11:03
…o, and cleanup NV-7373

- Add --agent-identifier flag to `novu init` so the dashboard onboarding
  can pass the exact agent identifier into the scaffolded template
- Add --template flag plumbing through init → createApp → installTemplate
- Add APP_AGENT to TemplateTypeEnum
- Fix JSX Card elements not being serialized by ctx.reply() — use
  chat's isJSX()/toCardElement() before the wire-format check
- Add --no-studio flag to `novu dev` to skip starting Studio server
- Remove DEV badge from agent list table (keep on detail page only)
- Clean up mock-agent-handler e2e fixture

Made-with: Cursor
…-7373

- Load .env.local before .env so NOVU_SECRET_KEY set by `novu init` is
  picked up automatically by `novu dev`
- Fix Commander --no-studio flag: use `studio: boolean` (Commander's
  negation convention) instead of `noStudio`

Made-with: Cursor
Reconcile templateChoice (from next) with agentIdentifier (from this branch).
Remove redundant template: templateFlag param since templateChoice already
carries the template selection.

Made-with: Cursor
@ChmaraX ChmaraX marked this pull request as ready for review April 16, 2026 08:12
Verifies that ctx.reply() correctly serializes JSX-produced Card elements
(via isJSX/toCardElement) — distinct from the existing function-call Card test.

Made-with: Cursor
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (4)
apps/dashboard/src/components/agents/agent-sidebar-widget.tsx (2)

70-102: Add blank line before return statement in BridgeUrlSection.

As per coding guidelines, include a blank line before every return statement.

🔧 Proposed fix
 function BridgeUrlSection({ agent, canWrite, isUpdatePending, onUpdate }: BridgeUrlSectionProps) {
   const activeBridgeUrl = agent.devBridgeActive && agent.devBridgeUrl ? agent.devBridgeUrl : agent.bridgeUrl;
 
+
   return (
     <>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/agents/agent-sidebar-widget.tsx` around lines
70 - 102, The function BridgeUrlSection currently has no blank line immediately
before its return; update the BridgeUrlSection function so there is a single
blank line separating the function-level declarations (e.g., the activeBridgeUrl
const) from the return statement to comply with the coding guideline; modify the
body of BridgeUrlSection (the area after const activeBridgeUrl = ...) to insert
a blank line before the return (...) while leaving the return JSX and existing
logic (Badges, TruncatedUrl, SidebarRow, Switch, onUpdate) unchanged.

48-61: Add blank line before return statement.

As per coding guidelines, include a blank line before every return statement.

🔧 Proposed fix
 function TruncatedUrl({ url }: { url: string }) {
+
   return (
     <Tooltip>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/agents/agent-sidebar-widget.tsx` around lines
48 - 61, Add a blank line before the return statement in the TruncatedUrl
component to follow the coding guideline: locate the function TruncatedUrl and
insert a single empty line immediately above the line starting with "return ("
so the return is separated from the preceding function signature/body.
packages/novu/src/commands/init/templates/index.ts (1)

74-80: Consider sanitizing agentIdentifier before string interpolation.

If agentIdentifier contains characters like ', \, or $, the generated code could have syntax errors or unexpected behavior. Consider validating the identifier matches expected slug format before interpolation.

🛡️ Proposed validation
 if (template === TemplateTypeEnum.APP_AGENT && agentIdentifier) {
+  if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(agentIdentifier)) {
+    throw new Error(`Invalid agent identifier: ${agentIdentifier}. Must be a valid slug.`);
+  }
   const agentFile = path.join(root, 'app', 'novu', 'agents', 'support-agent.tsx');
   await fs.writeFile(
     agentFile,
     (await fs.readFile(agentFile, 'utf8')).replace("agent('support-agent',", `agent('${agentIdentifier}',`)
   );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/novu/src/commands/init/templates/index.ts` around lines 74 - 80, The
code interpolates agentIdentifier directly into the agent(...) call (see
TemplateTypeEnum.APP_AGENT branch and variable agentFile pointing to
support-agent.tsx), which can break generated code if agentIdentifier contains
quotes or other illegal chars; validate and sanitize agentIdentifier first
(e.g., enforce a slug/regex allowed chars like [a-z0-9-_] or escape problematic
characters) and only write the file after the identifier passes validation or
has been safely escaped to ensure the generated agent('...') string is
syntactically valid.
packages/novu/src/commands/dev/dev.ts (1)

186-205: Add blank line before return statements.

As per coding guidelines, include a blank line before every return statement.

🔧 Proposed fix
 async function discoverAgents(endpointUrl: string): Promise<string[]> {
   try {
     const res = await fetch(`${endpointUrl}?action=discover`, {
       method: 'GET',
       headers: {
         accept: 'application/json',
         'Content-Type': 'application/json',
         'User-Agent': `novu@${version}`,
       },
     });
 
-    if (!res.ok) return [];
+    if (!res.ok) {
+      return [];
+    }
 
     const data = (await res.json()) as DiscoverResponse;
 
     return (data.agents ?? []).map((a) => a.agentId);
   } catch {
+
     return [];
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/novu/src/commands/dev/dev.ts` around lines 186 - 205, In the
discoverAgents function ensure there is a blank line immediately before each
return statement; update the GET response error branch (before "return []"), the
successful mapping return (before "return (data.agents ?? []).map(...)"), and
the catch block return (before "return []") so each return is preceded by one
empty line to satisfy the style guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/app/agents/usecases/update-agent/update-agent.usecase.ts`:
- Around line 30-32: The current guard calls assertNotProductionEnvironment
whenever command.devBridgeUrl is present in the request, which blocks callers
from clearing a stale prod URL; change the condition in update-agent.usecase.ts
so you only assert for unsafe transitions: when command.devBridgeActive === true
OR when command.devBridgeUrl is being set to a non-null value (i.e. devBridgeUrl
!== undefined && devBridgeUrl !== null). Update the identical check around lines
113-122 as well (same use of command.devBridgeActive / command.devBridgeUrl and
the assertNotProductionEnvironment call) so clearing with null is allowed but
enabling or setting a non-null URL still triggers the production guard.
- Around line 16-24: The current hasGeneralFields treats any defined
command.behavior (even {}) as an update, but the updateOne payload only persists
specific nested flags so empty behavior objects pass validation but cause no
changes; create a helper (e.g., hasValidBehavior) and update the
hasGeneralFields check to use it: consider behavior as an update only if
command.behavior is a non-null object with at least one of the concrete nested
properties you later persist (e.g., reactions or other persisted keys) and for
nested objects ensure they contain non-empty own properties
(Object.keys(...).length > 0) so behavior: {} or behavior: { reactions: {} } is
rejected; also ensure the code building the update payload (the updateOne call)
ignores undefined/no-op behavior fields so you don't send empty updates.

In `@packages/novu/src/commands/dev/dev.ts`:
- Around line 207-230: The API path in activateAgentBridge currently
interpolates agentId directly into the URL which can break when agentId contains
characters like '/' or '%'; update the fetch call URL to use
encodeURIComponent(agentId) (e.g., replace
`${apiUrl}/v1/agents/${agentId}/bridge` with
`${apiUrl}/v1/agents/${encodeURIComponent(agentId)}/bridge`) so special
characters are safely encoded while keeping all other logic (headers/body/status
handling) unchanged.

In `@packages/novu/src/commands/sync.ts`:
- Around line 64-77: The API path concatenates agent.agentId directly into the
URL in the agents.map callback inside sync.ts, which can break if agent IDs
contain special characters; update the axios PUT call that builds
`${apiUrl}/v1/agents/${agent.agentId}/bridge` to URL-encode the agent identifier
(use encodeURIComponent or equivalent) when constructing the path so the request
URL is safe and robust (adjust any other occurrences in the same module if
present).
- Around line 52-56: The discovery URL in syncAgentBridgeUrls is built
incorrectly by appending "/novu" to bridgeUrl; change the URL construction to
use `${bridgeUrl}?action=discover` (i.e., remove the redundant "/novu") so that
when bridgeUrl already contains the /api/novu route the discovery call is
correct; update the discoverUrl variable and ensure axios.get still uses the
same timeout and typing (DiscoverResponse) and that subsequent use of
agents/discoverRes remains unchanged.

---

Nitpick comments:
In `@apps/dashboard/src/components/agents/agent-sidebar-widget.tsx`:
- Around line 70-102: The function BridgeUrlSection currently has no blank line
immediately before its return; update the BridgeUrlSection function so there is
a single blank line separating the function-level declarations (e.g., the
activeBridgeUrl const) from the return statement to comply with the coding
guideline; modify the body of BridgeUrlSection (the area after const
activeBridgeUrl = ...) to insert a blank line before the return (...) while
leaving the return JSX and existing logic (Badges, TruncatedUrl, SidebarRow,
Switch, onUpdate) unchanged.
- Around line 48-61: Add a blank line before the return statement in the
TruncatedUrl component to follow the coding guideline: locate the function
TruncatedUrl and insert a single empty line immediately above the line starting
with "return (" so the return is separated from the preceding function
signature/body.

In `@packages/novu/src/commands/dev/dev.ts`:
- Around line 186-205: In the discoverAgents function ensure there is a blank
line immediately before each return statement; update the GET response error
branch (before "return []"), the successful mapping return (before "return
(data.agents ?? []).map(...)"), and the catch block return (before "return []")
so each return is preceded by one empty line to satisfy the style guideline.

In `@packages/novu/src/commands/init/templates/index.ts`:
- Around line 74-80: The code interpolates agentIdentifier directly into the
agent(...) call (see TemplateTypeEnum.APP_AGENT branch and variable agentFile
pointing to support-agent.tsx), which can break generated code if
agentIdentifier contains quotes or other illegal chars; validate and sanitize
agentIdentifier first (e.g., enforce a slug/regex allowed chars like [a-z0-9-_]
or escape problematic characters) and only write the file after the identifier
passes validation or has been safely escaped to ensure the generated
agent('...') string is syntactically valid.
🪄 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: Pro

Run ID: 923ca0c6-1d7b-40df-b252-e27defd00e5e

📥 Commits

Reviewing files that changed from the base of the PR and between 8fa54f5 and 2c0756e.

📒 Files selected for processing (35)
  • .source
  • apps/api/src/app/agents/agents.controller.ts
  • apps/api/src/app/agents/dtos/agent-response.dto.ts
  • apps/api/src/app/agents/dtos/index.ts
  • apps/api/src/app/agents/dtos/update-agent-bridge-request.dto.ts
  • apps/api/src/app/agents/dtos/update-agent-request.dto.ts
  • apps/api/src/app/agents/e2e/agents.e2e.ts
  • apps/api/src/app/agents/e2e/mock-agent-handler.ts
  • apps/api/src/app/agents/mappers/agent-response.mapper.ts
  • apps/api/src/app/agents/services/agent-config-resolver.service.ts
  • apps/api/src/app/agents/services/bridge-executor.service.ts
  • apps/api/src/app/agents/usecases/update-agent/update-agent.command.ts
  • apps/api/src/app/agents/usecases/update-agent/update-agent.usecase.ts
  • apps/dashboard/src/api/agents.ts
  • apps/dashboard/src/components/agents/agent-details-header.tsx
  • apps/dashboard/src/components/agents/agent-sidebar-widget.tsx
  • libs/dal/src/repositories/agent/agent.entity.ts
  • libs/dal/src/repositories/agent/agent.schema.ts
  • packages/framework/jsx-dev-runtime/package.json
  • packages/framework/package.json
  • packages/framework/src/client.ts
  • packages/framework/src/jsx-dev-runtime.ts
  • packages/framework/src/resources/agent/agent.context.ts
  • packages/framework/src/resources/agent/agent.test.ts
  • packages/framework/src/types/discover.types.ts
  • packages/framework/tsup.config.ts
  • packages/novu/src/commands/dev/dev.ts
  • packages/novu/src/commands/dev/types.ts
  • packages/novu/src/commands/init/create-app.ts
  • packages/novu/src/commands/init/index.ts
  • packages/novu/src/commands/init/templates/index.ts
  • packages/novu/src/commands/init/templates/types.ts
  • packages/novu/src/commands/sync.ts
  • packages/novu/src/constants/constants.ts
  • packages/novu/src/index.ts
💤 Files with no reviewable changes (1)
  • apps/api/src/app/agents/e2e/mock-agent-handler.ts

Comment thread apps/api/src/app/agents/usecases/update-agent/update-agent.usecase.ts Outdated
Comment thread packages/novu/src/commands/dev/dev.ts Outdated
Comment thread packages/novu/src/commands/sync.ts
Comment thread packages/novu/src/commands/sync.ts
- Reject empty behavior payloads (behavior: {} no longer counts as update)
- Allow clearing devBridgeUrl with null on production environments
- URL-encode agentId in CLI bridge activation and sync paths
- Remove redundant /novu from sync discovery URL
- Validate agentIdentifier as slug before template interpolation
- Add blank lines before return statements per style convention
- Use NOVU_SECRET_KEY/NOVU_API_URL from env instead of CLI flags

Made-with: Cursor
Keep per-agent bridge URL resolution from config (HEAD) over
the older environment-level lookup introduced on next.

Made-with: Cursor
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/novu/src/commands/dev/dev.ts (1)

198-207: Add the required blank line before these return statements.

The new helper branches here don't follow the repo's return-spacing rule.

As per coding guidelines "Include a blank line before every return statement".

Also applies to: 222-234, 241-241

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/novu/src/commands/dev/dev.ts` around lines 198 - 207, Add a blank
line immediately before every return in the helper that handles the fetch
response: insert an empty line before the `return [];` after the `if (!res.ok)`
check, before the `return (data.agents ?? []).map((a) => a.agentId);` line, and
before the `return [];` inside the `catch` block; apply the same
blank-line-before-return rule to the other new helper branches that return early
in this area so all `return` statements in this function are preceded by a blank
line.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/dashboard/src/components/agents/agent-sidebar-widget.tsx`:
- Around line 52-56: The URL tooltip trigger uses TooltipTrigger with a
non-focusable span, preventing keyboard users from revealing the tooltip; make
the trigger element focusable so TooltipTrigger can open on keyboard
focus/Enter/Space by replacing the plain <span> used around {url} with a
focusable element (e.g., a <button> or the same <span> with tabIndex={0} and
role="button"/aria-label) while preserving the existing className styling (the
text-text-sub font-code text-label-xs block max-w-[160px] truncate
tracking-tight) so visual layout doesn't change; ensure the element remains
focusable and accessible to screen readers so TooltipTrigger responds to
keyboard interactions.
- Around line 72-83: The DEV badge rendering in agent-sidebar-widget.tsx can be
misleading because it checks only agent.devBridgeActive while the actual
activeBridgeUrl is computed from agent.devBridgeActive && agent.devBridgeUrl;
update the badge condition to reflect the true active source by checking both
agent.devBridgeActive and agent.devBridgeUrl (or compare activeBridgeUrl ===
agent.devBridgeUrl) before rendering the Badge inside the SidebarRow so the DEV
badge only appears when the dev bridge URL is actually being used.

In `@packages/novu/src/commands/dev/dev.ts`:
- Around line 211-234: activateAgentBridge currently treats non-2xx HTTP
responses as best-effort but does not catch network/DNS/TLS exceptions from
fetch, allowing those errors to bubble up and abort the dev command; wrap the
fetch call (and subsequent response handling) in a try/catch inside
activateAgentBridge, catch any thrown errors from fetch, log a yellow warning
using chalk with the agentId and error message, and return false on error so
bridge activation failures remain best-effort and do not crash devCommand.

---

Nitpick comments:
In `@packages/novu/src/commands/dev/dev.ts`:
- Around line 198-207: Add a blank line immediately before every return in the
helper that handles the fetch response: insert an empty line before the `return
[];` after the `if (!res.ok)` check, before the `return (data.agents ??
[]).map((a) => a.agentId);` line, and before the `return [];` inside the `catch`
block; apply the same blank-line-before-return rule to the other new helper
branches that return early in this area so all `return` statements in this
function are preceded by a blank line.
🪄 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: Pro

Run ID: 0f5aad58-f1f5-46c5-be5a-a1458f4f0d98

📥 Commits

Reviewing files that changed from the base of the PR and between 2c0756e and 4dcec79.

📒 Files selected for processing (10)
  • apps/api/src/app/agents/usecases/update-agent/update-agent.usecase.ts
  • apps/dashboard/src/components/agents/agent-sidebar-widget.tsx
  • packages/novu/src/commands/dev/dev.ts
  • packages/novu/src/commands/dev/types.ts
  • packages/novu/src/commands/init/create-app.ts
  • packages/novu/src/commands/init/index.ts
  • packages/novu/src/commands/init/templates/index.ts
  • packages/novu/src/commands/init/templates/types.ts
  • packages/novu/src/commands/sync.ts
  • packages/novu/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/novu/src/commands/init/templates/types.ts
  • packages/novu/src/commands/dev/types.ts
  • packages/novu/src/commands/init/create-app.ts
  • packages/novu/src/commands/init/index.ts
  • packages/novu/src/index.ts
  • packages/novu/src/commands/init/templates/index.ts
  • apps/api/src/app/agents/usecases/update-agent/update-agent.usecase.ts
  • packages/novu/src/commands/sync.ts

Comment thread apps/dashboard/src/components/agents/agent-sidebar-widget.tsx
Comment thread apps/dashboard/src/components/agents/agent-sidebar-widget.tsx Outdated
Comment thread packages/novu/src/commands/dev/dev.ts
- Make URL tooltip keyboard-accessible (span → button)
- Fix DEV badge showing when devBridgeUrl is missing
- Wrap fetch in try/catch so network errors don't crash novu dev

Made-with: Cursor
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/api/src/app/agents/services/bridge-executor.service.ts (1)

215-240: Bridge URL resolution logic is correct.

The priority chain (devBridgeActive && devBridgeUrlbridgeUrlnull) aligns with the PR's per-agent bridge specification. The new URL() constructor provides implicit validation by throwing on malformed URLs.

Minor: per coding guidelines, add a blank line before return statements.

♻️ Suggested formatting fix
     url.searchParams.set('agentId', agentIdentifier);
     url.searchParams.set('event', event);
 
+
     return url.toString();
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/src/app/agents/services/bridge-executor.service.ts` around lines 215
- 240, In resolveBridgeUrl, add a blank line before each return to match
formatting guidelines: insert an empty line before the early "return null;"
after this.logger.warn(...) and another blank line before the final "return
url.toString();" to separate logic from return statements; references:
resolveBridgeUrl, this.logger.warn, return null, return url.toString().
packages/framework/src/resources/agent/agent.test.ts (1)

489-533: Consider asserting serialized JSX children to harden this test.

Right now the test verifies card presence/type/title, but a regression in JSX child conversion could still pass. Adding child assertions would make this scenario more robust.

Suggested assertion extension
     expect(replyBody.reply.card).toBeDefined();
     expect(replyBody.reply.card.type).toBe('card');
     expect(replyBody.reply.card.title).toBe('JSX Card');
+    expect(replyBody.reply.card.children).toHaveLength(2);
+    expect(replyBody.reply.card.children[0]).toMatchObject({ type: 'cardText', content: 'Hello from JSX' });
+    expect(replyBody.reply.card.children[1]).toMatchObject({ type: 'button', id: 'ok', label: 'OK' });
     expect(replyBody.reply.text).toBeUndefined();
     expect(replyBody.reply.markdown).toBeUndefined();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/framework/src/resources/agent/agent.test.ts` around lines 489 - 533,
The test currently checks only card presence/type/title but should also assert
the serialized JSX children to prevent regressions: after parsing replyBody in
the 'should serialize JSX Card elements on reply' test, verify that
replyBody.reply.card.children includes the CardText content ('Hello from JSX')
and that the Button child serializes expected properties (id 'ok', label 'OK',
style 'primary'); reference the jsxCard, CardText, Button, testBot and
NovuRequestHandler symbols to locate the test and add assertions against
replyBody.reply.card.children and the specific child objects for text/button
fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/api/src/app/agents/services/bridge-executor.service.ts`:
- Around line 215-240: In resolveBridgeUrl, add a blank line before each return
to match formatting guidelines: insert an empty line before the early "return
null;" after this.logger.warn(...) and another blank line before the final
"return url.toString();" to separate logic from return statements; references:
resolveBridgeUrl, this.logger.warn, return null, return url.toString().

In `@packages/framework/src/resources/agent/agent.test.ts`:
- Around line 489-533: The test currently checks only card presence/type/title
but should also assert the serialized JSX children to prevent regressions: after
parsing replyBody in the 'should serialize JSX Card elements on reply' test,
verify that replyBody.reply.card.children includes the CardText content ('Hello
from JSX') and that the Button child serializes expected properties (id 'ok',
label 'OK', style 'primary'); reference the jsxCard, CardText, Button, testBot
and NovuRequestHandler symbols to locate the test and add assertions against
replyBody.reply.card.children and the specific child objects for text/button
fields.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 96f28d6e-adfa-4a06-9732-7e71ec3b7668

📥 Commits

Reviewing files that changed from the base of the PR and between 4dcec79 and 2a86554.

📒 Files selected for processing (3)
  • apps/api/src/app/agents/e2e/mock-agent-handler.ts
  • apps/api/src/app/agents/services/bridge-executor.service.ts
  • packages/framework/src/resources/agent/agent.test.ts
💤 Files with no reviewable changes (1)
  • apps/api/src/app/agents/e2e/mock-agent-handler.ts

@ChmaraX ChmaraX merged commit afc3d0c into next Apr 16, 2026
29 of 38 checks passed
@ChmaraX ChmaraX deleted the nv-7373-per-agent-bridge-url-with-devproduction-toggle branch April 16, 2026 08:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant