Skip to content

feat(dashboard, api-service): Filter conversations by agent; add recent UI & CTA#10767

Merged
scopsy merged 5 commits intonextfrom
recent-conversations-flow
Apr 17, 2026
Merged

feat(dashboard, api-service): Filter conversations by agent; add recent UI & CTA#10767
scopsy merged 5 commits intonextfrom
recent-conversations-flow

Conversation

@scopsy
Copy link
Copy Markdown
Contributor

@scopsy scopsy commented Apr 17, 2026

Expose agentId filtering end-to-end and introduce a compact recent conversations UI with upgrade CTA.

  • API: add agentId to ConversationFilters and include it in getConversationsList query params.
  • DAL: apply agentId (_agentId) filter in ConversationRepository queries.
  • UI: refactor RecentConversationsSection to fetch recent conversations for an agent, render skeleton/error/empty states, conversation items, and format timestamps; gate detailed list behind IS_ENTERPRISE and show ConversationsUpgradeCta for non-enterprise users.
  • Conversations page: gate main conversations content behind IS_ENTERPRISE and split into EnterpriseConversationsContent.
  • Add ConversationsUpgradeCta component (compact/default variants) to prompt upgrade/contact-sales and link docs.
  • Update submodule reference (.source).

These changes enable agent-scoped conversation listing and a clear upgrade path for non-enterprise installs.

What changed? Why was the change needed?

What changed

Agent-scoped conversation listing was implemented end-to-end: API and DAL accept an optional agentId filter, the dashboard UI fetches and displays recent conversations scoped to an agent, and a compact Recent Conversations panel plus an upgrade CTA were added to handle non-enterprise/self-hosted installs. The change enables agent-specific conversation views and provides a clear upgrade/contact-sales path where full conversation listing is enterprise-only.

Affected areas

  • api: ConversationFilters now includes an optional agentId and getConversationsList forwards agentId as a query parameter when provided.
  • dal: listConversations accepts an optional agentId and applies an _agentId equality filter; conversation schema adds compound indexes on {_environmentId, _agentId, _id}, {_environmentId, _agentId, createdAt}, and {_environmentId, _agentId, lastActivityAt}.
  • dashboard (react): RecentConversationsSection now accepts an agent prop, fetches recent conversations filtered by agentId, renders loading/error/empty states and conversation rows with formatted timestamps; ConversationsContent is gated by IS_ENTERPRISE and delegates enterprise UI to EnterpriseConversationsContent; ConversationsUpgradeCta component (compact/default) was added to prompt upgrades and link docs.
  • submodule: Updated .source submodule pointer to a new commit.

Key technical decisions

  • Enterprise gating: full conversation listing and navigation are only available when IS_ENTERPRISE is true; non-enterprise installs see an upgrade CTA that routes differently for self-hosted vs SaaS.
  • Schema/indexing: added agent-scoped compound indexes to support efficient queries by (_environmentId, _agentId) for createdAt, lastActivityAt, and _id.
  • API contract: ConversationFilters gained an additive optional agentId (non-breaking).

Testing

No automated tests were added; manual verification is recommended for agent-scoped listing correctness, UI loading/error/empty states, enterprise gating behavior, CTA navigation for self-hosted vs SaaS, and telemetry firing on CTA clicks.

Screenshots

Expand for optional sections

Related enterprise PR

Special notes for your reviewer

Expose agentId filtering end-to-end and introduce a compact recent conversations UI with upgrade CTA.

- API: add agentId to ConversationFilters and include it in getConversationsList query params.
- DAL: apply agentId (_agentId) filter in ConversationRepository queries.
- UI: refactor RecentConversationsSection to fetch recent conversations for an agent, render skeleton/error/empty states, conversation items, and format timestamps; gate detailed list behind IS_ENTERPRISE and show ConversationsUpgradeCta for non-enterprise users.
- Conversations page: gate main conversations content behind IS_ENTERPRISE and split into EnterpriseConversationsContent.
- Add ConversationsUpgradeCta component (compact/default variants) to prompt upgrade/contact-sales and link docs.
- Update submodule reference (.source).

These changes enable agent-scoped conversation listing and a clear upgrade path for non-enterprise installs.
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 17, 2026

Deploy Preview for dashboard-v2-novu-staging canceled.

