diff --git a/.cursor/skills/pre-pr-review-fixes/SKILL.md b/.cursor/skills/pre-pr-review-fixes/SKILL.md new file mode 100644 index 000000000..504c21c29 --- /dev/null +++ b/.cursor/skills/pre-pr-review-fixes/SKILL.md @@ -0,0 +1,83 @@ +--- +name: pre-pr-review-fixes +description: Fix pull request feedback before opening or updating a PR. Use when the user asks to address reviewer comments, clean up docs/style issues before PR, or apply repeated feedback patterns (wording, headings, spacing, capitalization, markdown tables, and consistency fixes). +disable-model-invocation: true +--- + +# Pre-PR Review Fixes + +Use this skill to systematically apply reviewer feedback before creating or updating a PR. + +## When to use + +- User asks to "fix PR comments" or "address review feedback" +- User asks to "prepare this for PR" or "clean this up before PR" +- Same reviewer repeatedly flags style/structure issues across multiple files + +## Why this is a skill (not a rule) + +- This workflow is task-specific and should run on demand. +- A global rule would trigger too often and add overhead to unrelated tasks. +- Skills are easier to extend with new reviewer patterns over time. + +## Inputs expected + +- PR URL or PR number +- Reviewer name(s) to prioritize (if applicable) +- Optional scope limits (files/folders) + +## Workflow + +1. Fetch review feedback + - Pull top-level review summaries and inline comments. + - Filter to actionable reviewer comments (ignore bot noise unless requested). +2. Build an action list + - Group comments by file and section. + - Normalize into concrete edits (wording, formatting, structure, examples, links). + - Deduplicate repeated comments and mark global patterns to apply throughout. +3. Apply fixes in files + - Edit only requested/scope-relevant files. + - Keep existing technical intent; change style/clarity unless behavior changes are requested. +4. Validate + - Run lints on edited files. + - Re-read changed sections to ensure comment intent is fully addressed. +5. Report back + - List updated files. + - Map key edits to reviewer themes. + - Call out any comments that were ambiguous or not applicable. + +## Common docs fixes checklist + +- Add a context paragraph before a heading/table when needed +- Avoid bold text as faux headings; use real heading levels +- Use italics (not bold) for light emphasis when requested +- Remove extra blank lines (keep one empty line between blocks) +- Fix incomplete/ambiguous phrasing +- Standardize reference style: "For ..., refer to [Link](url)." +- Replace vague endings like `etc.` with explicit wording +- Keep capitalization consistent across bullets/tables +- Ensure markdown tables are valid and readable + +## Comment triage rules + +- **Apply directly:** explicit suggestions and clear style requests +- **Apply throughout:** repeated reviewer guidance across multiple files +- **Ask user before changing:** unclear product intent, technical behavior changes, or conflicting reviewer guidance +- **Skip with note:** non-actionable comments (for example, design critique with no available source/update path) + +## Output format to user + +Use this structure: + +1. Files changed +2. What was fixed (by reviewer theme) +3. What was not changed (and why) +4. Validation result (lint/tests run) + +## Extension notes + +To extend this skill, add: + +- A `patterns.md` file with reviewer-specific conventions +- A `commands.md` file with reusable `gh` queries for PR feedback +- Team-specific wording conventions for docs pages diff --git a/content/docs/agents/add-connect-components.mdx b/content/docs/agents/add-connect-components.mdx new file mode 100644 index 000000000..90404af4f --- /dev/null +++ b/content/docs/agents/add-connect-components.mdx @@ -0,0 +1,113 @@ +--- +title: "Add Connect Components" +pageTitle: 'Novu Agents Add Connect Components' +description: 'Add Connect UI components so users can link accounts and channels to your agent.' +--- + +Novu provides pre-built UI components for chat platforms like Slack and Microsoft Teams. Add these components so users can install the Slack or Microsoft Teams app in their workspace and connect it to your agent. + +## Slack connect button + +`SlackConnectButton` is a pre-built UI component in the `@novu/react` SDK that connects an agent to a Slack workspace. Check out the example below. + +```tsx +import { SlackConnectButton } from '@novu/react'; + +const SlackConnectButtonComponent = () => { + const subscriberId = 'subscriber-id'; + const integrationIdentifier = 'integration-identifier'; + const agent = { + identifier: 'agent-identifier', + name: 'agent-name', + }; + + const handleSlackOAuthSuccess = () => { + // Handle success + }; + + return ( + { + console.error(error); + }} + /> + ); +}; + +export default SlackConnectButtonComponent; +``` + +### API reference + +`SlackConnectButton` accepts the following props to customize the UI and behavior: + +>" + }, + "scope": { + "description": "", + "type": "string[]" + }, + "connectionMode": { + "description": "", + "type": "ConnectionMode" + }, + "onConnectSuccess": { + "description": "", + "type": "((connectionIdentifier: string) => void)" + }, + "onConnectError": { + "description": "", + "type": "((error: unknown) => void)" + }, + "onDisconnectSuccess": { + "description": "", + "type": "(() => void)" + }, + "onDisconnectError": { + "description": "", + "type": "((error: unknown) => void)" + }, + "connectLabel": { + "description": "", + "type": "string" + }, + "connectedLabel": { + "description": "", + "type": "string" + }, + "appearance": { + "description": "", + "type": "ReactInboxAppearance | ReactSubscriptionAppearance | ReactAllAppearance" + }, + "container": { + "description": "", + "type": "string | Node | null" + } +}} /> + +## Microsoft Teams connect button + + + We are working on adding a Microsoft Teams connect button to the `@novu/react` SDK. Reach out to us at support@novu.co to get access to a pre-release version. + diff --git a/content/docs/agents/add-connect-components.model.mdx b/content/docs/agents/add-connect-components.model.mdx new file mode 100644 index 000000000..9f568e5b7 --- /dev/null +++ b/content/docs/agents/add-connect-components.model.mdx @@ -0,0 +1,58 @@ +--- +title: "Add Connect Components" +pageTitle: 'Novu Agents Add Connect Components' +description: 'Add Connect UI components so users can link accounts and channels to your agent.' +--- + +Novu provides pre-built UI components for chat platforms like Slack and Microsoft Teams. Add these components so users can install the Slack or Microsoft Teams app in their workspace and connect it to your agent. + +## Slack connect button + +`SlackConnectButton` is a pre-built UI component in the `@novu/react` SDK that connects an agent to a Slack workspace. Check out the example below. + +```tsx +import { SlackConnectButton } from '@novu/react'; + +const SlackConnectButtonComponent = () => { + const subscriberId = 'subscriber-id'; + const integrationIdentifier = 'integration-identifier'; + const agent = { + identifier: 'agent-identifier', + name: 'agent-name', + }; + + const handleSlackOAuthSuccess = () => { + // Handle success + }; + + return ( + { + console.error(error); + }} + /> + ); +}; + +export default SlackConnectButtonComponent; +``` + +### API reference + +`SlackConnectButton` accepts the following props to customize the UI and behavior: + +---type-table--- +../platform/sdks/types/react-types.ts#SlackConnectButtonProps +---end--- + +## Microsoft Teams connect button + + + We are working on adding a Microsoft Teams connect button to the `@novu/react` SDK. Reach out to us at support@novu.co to get access to a pre-release version. + diff --git a/content/docs/agents/deploy-your-agent.mdx b/content/docs/agents/deploy-your-agent.mdx new file mode 100644 index 000000000..1792f422d --- /dev/null +++ b/content/docs/agents/deploy-your-agent.mdx @@ -0,0 +1,110 @@ +--- +title: "Agent Deployment" +pageTitle: 'Agent Deployment' +description: 'Learn how to run a conversational agent on your local machine, test it in a development environment, and deploy it to production.' +--- + +Agents built with Novu can run on your local machine. Deploy them to development and production environments when you are ready. + +## Agent on your local machine + +By default, the agent runs on your local machine. To run the agent locally: + +* Scaffold your agent with `npx novu init -t agent`. +* Add valid `NOVU_SECRET_KEY` and `NOVU_API_URL` values to your `.env` file. +* Run the scaffolded project on your local machine. +* Set the bridge option to `Local` in the agent overview section. + +When you complete these steps, run the agent using the Novu CLI command below. + +```bash +npx novu@latest dev --port --no-studio +``` + +* **bridge_application_port** - Port where your scaffolded project runs. Defaults to 4000. +* **no-studio** - Disables Studio. + +After you run the command, a tunnel starts on your machine. It forwards requests to your scaffolded project on `bridge_application_port`. + +Novu sets the bridge URL automatically to `tunnel-url/api/novu`. + +The agent listens for new messages and responds while running locally. + +## Agent in development + +After you test the agent locally, deploy it to a development environment. A scaffolded app from `npx novu init -t agent` uses Next.js and exposes a `/api/novu` endpoint for communicating with Novu. Deploy the app to your preferred hosting provider, then copy the deployed URL. + +* Add valid `NOVU_SECRET_KEY` and `NOVU_API_URL` values in the environment variables section of your hosting provider. +* Toggle the bridge option to `Development` in the agent overview section. +* For a deployed app at `https://dev.my-agent-app.com`, use `https://dev.my-agent-app.com/api/novu` as the bridge URL in the agent overview section. + +## Agent in production + +Before you use an agent in production, publish the agent from the development environment to the production environment. Use the *Publish changes* option on the dashboard. + +After you publish: + +* Publishing sends the agent to production with the same name, identifier, and description. +* The agent stays inactive until you activate it from the agent overview section. +* Configure and connect every provider again in the production environment. +* Deploy the agent bridge application to the production environment. +* Add valid `NOVU_SECRET_KEY` and `NOVU_API_URL` values in the environment variables section of your hosting provider. + +### Deploying to production + +After you publish the agent and complete the above steps, deploy the agent bridge application to the production environment. + +#### Using Novu CLI + +```bash +npx novu@latest sync --bridge-url --secret-key --api-url +``` + +For example, with `https://prod.my-agent-app.com` as the bridge app URL, use `https://prod.my-agent-app.com/api/novu` as the bridge URL. + +In the command above: + +* ``: URL of your deployed agent bridge application. +* ``: secret key for your Novu account. +* ``: API URL for your Novu account. + +After you run the command, the agent overview section shows the updated production bridge URL. + +#### Using GitHub Actions + +You can use our built-in GitHub Action to deploy your agent to the production environment.--> + +```yaml +name: Deploy agent to Novu Cloud + +on: + workflow_dispatch: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + # https://github.com/novuhq/actions-novu-sync + - name: Deploy agent to Novu Cloud + uses: novuhq/actions-novu-sync@v2 + with: + # The secret key used to authenticate with Novu Cloud + # To get the secret key, go to https://web.novu.co/api-keys. + # Required. + secret-key: ${{ secrets.NOVU_SECRET_KEY }} + + # The publicly available endpoint hosting the bridge application + # where notification entities (e.g. workflows, topics) are defined. + # Required. + bridge-url: ${{ secrets.NOVU_BRIDGE_URL }} + + # The Novu Cloud API URL to sync with. + # Optional. + # Defaults to https://api.novu.co + api-url: https://api.novu.co +``` + +Local tunnel bridge URLs cannot be activated on production environments (returns 403 Forbidden) to prevent accidental routing of production traffic to a local machine. diff --git a/content/docs/agents/get-started/core-concepts.mdx b/content/docs/agents/get-started/core-concepts.mdx new file mode 100644 index 000000000..078f904b0 --- /dev/null +++ b/content/docs/agents/get-started/core-concepts.mdx @@ -0,0 +1,187 @@ +--- +title: "Core Concepts" +pageTitle: 'Core Concepts' +description: "Understand the core entities, lifecycle, and building blocks inside Novu's Agent Communication Infrastructure implementation." +icon: Blocks +--- + +import { HomeIcon } from 'lucide-react'; + + +Novu for Agents is built around a set of concepts, these concepts are how Novu implements Agent Communication Infrastructure (ACI). + +## The mental model + +Novu for Agents connects messaging platform to your agent logic through a standard communication layer. This communication can be bi-directional, a user can message your agent, and your agent can proactively reach out to a user. + +When a user sends a message or interacts with your agent from a connected platform, the inbound flow works like this. Novu receives it, resolves identity and conversation state, and forwards the full context to your server. Your agent processes the message and replies, then Novu delivers the response back to the platform. The flow works like this: + +Here's what happens at each step: + +![architecture](/images/agents/concept/mental-model.png) + + +### Receive and identify + +Novu receives the platform webhook and normalizes it into a standard format. It matches the platform user, for example, a Slack user ID to a Novu subscriber. Known users get their full profile attached, while Novu tracks unknown users as anonymous until their identity is resolved. + +### Load conversation + +Novu creates a new conversation, if this is the first message or loads the existing one. The message history, metadata, and subscriber information are assembled into a single context object. + +### Your handler + +Novu calls your handler with the complete context: the message, conversation history, subscriber, and platform details. You process it with your LLM, business logic, or whatever system you've chosen, and call `ctx.reply()` to respond. + +### Deliver and persist + +Novu delivers the reply to the correct thread on the platform, persists it in the conversation history, and records the activity. Every message, signal, and status change is visible in the dashboard. + +Your server never talks to the platform directly. It receives a context object and returns a reply, Novu handles everything else. + +## Key entities + +Four core entities make up the system. + +### Agents + +An agent is the entity you create in Novu when you want to expose agent logic through one or more communication providers. + +Each agent has a name, an identifier, and a set of event handlers that define how your application responds to conversation events. An agent can be connected to one or more providers, such as Slack, Microsoft Teams, WhatsApp, or email. + +The agent doesn't define your AI model, prompts, tools, or business logic. It gives Novu a bridge to the application code where that logic runs. The provider handles where the user communicates. The agent handles how your application responds. + +### Providers connection + +A provider connection connects an agent to a messaging platform. Providers are the surfaces where users interact with your agent. A provider connection gives Novu the credentials and configuration needed to receive events from that provider and deliver responses back to it. + +Each provider has its own setup flow and platform capabilities. Some providers support reactions, typing indicators, attachments, rich cards, or message editing. Others may support only a subset of those behaviors. Novu normalizes provider events before passing them to your agent handler, so your code works with a consistent interface instead of provider-specific webhook payloads. + +This is what makes the architecture channel agnostic, you won't see anything related to Slack or WhatsApp or email in your agent code. Novu takes care of translating messages downstream to each provider's specific format and capabilities. + +### Conversations + +A conversation is the stateful thread where communication happens. When a user sends a message from a connected provider, Novu creates a new conversation or loads the existing one for that thread. The conversation includes the message history, metadata, participants, status, and platform context needed to process the next interaction. + +A conversation can include multiple participants. The agent is one participant in the conversation, but it is not the conversation itself. This distinction matters when you inspect conversations in the dashboard or build agent behavior around conversation state. A Slack thread, email thread, or other provider-specific thread maps to a conversation in Novu automatically. + +The conversations follow a simple lifecycle, a conversation starts as active when the first message arrives. Messages, actions, and reactions continue updating the conversation while it remains active. The conversation can be marked as resolved when the agent calls the resolve signal. When a conversation is resolved, the resolve handler fires and an optional summary is stored. + +If a user sends a new message after a conversation has been resolved, the conversation automatically reopens so the interaction can continue. + +### Participants and subscriber identity + +Participants are the people, agents, or systems involved in a conversation. When a user messages your agent from a provider, Novu tries to resolve that platform user to a known subscriber. + +- If a match exists, then the handler receives subscriber information such as the subscriber ID, name, or email. +- If no match exists, then the user is tracked as a platform user and your handler should account for cases where subscriber data is unavailable. + +Once identity is resolved later, they are upgraded to a full subscriber and future messages include their subscriber data. + +Subscriber identity helps your agent personalize responses, look up account details, or decide whether a conversation should be escalated. Identity resolution is provider-aware, a Slack user, or email sender may each have different identifiers at the provider level that Novu resolves to a single subscriber. + +## The bridge surface + +Novu exposes a consistent set of building blocks to your agent code. These are the same regardless of which provider the message came from or what brain you've plugged in. + +### Event handlers + +Event handlers are functions that respond to events in a conversation. Your agent can respond to four event types: + +| Handler | When it runs | Common use case | +|---------|-------------|-----------------| +| `onMessage` | A user sends a message in the conversation | Process the message and reply | +| `onAction` | A user clicks a button or selects a value in an interactive card | Handle form submissions, button clicks, or dropdown selections | +| `onReaction` | A user adds or removes a reaction | Capture feedback or trigger a follow-up | +| `onResolve` | The conversation is marked as resolved | Clean up state, log analytics, or send a summary | + +Handlers are where the communication layer connects to your application logic. For example, an `onMessage` handler receives the user's message, passes the conversation context to an LLM or custom function, and sends the response back through Novu. + +### Context object + +Each event handler receives a context object. The context object gives your handler the information it needs to understand the current event and respond to it. Depending on the event type, it can include: +* The incoming message. +* The current conversation state and metadata. +* The resolved subscriber (when available). +* Recent conversation history. +* Provider information. +* Platform-specific details, such as thread or channel identifiers. +* Methods for replying, updating metadata, triggering workflows, or resolving the conversation. + +The context object is the single interface between Novu's communication layer and your agent logic. Your code doesn't need to talk directly to Slack, Microsoft Teams, WhatsApp, or email, everything flows through the context. + +### Replies and interactions + +A reply sends a message back into the conversation. Agents can reply with different content types depending on the provider and platform capabilities: plain text, markdown with files, or interactive cards. + +Interactive cards let your agent send structured responses with components such as buttons, dropdowns, links, and text inputs. When a user interacts with a card, the `onAction` handler fires with the action ID and selected value. + +Replies are user-facing, use them when the agent needs to communicate something back to the participant in the conversation. + +### Signals + +Signals are system-facing actions your agent can perform during a conversation. Use signals when your handler needs to update state, trigger another process, or close a conversation, without necessarily sending another message to the user. + +| Signal | What it does | +|--------|-------------| +| Metadata | Stores key-value data on the conversation that persists across messages | +| Trigger | Starts a Novu workflow from within the conversation | +| Resolve | Marks the conversation as resolved with an optional summary | + +A useful way to think about the difference: replies communicate with the user, signals update the system around the conversation. A single handler invocation can do both, reply to the user, store a sentiment score in metadata, trigger an escalation workflow, and resolve the conversation. + +Signals aren't sent immediately when you call them. They're queued in memory and batched together with your next reply call in a single request. If your handler finishes without calling reply handler, any pending signals are still sent automatically. + +## Conversations and workflows + +ACI's conversation model and Novu's workflows are designed to work together. A conversation can trigger a workflow. + +For example, a user chatting with an agent in Slack asks it to prepare a report and send it via email. The agent calls a trigger function to fire a Novu workflow that handles the email delivery using the same workflow infrastructure your team might already have in place. + +A workflow can convert into a conversation. For example, you send a weekly digest notification to a user's email. The user replies to that email with a follow-up question, and that reply enters ACI as a new conversation with the agent. What started as a one-directional notification becomes a back-and-forth exchange. + +This connection means ACI is not a replacement for Novu's workflow system — it's an extension of it. The structured, predictable nature of transactional workflows and the open-ended, freeform nature of agent conversations are complementary. The value is in the combination of the two within the same platform. + +## How the Concepts Work Together + +Here is the end-to-end flow when a user messages your agent: + +1. A user sends a message to your agent from Slack. +2. Novu receives the Slack event through the provider connection. +3. Novu maps the Slack thread to a conversation and resolves the platform user to a subscriber when possible. +4. Novu calls the agent's `onMessage` handler with the context object. +5. Your handler passes the message and conversation context to your agent logic. +6. Your agent logic decides what should happen next. +7. Your handler sends a reply, emits signals, or both. +8. Novu delivers the reply back to the Slack thread. +9. Novu records the messages, participants, metadata, signals, and conversation status. + +The same agent logic works across all connected providers because Novu handles the provider-specific communication layer. Connecting a new provider does not require changing your agent code. + +## Next steps + +Now that you understand the core concepts, continue with: + + + } + href="/agents/getting-started/quickstart" + title="Quickstart" + > + Create your first agent, connect a provider, and send a message. + + } + href="/agents/agent-bridge/agent-definition" + title="Agent bridge" + > + Deep dive into defining agents, event handlers, context, replies, and signals.Create your first agent, connect a provider, and send a message. + + } + href="/agents/agent-logic/overview" + title="Agent logic" + > + Connect your preferred framework or runtime. + + \ No newline at end of file diff --git a/content/docs/agents/get-started/meta.json b/content/docs/agents/get-started/meta.json new file mode 100644 index 000000000..3470899ed --- /dev/null +++ b/content/docs/agents/get-started/meta.json @@ -0,0 +1,3 @@ +{ + "pages": ["what-is-aci", "core-concepts", "quickstart"] +} diff --git a/content/docs/agents/get-started/quickstart.mdx b/content/docs/agents/get-started/quickstart.mdx new file mode 100644 index 000000000..3853745bd --- /dev/null +++ b/content/docs/agents/get-started/quickstart.mdx @@ -0,0 +1,180 @@ +--- +title: "Quickstart" +pageTitle: 'Get started with Novu for Agents' +description: 'Create your first agent and connect it to Slack in under 10 minutes.' +icon: Zap +--- + +import { MousePointerClick, LayoutGrid, Signal, Brain, Plus } from 'lucide-react'; + +This guide walks you through agent creation, connecting it to Slack, sending your first message to the agent and connecting the agent to your code. + +By the end, you'll have a working agent that receives messages from Slack and replies based on its configuration in your code. + +**Prerequisites:** + +To get the most out of this guide, you’ll need to: + +* A [Novu account](https://dashboard.novu.co). +* A Slack workspace where you have permission to install apps. +* Node.js installed on your machine. + + + + + +## Create your agent + +1. Go to the Novu dashboard. +2. In the sidebar, click **Agents**. +3. Click **Create agent**. +4. Fill in the required fields: + * **Agent name**: The display name for your agent. + * **Identifier**: A unique slug used in code. You cannot change it after creation. + * **Description**: Optional. +5. Click **Setup agent**. + + ![Add agent](/images/agents/quickstart/add-agent.png) + +After you create the agent, Novu opens the guided setup page. + + + + + +## Select a provider + +1. In the setup page, open the **Select provider** dropdown. +2. Select **Slack**. + + ![Select provider](/images/agents/quickstart/select-provider.png) + +The dashboard detects the selected provider and unlocks the Slack setup steps. + + + + + +## Create a Slack app + +Novu can create the Slack app for you automatically using a Slack App Configuration Token. You'll generate the token once, paste it into Novu, and Novu handles the rest. + +1. In the setup step, click **Slack App Configuration Token**. This will open the [Slack API apps](https://api.slack.com/apps) page. +2. Under **Your App Configuration Tokens**, click **Generate Token**. + ![Select provider](/images/agents/quickstart/generate-slack-token.png) +3. Select the Slack workspace where you want the agent to live. +4. Click **Generate**. +5. Copy the generated token. +6. Back in the Novu dashboard, paste the token into the input field and click **Create app**. Novu uses the token to create the Slack app in your workspace. + + + The configuration token is used once to create your Slack app and is then discarded. Novu doesn't store it. + + + + + + +## Install the app in your workspace + +After Novu creates the Slack app, install it to your workspace. This is the same flow your end users will follow when they install the agent. + +1. Click **Install agent**. + If the button uses the agent name, it may appear as **Install [agent name]**. +2. Slack opens a permissions review page. +3. Review the requested permissions. +4. Click **Allow**. + + ![Select provider](/images/agents/quickstart/install-app.png) + +Once installed, you'll receive a welcome message from the agent. The provider connection is now complete. + + + + + +## Scaffold your agent project + +With the provider connected, the dashboard shows the next phase: connecting your code. Novu provides a pre-filled CLI command that creates a starter project with your agent identifier and secret key already configured. + +1. Copy the scaffold command from the dashboard. + +```bash +npx novu@latest init -t agent \ + --secret-key= \ + --api-url= +``` +2. Open your terminal. +3. Go to the directory where you want to create the project and paste the command. +4. Run the copied command. + +The CLI creates a new project with the bridge endpoint preconfigured. + + + + + +## Start your agent locally + +Run the dev command below in your project directory. This starts your app, opens a dev tunnel, and registers the bridge URL with Novu so messages can reach your local handler. + +```bash +npm run dev:novu +``` + +The dashboard polls for the bridge connection. Once it confirms the connection, you'll see another message in Slack from your agent + + + + +## Send a message + +Open the Slack channel where your app is installed and send a message to your bot. Your `onMessage` handler fires, and the reply appears in the thread. + + ![Slack message](/images/agents/quickstart/slack-message.png) + +Edit the files in `app/novu/agents/` to customize how your agent responds. See [Handle Events](/agents/setup-your-agent/connect-your-code/handle-events) for the full list of events your agent can respond to. + + + +## Next steps + +You have a working agent connected to Slack. Here's where to go from here: + + + } + href="/agents/agent-bridge/event-handlers" + title="Event handlers" + > + Respond to actions, reactions, and resolution events beyond messages. + + } + href="/agents/agent-bridge/reply-types-and-cards" + title="Reply types & cards" + > + Send markdown, file attachments, and interactive cards with buttons and dropdowns. + + } + href="/agents/agent-bridge/signals" + title="Signals" + > + Store metadata, trigger Novu workflows, and resolve conversations. + + } + href="/agents/agent-logic/overview" + title="Agent logic" + > + Connect Vercel AI SDK, LangChain, or OpenAI SDK as your agent's brain. + + } + href="/agents/providers" + title="Add another provider" + > + Connect Microsoft Teams, WhatsApp, or Telegram, your agent code stays the same. + + diff --git a/content/docs/agents/get-started/what-is-aci.mdx b/content/docs/agents/get-started/what-is-aci.mdx new file mode 100644 index 000000000..78d36933e --- /dev/null +++ b/content/docs/agents/get-started/what-is-aci.mdx @@ -0,0 +1,121 @@ +--- +title: "What is ACI?" +pageTitle: 'What is Agent Communication Infrastructure (ACI)?' +description: 'Learn what ACI is, what it solves, and how it helps agents communicate across channels.' +icon: CircleHelp +--- + +import { BookText, HomeIcon } from 'lucide-react'; + +Agent Communication Infrastructure (ACI) is a protocol-driven infrastructure layer for enabling structured, stateful communication between autonomous agents and your users across messaging platforms. + +ACI sits between messaging platforms and your agent logic. Instead of building separate integrations for each channel, ACI provides a unified interface for webhooks, message delivery, conversation state, and subscriber identity. + +The agent itself can be powered by an LLM, custom code, a rules engine, a human-in-the-loop workflow, or a combination of systems. ACI doesn't define the agent’s intelligence. It defines the communication infrastructure around it. + +## What does ACI solve? + +Software is becoming more conversational. Users no longer only click through interfaces or receive one-way notifications. They ask questions, reply to messages, clarify requests, approve actions, send files, react with emojis, and expect software to continue the conversation in channels where they already are. + +At the same time, agents are becoming more capable. And most teams already have agents that answers users questions, analyze information, trigger workflows, escalate issues, and coordinate with other systems. + +In most cases, these agents only work inside one platform, getting these agents into a different platform requires rebuilding the communication layer from scratch. + +This creates an infrastructure problem where each channel has its own webhook format, identity model, threading behavior, permissions, message formatting, interaction patterns, and delivery constraints. Teams that want to expose an agent across Slack, Microsoft Teams, WhatsApp, email, or other channels often end up rebuilding the same communication plumbing for every channel. + +ACI exists to standardize that layer. It separates the communication layer from the agent logic, so teams can connect agents to different messaging platforms without hand-building every channel integration from scratch. + +You build your agent once. ACI handles delivery everywhere. The goal isn't only to send messages but to let agents hold useful conversations across channels while preserving context, control, and visibility. + +## How ACI works + +ACI introduces a three-part architecture that separates platform delivery from agent intelligence. + +![architecture](/images/agents/concept/architecture.png) + +### Communication channels + +Communication channels are the places where users interact with the agent, such as Slack, Microsoft Teams, WhatsApp, Telegram, and more. When a user sends a message on any of these platforms, it enters the ACI layer. + +### Bridge + +The bridge is the infrastructure layer in the middle. It receives the platform webhook, normalizes the message into a standard format, and resolves the user's identity. + +It creates or loads the conversation with its full history, and forwards everything to your server as a single context object. + +When your agent replies, the bridge delivers the response back to the correct platform thread, persists it in the conversation history, and records the activity. Your server never talks to the platform directly. + + +### Agent brain + +The agent brain is your code. Your server receives the context object and processes it however you decide. Call an LLM, run business logic, route to a human, or combine all three. + +The brain is entirely yours. The agent can be an AI, a human, a human assisted by AI, or anything else. There's no limitation on what the brain can be. + +The channel-agnostic nature of this architecture means that when you connect a new provider, your agent implementation doesn't change. Not a single line of code. The same brain that responds on Slack will respond on Microsoft Teams, WhatsApp, or email without modification. + +## What ACI handles vs. what you control + +ACI draws a clear boundary between infrastructure and intelligence. + +### The infrastructure handles + +* Webhook ingestion and message normalization across all connected platforms. +* Message delivery to the correct platform thread. +* Conversation persistence and state management. +* Subscriber identity resolution, matching platform users to a unified identity. +* Typing indicators and platform-specific formatting. +* Conversation context and history, passed to your agent on every message. + +### You control + +* Your LLM, prompts, and model configuration. +* Your tools and function calls. +* Your business logic and decision-making. +* Your API keys and credentials. +* Your choice of runtime, which can be managed agents, Vercel AI SDK, LangChain, OpenAI SDK, or any custom code of your choosing. + +ACI is opinionated about infrastructure and unopinionated about intelligence. It handles the delivery problem so you can focus on the capability problem. + +## Common use cases + +ACI is built for teams that have an agent or are building one and need to connect it to the messaging platforms where their users already are. + +Common use cases include: + +* Support agents that answer questions from Slack, Microsoft Teams, WhatsApp, or email. +* Internal operations agents that triage requests and route work. +* Conversational agents that collect information from users and continue the conversation across turns. +* File-processing agents that receive attachments and respond with analysis or follow-up actions. +* Human-in-the-loop agents that escalate complex conversations to a person. +* Workflow agents that trigger notifications, approvals, summaries, or follow-ups from within a conversation. +* Multi-channel agents that need one communication interface across several providers. + +## Start building + + + } + > + Create your first agent and connect it to Slack in under 10 minutes. + + } title="Connect your providers">Learn how to connect any provider of your choice. + } href="/" title="Connect your code"> + Learn how to connect your to the Novu + + } href="/" title="Add agent logic"> + Learn how to connect your agent brain to the agent communication layer + + + +## Learn more + + + } href="/" title="Understand concepts"> + Learn more about the core concepts of Novu Agents. + + + + diff --git a/content/docs/agents/index.mdx b/content/docs/agents/index.mdx new file mode 100644 index 000000000..81f240627 --- /dev/null +++ b/content/docs/agents/index.mdx @@ -0,0 +1,5 @@ +--- +title: "Overview" +pageTitle: 'Novu Agents Overview' +description: 'Explore Novu Agents: conversational agents built on Novu.' +--- \ No newline at end of file diff --git a/content/docs/agents/manage-conversations.mdx b/content/docs/agents/manage-conversations.mdx new file mode 100644 index 000000000..fa5682dc1 --- /dev/null +++ b/content/docs/agents/manage-conversations.mdx @@ -0,0 +1,44 @@ +--- +title: "Agent conversations" +pageTitle: 'Manage agents conversations' +description: 'How to manage agent conversations, their history, lifecycle and observability.' +--- + +Novu provides full observability for agent conversations. You can view and manage conversations in the Novu dashboard. To access the conversations, go to the Activity Feed page on the dashboard and switch to the Agent Conversations tab. + +## Conversation lifecycle + +An agent conversation follows a simple state machine: + +![Agent conversation lifecycle](/images/agents/manage-conversations/agent-lifecycle.png) + +- **Active** — The conversation is ongoing. Messages increment the count, metadata can be updated, and `lastActivityAt` is refreshed. +- **Resolved** — The agent called `ctx.resolve()`. The `onResolve` handler fires, and an optional summary is stored. If the user sends another message, then the conversation automatically reopens. + +## Conversation observability + +Every conversation is fully observable in the Novu dashboard. Navigate to your agent's detail page to see all conversations, their status, and a complete timeline of activity. + +The dashboard shows: + +- **Conversation list** — All conversations for the agent, with status (active / resolved), last message preview, message count, and last activity timestamp. +- **Full message history** — Every message exchanged between the user and the agent, including rich content (cards, markdown, files). +- **Metadata** — All key-value pairs set via `ctx.metadata.set()`, visible per conversation. +- **Signal activity** — A log of all signals fired (metadata changes, workflow triggers, resolve events). +- **Participant details** — Which subscribers and platform users are part of each conversation, with identity resolution status. +- **Platform context** — Which platform and thread the conversation is happening on. + +![Manage agent conversations list](/images/agents/manage-conversations/manage-agent-conversations.gif) + +## Supported platforms + +| Platform | Status | Notes | +| --- | --- | --- | +| Slack | Available | Text, markdown, files, interactive cards, reactions, typing indicators. File uploads require the files:write bot scope. | +| Microsoft Teams | Available | Text, markdown, files, and cards via Bot Framework. Dashboard includes a guided onboarding flow with one-click app package download. | +| WhatsApp | Available | Text, markdown, interactive buttons. Inbound media is surfaced via attachments; outbound file attachments are not supported yet. | +| Email | Available | Text, markdown, files, interactive cards, emoji reactions (Gmail only). | + + +Platform capabilities vary. For example, typing indicators are only shown on platforms that support them, and message editing depends on platform adapter support. + \ No newline at end of file diff --git a/content/docs/agents/meta.json b/content/docs/agents/meta.json new file mode 100644 index 000000000..277efd266 --- /dev/null +++ b/content/docs/agents/meta.json @@ -0,0 +1,16 @@ +{ + "root": true, + "title": "Agents", + "description": "Agents", + "pages": [ + "---Get Started---", + "get-started/what-is-aci", + "get-started/core-concepts", + "get-started/quickstart", + "setup-your-agent", + "manage-conversations", + "deploy-your-agent", + "add-connect-components", + "receive-inbound-email" + ] +} diff --git a/content/docs/agents/receive-inbound-email.mdx b/content/docs/agents/receive-inbound-email.mdx new file mode 100644 index 000000000..60510cb08 --- /dev/null +++ b/content/docs/agents/receive-inbound-email.mdx @@ -0,0 +1,5 @@ +--- +title: "Receive Inbound Email" +pageTitle: 'Novu Agents Inbound Email' +description: 'Receive and process inbound email with your Novu Agent.' +--- diff --git a/content/docs/agents/setup-your-agent/connect-providers.mdx b/content/docs/agents/setup-your-agent/connect-providers.mdx new file mode 100644 index 000000000..e45e5b5c5 --- /dev/null +++ b/content/docs/agents/setup-your-agent/connect-providers.mdx @@ -0,0 +1,7 @@ +--- +title: "Connect Providers" +pageTitle: 'Novu Agents Connect Providers' +description: 'Connect channels and third-party providers to your Novu Agent.' +--- + +Once an agent is created, providers can be connected to the agent. This page guides you through how to connect your first provider to the agent and then later extend the agent to connect more providers. diff --git a/content/docs/agents/setup-your-agent/connect-your-code/handle-events.mdx b/content/docs/agents/setup-your-agent/connect-your-code/handle-events.mdx new file mode 100644 index 000000000..0aa7bc3c6 --- /dev/null +++ b/content/docs/agents/setup-your-agent/connect-your-code/handle-events.mdx @@ -0,0 +1,134 @@ +--- +title: "Handle Events" +pageTitle: 'Novu Agents Handle Events' +description: 'Handle agent events in your webhooks, handlers, and backend services.' +--- + +Agents receive events as users interact in a conversation. This page explains the event flow and shows how to handle each event type in your code. + +## Agent conversation flow + +Agent conversation starts when a user sends a message in a conversation with your agent. This message is then processed by the agent and the agent replies to the user. This conversation is then stored in the Novu platform and can be accessed later. Below is the flow of how a conversation works with your agent: + +![Agent Conversation Flow](/images/agents/setup-your-agent/handle-events/agent-conversation-flow.png) + +## Agent events + +A conversational agent responds to four types of events: + +| Event | When it fires | Common use case | +| --- | --- | --- | +| `onMessage` | User sends a text message in the thread | Process the message with your LLM and reply | +| `onAction` | User clicks a button or selects a value in an interactive card | Handle form submissions, button clicks, dropdown selections | +| `onReaction` | User adds or removes an emoji reaction on a message | Capture feedback (thumbs up/down), trigger follow-ups | +| `onResolve` | The conversation is marked as resolved | Clean up, log analytics, send a summary email | + +### onMessage + +`onMessage` is the primary handler event for the agent. It fires every time a user sends a message in a conversation with your agent. + +```typescript +import { agent } from '@novu/agent'; + +export const myAgent = agent("my-agent", { + onMessage: async (ctx) => { + const userMessage = ctx.message?.text ?? ''; + const conversationHistory = ctx.history; + const subscriber = ctx.subscriber; + + const response = await yourLLM.chat(userMessage, conversationHistory); + + ctx.metadata.set('lastIntent', response.intent); + await ctx.reply(response.text); + }, +}); +``` + +#### Handling attachments + +Inbound messages to the agent can include file attachments (images, documents, audio, video) when the platform supports them. Novu normalizes provider files into ctx.message.attachments with downloadable signed URLs: + +```typescript +import { agent } from '@novu/agent'; + +export const myAgent = agent("my-agent", { + onMessage: async (ctx) => { + const attachments = ctx.message?.attachments ?? []; + + for (const file of attachments) { + // file.type — 'image', 'document', 'audio', 'video' + // file.url — short-lived download URL + // file.name — original filename + // file.mimeType — e.g. 'image/jpeg' + // file.size — size in bytes + } + } +}); +``` + + + Download attachment URLs promptly inside your handler. These signed links are valid for 15 minutes. If a link expires before your agent reads it, then re-request the file through a subsequent event or conversation history where available. Inbound attachments are limited to 25 MB per file. + + +### onAction + +`onAction` event is fired when a user clicks a button or selects a value in an interactive card. For interactive cards, refer to [Interactive cards](/agents/setup-your-agent/connect-your-code/reply-types#interactive-cards). This event is used to handle form submissions, button clicks, dropdown selections, and more. + +```typescript +import { agent } from '@novu/agent'; + +export const myAgent = agent("my-agent", { + onAction: async (ctx) => { + const { actionId, value } = ctx.action!; + + // Handle the action based on the actionId + if (actionId === 'approve' && value === 'true') { + // Reply to the user + await ctx.reply('Request approved!'); + + // Trigger the approval workflow + ctx.trigger('approval-workflow', { + to: ctx.subscriber?.subscriberId, + payload: { approved: true }, + }); + } + } +}); +``` + +### onReaction + +`onReaction` event is fired when a user adds or removes an emoji reaction on a message. This event is used to capture feedback (thumbs up/down), trigger follow-ups, and more. + +```typescript +import { agent } from '@novu/agent'; + +export const myAgent = agent("my-agent", { + onReaction: async (ctx) => { + const { emoji, added, message } = ctx.reaction!; + + if (emoji.name === 'thumbs_up' && added) { + ctx.metadata.set('userSatisfied', true); + } else if (emoji.name === 'thumbs_down' && added) { + ctx.metadata.set('userUnsatisfied', true); + } + await ctx.reply('Thank you for your feedback!'); + }, +}); +``` + +### onResolve + +`onResolve` event is fired when the conversation is marked as resolved via `ctx.resolve()` or the resolve signal is triggered. This event is used to clean up, log analytics, send a summary email, and more. + +```typescript +import { agent } from '@novu/agent'; + +export const myAgent = agent("my-agent", { + onResolve: async (ctx) => { + // Clean up the conversation + ctx.metadata.set('resolvedAt', new Date().toISOString()); + await ctx.reply('Conversation resolved!'); + }, +}); +``` \ No newline at end of file diff --git a/content/docs/agents/setup-your-agent/connect-your-code/index.mdx b/content/docs/agents/setup-your-agent/connect-your-code/index.mdx new file mode 100644 index 000000000..f3191b47a --- /dev/null +++ b/content/docs/agents/setup-your-agent/connect-your-code/index.mdx @@ -0,0 +1,5 @@ +--- +title: "Connect Your Code" +pageTitle: 'Novu Agents Connect Your Code' +description: 'Integrate your codebase with Novu Agents using events, replies, and signals.' +--- diff --git a/content/docs/agents/setup-your-agent/connect-your-code/meta.json b/content/docs/agents/setup-your-agent/connect-your-code/meta.json new file mode 100644 index 000000000..2780a4986 --- /dev/null +++ b/content/docs/agents/setup-your-agent/connect-your-code/meta.json @@ -0,0 +1,6 @@ +{ + "title": "Connect Your Code", + "description": "Connect Your Code", + "defaultOpen": true, + "pages": ["handle-events", "reply-types", "use-signals"] +} diff --git a/content/docs/agents/setup-your-agent/connect-your-code/reply-types.mdx b/content/docs/agents/setup-your-agent/connect-your-code/reply-types.mdx new file mode 100644 index 000000000..c75831ac6 --- /dev/null +++ b/content/docs/agents/setup-your-agent/connect-your-code/reply-types.mdx @@ -0,0 +1,148 @@ +--- +title: "Reply Types" +pageTitle: 'Novu Agents Reply Types' +description: 'Choose and send the right reply types from your agent handlers.' +--- + +The public reply API is `ctx.reply(content, options?)`. The content argument must be either a plain text string, markdown value or a `ChatElement` card. File attachments are passed in the optional second argument. + +## Plain text + +The most basic reply is a plain text string. + +```typescript +await ctx.reply('Hello! How can I help?'); +``` + +## Markdown + +Markdown is supported for rich text replies. Use markdown to format your replies with headings, lists, links, and other rich text elements. + +```typescript +await ctx.reply('**Report generated.** See the attached PDF.'); +``` + +## Sending attachments + +You can include files in your markdown or plain text replies. The files are displayed as inline attachments in the reply. + + + Attachment files are limited to 25 MB per file. Files are only supported with string/markdown replies, not card replies. Provide each file with exactly one of url or data properties. + +File reference type: + +```tsx +type FileRef = { + filename: string; + mimeType?: string; + data?: string | Uint8Array | ArrayBuffer | Blob; + url?: string; +}; +``` + +Use `url` for files larger than a few megabytes. Novu fetches public HTTP(S) URLs server-side, and uploads the resulting file to the provider. Use `data` for small generated files that your agent already has in memory. +```tsx +// Public URL: recommended for larger files +await ctx.reply('Here is your report.', { + files: [{ filename: 'report.pdf', mimeType: 'application/pdf', url: reportUrl }], +}); + +// Inline binary data: useful for generated files +const csv = new TextEncoder().encode('name,total\nNovu,42'); +await ctx.reply('CSV generated.', { + files: [{ filename: 'report.csv', mimeType: 'text/csv', data: csv }], +}); + +// Node Buffer also works because Buffer extends Uint8Array +const screenshot = await page.screenshot(); +await ctx.reply('Screenshot attached.', { + files: [{ filename: 'screenshot.png', mimeType: 'image/png', data: screenshot }], +}); +``` + +Inline `data` can also be a base64 string, but passing binary values such as `Uint8Array`, `ArrayBuffer`, `Blob`, or a Node `Buffer` is usually easier when working with AI SDKs, PDF generators, screenshots, audio, or other generated files. + +## Interactive cards + +Cards are rich, structured messages with buttons, dropdowns, links, and more. They can be built using *function calls* or *JSX*. + +### Function call API + +Use this API when building card structures as function calls. + +```tsx +import { + Card, Button, CardText, Actions, + Select, SelectOption, Divider, CardLink, +} from '@novu/framework'; + +await ctx.reply(Card({ title: 'Order #1234', children: [ + CardText('Your order is ready for pickup.'), + Divider(), + Actions([ + Button({ id: 'ack', label: 'Acknowledge' }), + Button({ id: 'escalate', label: 'Escalate', style: 'danger' }), + ]), + CardLink({ url: 'https://example.com/order/1234', children: 'View details' }), +] })); +``` + +### JSX API + +Use this API when you prefer JSX syntax. Configure `tsconfig.json` with `"jsxImportSource": "@novu/framework"`. + +```tsx +import { + Card, Button, CardText, Actions, + Select, SelectOption, Divider, CardLink, +} from '@novu/framework'; + +await ctx.reply( + + Your order is ready for pickup. + + +