---
name: iblai-vibe-agent-safety
description: Add the agent Safety tab (the moderation check on user messages, the safety check on agent replies, the reply each one sends, and a host-provided list of flagged prompts) to your Next.js app. Use when the user mentions moderation, safety prompts, guardrails, inappropriate content, blocked messages, or flagged prompts. For the REST contract and the moderation logs see /iblai-api-agent-safety.
globs:
alwaysApply: false
metadata:
kind: ui
---
# /iblai-vibe-agent-safety
Add the agent **Safety tab** -- the two guardrails on an agent's chats.
**Scope: per agent.** The **Moderation Prompt** checks each user message
before the agent sees it; the **Safety Prompt** checks each reply before the
user sees it. Each has an Active switch, and each has a response sent
instead when it trips. This is one tab in the agent-settings family indexed
by `/iblai-vibe-agent`; it shares the `AgentSettingsProvider` wrapper with
every other tab.
**Safety** -- four cards (Moderation Prompt, Safety Prompt, Moderation
Response, Safety Response), each with **Edit** and **Copy**; the two prompt
cards have the Active switch. **View Flagged Prompts** shows only when you
pass a `FlaggedPromptsModal` and `showFlaggedPrompts` is true (Step 2).

**Edit** -- a rich-text editor for the prompt or response; **Save** writes
it to the agent.

**Flagged prompts** -- the modal your app supplies. This one is the
minimal modal from Step 2: the user messages the Moderation Prompt stopped,
with who sent each, when, the message, and the reason.

> **Common setup (brand, conventions, env files, verification):** see [docs/skill-setup.md](https://raw.githubusercontent.com/iblai/vibe/refs/heads/main/docs/skill-setup.md).
## Prerequisites
- Auth set up (`/iblai-vibe-auth`, or vibe-starter).
- `AgentSettingsProvider` wraps the route (`/iblai-vibe-agent` §1).
- `@iblai/iblai-js@^2.62.1` (resolves `@iblai/web-containers` 1.62.1). Check with
`pnpm why @iblai/web-containers`.
- A real agent UUID. Ask the user; never invent one.
- To list flagged prompts, the user needs the agent's
`#view_moderation_logs` grant (org admins have it).
## Step 1: Check Environment
Look for `iblai.env` in the project root with `PLATFORM`, `DOMAIN`, and
`TOKEN`. If it is missing, tell the user:
"You need an `iblai.env` with your platform configuration. Download the
template and fill in your values:
`curl -o iblai.env https://raw.githubusercontent.com/iblai/vibe/refs/heads/main/iblai.env`"
## Step 2: Mount `AgentSafetyTab`
The four cards need nothing but the provider: ``. The
flagged-prompts list is not part of the SDK tab -- the host injects it.
These are the pieces a full mount wires:
- `FlaggedPromptsModal` -- your modal; it gets `isOpen`, `onClose`,
`mentorId`, `tenantKey`, `username`.
- `showFlaggedPrompts` -- whether to show the button. Gate it on the
`/mentors//#view_moderation_logs` grant.
- `renderPromptContent` -- how the cards render prompt text (plain text by
default, so line breaks and Markdown are lost). Pass a Markdown renderer
to keep them.
- `executeGatedAction` on the provider -- wraps the two Active switches,
for a paywall or free-trial check. Prompt edits are not gated.
A minimal flagged-prompts modal, built on the starter's `Dialog`:
```tsx
// app/(app)/agents/[mentorId]/safety/flagged-prompts-modal.tsx
"use client";
import { useGetModerationLogsQuery } from "@iblai/iblai-js/data-layer";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
type FlaggedPromptsModalProps = {
isOpen: boolean;
onClose: () => void;
mentorId: string;
tenantKey: string;
username: string;
};
type ModerationLog = {
id: number;
username: string;
email: string | null;
prompt: string;
reason: string;
target_system: string;
date_created: string;
};
export function FlaggedPromptsModal({
isOpen,
onClose,
mentorId,
tenantKey,
username,
}: FlaggedPromptsModalProps) {
const { data, isLoading } = useGetModerationLogsQuery(
{
org: tenantKey,
// @ts-expect-error userId (the path's user) is read at runtime but missing from the type
userId: username,
mentor: mentorId,
page: 1,
pageSize: 20,
ordering: "-date_created",
},
{ skip: !isOpen },
);
const logs = (data?.results ?? []) as unknown as ModerationLog[];
return (
);
}
```
`userId` is the signed-in user (the path segment). SDK 2.36.2 types the
hook's argument without it, but the request needs it -- hence the
`@ts-expect-error`; `username` in that type filters to the flagged user
instead. `mentor` filters to this agent. Add search, a date range and
paging the same way. This list holds Moderation System rows only: replies
the Safety Prompt blocked are served by `…/safety-logs/` (same query and row
shape), which has no data-layer hook in SDK 2.36.2 -- call it from a server
route (`/iblai-api-agent-safety`).
The page loads the agent's grants, gates the button, and passes the
paywall gate through a nested provider:
```tsx
// app/(app)/agents/[mentorId]/safety/page.tsx
"use client";
import { useEffect, useState } from "react";
import {
AgentSafetyTab,
AgentSettingsProvider,
useAgentSettings,
} from "@iblai/iblai-js/web-containers/next";
import {
useGetMentorSettingsQuery,
useGetRbacPermissionsMutation,
} from "@iblai/iblai-js/data-layer";
import { checkRbacPermission } from "@iblai/iblai-js/web-utils";
import { FlaggedPromptsModal } from "./flagged-prompts-modal";
// Module scope: the provider memoizes on this function, so an inline arrow
// would rebuild the context on every render.
const executeGatedAction = (action: () => unknown) => action();
export default function AgentSafetyPage() {
const settings = useAgentSettings();
const { tenantKey, mentorId, username } = settings;
const [grants, setGrants] = useState