Name Link
🔨 Latest commit 70c73a5
🔍 Latest deploy log https://app.netlify.com/projects/dashboard-v2-novu-staging/deploys/69e1f5c9670e1900084b8174

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 17, 2026

Hey there and thank you for opening this pull request! 👋

We require pull request titles to follow specific formatting rules and it looks like your proposed title needs to be adjusted.

Your PR title is: feat(dashboard, api-service): Filter conversations by agent; add recent UI & CTA

Requirements:

  1. Follow the Conventional Commits specification
  2. As a team member, include Linear ticket ID at the end: fixes TICKET-ID or include it in your branch name

Expected format: feat(scope): Add fancy new feature fixes NOV-123

Details:

PR title must end with 'fixes TICKET-ID' (e.g., 'fixes NOV-123') or include ticket ID in branch name

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 79e0a1e2-c397-4946-b64f-02a470700ba3

📥 Commits

Reviewing files that changed from the base of the PR and between 5c4aeae and 70c73a5.

📒 Files selected for processing (1)
  • .source
✅ Files skipped from review due to trivial changes (1)
  • .source

📝 Walkthrough

Walkthrough

Adds optional agent-scoped conversation filtering across API and DAL, updates MongoDB indexes, gates conversations UI behind an enterprise flag with an upgrade CTA for non-enterprise, refactors recent-conversations to fetch/render agent-specific items, and updates the .source submodule pointer.

Changes

Cohort / File(s) Summary
Submodule reference
.source
Updated submodule commit pointer from bb487b7... to ff75602....
API types & client
apps/dashboard/src/api/conversations.ts
Added optional agentId?: string to ConversationFilters and include agentId in conversation list request query when provided.
DAL repository & schema
libs/dal/src/repositories/conversation/conversation.repository.ts, libs/dal/src/repositories/conversation/conversation.schema.ts
listConversations accepts agentId and applies _agentId equality filter; added compound indexes including _agentId ({_environmentId, _agentId, _id}, {_environmentId, _agentId, createdAt}, {_environmentId, _agentId, lastActivityAt}).
Recent conversations UI
apps/dashboard/src/components/agents/recent-conversations-section.tsx
Component now accepts an agent prop, fetches recent conversations filtered by agent.identifier, and replaces static empty-state with loading/error/empty/data states and per-item links/rows.
Conversations feature gating
apps/dashboard/src/components/conversations/conversations-content.tsx
Extracted enterprise UI into EnterpriseConversationsContent; ConversationsContent returns ConversationsUpgradeCta when IS_ENTERPRISE is false.
Upgrade CTA component
apps/dashboard/src/components/conversations/conversations-upgrade-cta.tsx
Added ConversationsUpgradeCta component with telemetry, routing to billing or self-hosted upgrade URL, and documentation link (variants default/compact).

Sequence Diagram

sequenceDiagram
    participant UI as Dashboard (UI)
    participant API as API Layer\n(`apps/dashboard/src/api/conversations.ts`)
    participant DAL as Repository\n(`libs/dal/.../conversation.repository.ts`)
    participant DB as MongoDB

    UI->>UI: Check IS_ENTERPRISE
    alt Enterprise
        UI->>API: GET /conversations?limit=5&agentId=...
        API->>DAL: listConversations({ agentId, limit:5, ... })
        DAL->>DB: Query {_environmentId, _agentId, ...}
        DB-->>DAL: Documents
        DAL-->>API: Conversation list
        API-->>UI: Filtered results
        UI->>UI: Render recent list with links/status/subscriber
    else Non-Enterprise
        UI->>UI: Render ConversationsUpgradeCta
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

@novu/api-service

🚥 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 valid type (feat) and scopes (dashboard, api-service), uses imperative mood, and accurately describes the main changes: agent-based conversation filtering, recent conversations UI, and upgrade CTA.

✏️ 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.

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: 4

🤖 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/recent-conversations-section.tsx`:
- Around line 161-184: RecentConversationsSkeleton currently renders 4
placeholder rows causing a layout shift; change it to render
RECENT_CONVERSATIONS_DISPLAY_LIMIT placeholders instead: import or reference the
RECENT_CONVERSATIONS_DISPLAY_LIMIT constant and use it as the length in
Array.from inside RecentConversationsSkeleton (keep the existing key pattern
like `skeleton-${index}`), so the skeleton count matches the real display limit
of 5.
- Around line 104-118: RecentConversationItem currently builds a detailPath and
renders a Link even when environmentSlug is missing, resulting in a dead "#"
link; update RecentConversationItem to avoid a non-functional interactive
element by either early-returning (do not render the item) when environmentSlug
is undefined or render a non-interactive container (e.g., a div/buttonless
element with the same classes) instead of Link when environmentSlug is falsy;
change usage of detailPath and the Link import accordingly so the clickable Link
is only used when environmentSlug is present and the non-interactive fallback
preserves visual styles but removes hover/focus interactivity.

In `@apps/dashboard/src/components/conversations/conversations-upgrade-cta.tsx`:
- Around line 86-90: The current code nests a LinkButton inside react-router's
<Link> for an absolute URL, creating nested interactive elements; change this to
either render a plain anchor for the external URL or make LinkButton render the
anchor directly via its asChild prop: replace the outer <Link
to="https://docs.novu.co/agents/overview" ...> wrapper with a standard <a
href="https://docs.novu.co/agents/overview" target="_blank" rel="noreferrer
noopener"> or keep LinkButton but pass asChild so LinkButton renders the <a>
itself (keeping the RiBookMarkedLine icon and size="sm" props) to avoid invalid
nested <a>/<button> elements and fix accessibility.

In `@libs/dal/src/repositories/conversation/conversation.repository.ts`:
- Around line 249-251: The Conversation repository adds an _agentId filter in
the query (see the if (agentId) { query._agentId = agentId; } block) but the
Conversation schema lacks a compound index that covers queries scoped by
_environmentId plus _agentId; add a compound index on the Conversation model
like { _environmentId: 1, _agentId: 1, _id: 1 } to avoid collection scans for
listConversations and, if listConversations is commonly sorted by other fields
such as createdAt or lastActivityAt, also add indexes like { _environmentId: 1,
_agentId: 1, createdAt: 1 } or { _environmentId: 1, _agentId: 1, lastActivityAt:
1 } in the Conversation schema definition so queries using the query._agentId
predicate and various sortBy values are covered.
🪄 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: c25f5e2f-cb74-4fd8-8f9b-4cb24a43c072

📥 Commits

Reviewing files that changed from the base of the PR and between b86a31a and 74a604d.

📒 Files selected for processing (6)
  • .source
  • apps/dashboard/src/api/conversations.ts
  • apps/dashboard/src/components/agents/recent-conversations-section.tsx
  • apps/dashboard/src/components/conversations/conversations-content.tsx
  • apps/dashboard/src/components/conversations/conversations-upgrade-cta.tsx
  • libs/dal/src/repositories/conversation/conversation.repository.ts

Comment thread apps/dashboard/src/components/agents/recent-conversations-section.tsx Outdated
Comment thread apps/dashboard/src/components/conversations/conversations-upgrade-cta.tsx Outdated
Comment thread libs/dal/src/repositories/conversation/conversation.repository.ts
Refactor RecentConversationItem to extract base and interactive classNames, render a non-interactive div when environmentSlug is missing, and only wrap with a Link when environmentSlug exists. Use RECENT_CONVERSATIONS_DISPLAY_LIMIT for skeleton item count. Update ConversationsUpgradeCta to use LinkButton asChild with a native anchor for the external docs link and remove the unused Link import. Add agent-scoped indexes to the conversation schema (_environmentId + _agentId with _id, createdAt, lastActivityAt) to improve query performance for agent-scoped operations.
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: 1

🧹 Nitpick comments (2)
apps/dashboard/src/components/agents/recent-conversations-section.tsx (2)

22-26: Minor: duplicated useEnvironment() call.

currentEnvironment is read here and again in RecentConversationsContent (line 57). Since the parent already computes conversationsPath, consider passing currentEnvironment?.slug (or conversationsPath) down as a prop to avoid re-subscribing to the context in the child.

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

In `@apps/dashboard/src/components/agents/recent-conversations-section.tsx` around
lines 22 - 26, The component calls useEnvironment() twice (once here to build
conversationsPath and again inside RecentConversationsContent), causing a
duplicate context subscription; pass the computed value down instead: compute
currentEnvironment?.slug (or conversationsPath) in this component (where
useEnvironment() is already used), then add a prop to RecentConversationsContent
(e.g., environmentSlug or conversationsPath) and update
RecentConversationsContent to consume that prop instead of calling
useEnvironment() itself (update usages around
buildRoute/ROUTES.ACTIVITY_CONVERSATIONS accordingly).

207-211: conversation._agentId fallback will render as a raw ObjectId.

If neither agent.agent?.name nor agent?.id is present, the UI displays the raw ObjectId from _agentId which is not user-friendly. Since the parent RecentConversationsSection already has the full AgentResponse in scope, consider threading the agent's display name down to RecentConversationItem and using it as the fallback (all listed conversations belong to that agent anyway).

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

In `@apps/dashboard/src/components/agents/recent-conversations-section.tsx` around
lines 207 - 211, getAgentName currently falls back to rendering
conversation._agentId (a raw ObjectId); instead, pass the parent AgentResponse's
display name down from RecentConversationsSection into each
RecentConversationItem and use that as the final fallback. Update the caller in
RecentConversationsSection to provide a prop (e.g., agentDisplayName or
agent.name) to RecentConversationItem, then change getAgentName(conversation:
ConversationDto) to accept an optional agentDisplayName parameter and return
agent?.agent?.name ?? agent?.id ?? agentDisplayName ?? 'agent'; ensure function
and prop names (getAgentName, RecentConversationItem,
RecentConversationsSection, AgentResponse, conversation._agentId) are used
consistently.
🤖 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/recent-conversations-section.tsx`:
- Around line 59-62: The useFetchConversations call is passing the wrong agent
identifier; update the filters argument in the useFetchConversations invocation
(the call that uses RECENT_CONVERSATIONS_DISPLAY_LIMIT) to use agent._id instead
of agent.identifier so the agentId filter matches the ObjectId stored in the
conversation schema.

---

Nitpick comments:
In `@apps/dashboard/src/components/agents/recent-conversations-section.tsx`:
- Around line 22-26: The component calls useEnvironment() twice (once here to
build conversationsPath and again inside RecentConversationsContent), causing a
duplicate context subscription; pass the computed value down instead: compute
currentEnvironment?.slug (or conversationsPath) in this component (where
useEnvironment() is already used), then add a prop to RecentConversationsContent
(e.g., environmentSlug or conversationsPath) and update
RecentConversationsContent to consume that prop instead of calling
useEnvironment() itself (update usages around
buildRoute/ROUTES.ACTIVITY_CONVERSATIONS accordingly).
- Around line 207-211: getAgentName currently falls back to rendering
conversation._agentId (a raw ObjectId); instead, pass the parent AgentResponse's
display name down from RecentConversationsSection into each
RecentConversationItem and use that as the final fallback. Update the caller in
RecentConversationsSection to provide a prop (e.g., agentDisplayName or
agent.name) to RecentConversationItem, then change getAgentName(conversation:
ConversationDto) to accept an optional agentDisplayName parameter and return
agent?.agent?.name ?? agent?.id ?? agentDisplayName ?? 'agent'; ensure function
and prop names (getAgentName, RecentConversationItem,
RecentConversationsSection, AgentResponse, conversation._agentId) are used
consistently.
🪄 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: 218e3b28-c631-4893-ba5d-773522edf890

📥 Commits

Reviewing files that changed from the base of the PR and between 74a604d and 5c4aeae.

📒 Files selected for processing (3)
  • apps/dashboard/src/components/agents/recent-conversations-section.tsx
  • apps/dashboard/src/components/conversations/conversations-upgrade-cta.tsx
  • libs/dal/src/repositories/conversation/conversation.schema.ts

Comment on lines +59 to +62
const { conversations, isLoading, isError } = useFetchConversations({
limit: RECENT_CONVERSATIONS_DISPLAY_LIMIT,
filters: { agentId: agent.identifier },
});
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Apr 17, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect AgentResponse shape and how _agentId is populated on conversations.
fd -t f 'agents.ts' apps/dashboard/src/api | xargs -I{} sh -c 'echo "=== {} ==="; cat {}'
echo "---- agent entity / schema ----"
fd -t f 'agent.entity.ts|agent.schema.ts' libs/dal/src | xargs -I{} sh -c 'echo "=== {} ==="; cat {}'
echo "---- conversation creation / _agentId assignment ----"
rg -nP --type=ts -C3 '_agentId\s*[:=]'

Repository: novuhq/novu

Length of output: 30067


🏁 Script executed:

fd -t f 'conversations.ts' apps/dashboard/src -type f

Repository: novuhq/novu

Length of output: 225


🏁 Script executed:

rg -n 'useFetchConversations' apps/dashboard/src --type=ts --type=tsx -A 5

Repository: novuhq/novu

Length of output: 82


🏁 Script executed:

rg -n 'const.*fetchConversations\|export.*useFetchConversations' apps/dashboard/src --type=ts --type=tsx -B 2 -A 10

Repository: novuhq/novu

Length of output: 82


🏁 Script executed:

fd -type f 'conversations' apps/dashboard/src

Repository: novuhq/novu

Length of output: 225


🏁 Script executed:

rg 'useFetchConversations' apps/dashboard/src -g '*.ts' -g '*.tsx' -A 5

Repository: novuhq/novu

Length of output: 3160


🏁 Script executed:

rg 'fetchConversations|useFetchConversations' apps/dashboard/src -g '*.ts' -g '*.tsx' -B 2 -A 10

Repository: novuhq/novu

Length of output: 9446


🏁 Script executed:

cat apps/dashboard/src/api/conversations.ts

Repository: novuhq/novu

Length of output: 4696


Use agent._id instead of agent.identifier for the agentId filter.

The conversation schema stores agent references as _agentId: Schema.Types.ObjectId, but you're filtering with agent.identifier (a string slug). The API will send ?agentId=<identifier-slug> to the backend, which tries to match it against an ObjectId field. This causes Mongoose to either throw a cast error or silently return zero results, leaving the Recent Conversations list empty.

Change line 61 to:

filters: { agentId: agent._id },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/components/agents/recent-conversations-section.tsx` around
lines 59 - 62, The useFetchConversations call is passing the wrong agent
identifier; update the filters argument in the useFetchConversations invocation
(the call that uses RECENT_CONVERSATIONS_DISPLAY_LIMIT) to use agent._id instead
of agent.identifier so the agentId filter matches the ObjectId stored in the
conversation schema.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

But the backend converts the identifier to _id

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for backend conversion of agentId/identifier to _id in conversation-related code
rg -n 'identifier|agentId' apps/api/src --type=ts -g '*conversation*' -A 5 -B 2

echo "---- Check use-cases or services that handle agentId ----"
rg -rn 'agentId' apps/api/src --type=ts -A 5 -B 2

Length of output: 95710


🏁 Script executed:

#!/bin/bash
# Find the list-conversations usecase to see how agentId is processed
rg -rn 'agentId' apps/api/src --type=ts -g '*conversation*' -A 5 -B 2

echo "---- list conversations usecase ----"
fd -t f 'list-conversations' apps/api/src | xargs -I{} sh -c 'echo "=== {} ==="; cat {}'

echo "---- conversations controller ----"
fd -t f 'conversations.controller.ts' apps/api/src | xargs -I{} sh -c 'echo "=== {} ==="; cat {}'

Length of output: 1897


🏁 Script executed:

#!/bin/bash
# Broader search for conversations listing and agentId handling
rg -rn 'agentId' apps/api/src --type=ts -A 5 -B 2

echo "==== conversations controller candidates ===="
fd -t f 'conversation' apps/api/src --extension ts | grep -v 'agent-conversation'

echo "==== list conversations ===="
rg -rn 'listConversations\|list_conversations\|getConversationsList\|ConversationFilters' apps/api/src --type=ts -A 5 -B 2

Length of output: 92861


🏁 Script executed:

#!/bin/bash
# Find the conversations listing API endpoint in apps/api
fd -t f --extension ts 'conversation' apps/api/src | grep -v 'agent-conversation\|e2e\|spec'

echo "==== search for agentId in non-agents context ===="
rg -rn 'agentId' apps/api/src --type=ts --iglob '!*agent*' -A 5 -B 3

echo "==== dal listConversations call sites ===="
rg -rn 'listConversations' --type=ts -A 5 -B 3

Length of output: 789


🏁 Script executed:

#!/bin/bash
cat libs/dal/src/repositories/conversation/conversation.repository.ts

echo "==== Find conversations API controller/usecase ===="
fd -t f --extension ts . apps/api/src | xargs grep -l 'agentId\|agent_id\|_agentId' 2>/dev/null | grep -v 'agent-conversation\|bridge-executor\|chat-sdk\|agent-inbound\|agent-config'

echo "==== conversations usecase files ===="
fd -t f --extension ts . apps/api/src/app | xargs grep -l 'ConversationFilters\|conversations.*usecase\|usecase.*conversation' 2>/dev/null

Length of output: 8864


🏁 Script executed:

#!/bin/bash
# Find conversations API endpoint/controller outside agents module
rg -rn 'getConversationsList\|conversations.*list\|list.*conversations' apps/api/src --type=ts -B 2 -A 5

echo "==== search for conversation routes ===="
rg -rn "'/conversations'\|\"conversations\"" apps/api/src --type=ts -B 2 -A 3

echo "==== Check what URL dashboard calls for conversations ===="
cat apps/dashboard/src/api/conversations.ts

Length of output: 4741


🏁 Script executed:

#!/bin/bash
# Find the /conversations endpoint handler
rg -rn "conversations" apps/api/src --type=ts -g '*controller*' -B 2 -A 5

echo "==== search for conversation module ===="
fd -t d 'conversation' apps/api/src

echo "==== check all controllers for conversations route ===="
fd -t f 'controller' apps/api/src --extension ts | xargs grep -l 'conversation' 2>/dev/null

Length of output: 194


🏁 Script executed:

#!/bin/bash
# Search across all apps for the conversations GET endpoint
rg -rn "Get.*conversations\|conversations.*Get\|@Get.*conver\|route.*conversation\|conversation.*route" --type=ts -B 2 -A 5

echo "==== find conversations endpoint in all apps ===="
rg -rn "'/conversations'\|\"\/conversations\"\|'/v1/conversations'\|v1.*conversations" --type=ts -B 2 -A 5

Length of output: 210


🏁 Script executed:

#!/bin/bash
# Search for conversations endpoint across all apps
rg -rn "conversations" --type=ts -g '*controller*' -B 2 -A 5

echo "==== find route /conversations ===="
rg -rn "conversations" --type=ts -g '*router*' -B 2 -A 5

echo "==== search all TS files for conversations endpoint ===="
rg -rn "@Get\('conversations'\)" --type=ts -B 2 -A 5

echo "==== look for usecase that handles list conversations with agentId conversion ===="
rg -rn "agentId" --type=ts -g '!**/agents/**' -B 3 -A 5

Length of output: 75597


@scopsy Thanks for the clarification — you’re right that several agent endpoints take an identifier and resolve it to _id server‑side. For conversations, though, the listing path expects the agent’s ObjectId under query param n, and the DAL filters on _n (ObjectId).

Concretely:

  • Conversation schema uses _n: ObjectId
  • ConversationRepository.listConversations takes n and sets query._n = n
  • Dashboard API client appends n when provided in filters and ConversationDto exposes _n

Given that, this component should pass the agent’s _id under n rather than the textual identifier under agentId. Suggested change in apps/dashboard/src/components/agents/recent-conversations-section.tsx:

  const { conversations, isLoading, isError } = useFetchConversations({
    limit: RECENT_CONVERSATIONS_DISPLAY_LIMIT,
-   filters: { agentId: agent.identifier },
+   filters: { n: agent._id },
  });

This aligns the UI with the conversations API contract and will reliably scope results to the agent. If you prefer keeping a friendlier prop name (agentId) in the hook, we can also translate agentId → n inside useFetchConversations, but today the API client already speaks in terms of n, so wiring it directly here is the smallest change.


🧠 Learnings used
Learnt from: CR
Repo: novuhq/novu PR: 0
File: .cursor/rules/pullrequest.mdc:0-0
Timestamp: 2026-03-24T13:18:35.551Z
Learning: Pull request title format: `type(scope): Description fixes NOV-<ticket-id>` (omit `fixes NOV-XXX` when no Linear ticket is in context). Example: `feat(dashboard): add workflow trigger button fixes NOV-123`

@scopsy scopsy merged commit 029e328 into next Apr 17, 2026
37 checks passed
@scopsy scopsy deleted the recent-conversations-flow branch April 17, 2026 08:59
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