feat(frontend): Change safety popup to per-agent instead of global

Changed AI_AGENT_SAFETY_POPUP_SHOWN from a boolean flag to an array of
agent IDs. This ensures users see the safety popup once per unique agent
instead of once globally.

Why this is better:
- Different agents have different capabilities (sensitive actions, HITL blocks)
- User should be aware of what THIS specific agent can do
- Not too annoying since it's still only once per agent, not every run
- Better safety awareness when switching between safe and risky agents

Changes:
- Store array of seen agent IDs in localStorage instead of single boolean
- Pass agentId to useAIAgentSafetyPopup hook and AIAgentSafetyPopup component
- Check if current agent ID is in the seen list before showing popup
- Add agent ID to list when user acknowledges popup

Testing:
- Clear localStorage or remove specific agent ID from array to re-trigger popup
- Each unique agent shows popup on first run only
This commit is contained in:
Zamil Majdy
2026-01-22 18:13:33 -05:00
parent 3b58684abc
commit e4c3f9995b
2 changed files with 23 additions and 8 deletions

View File

@@ -94,6 +94,7 @@ export function RunAgentModal({
const contentRef = useRef<HTMLDivElement>(null);
const { shouldShowPopup, dismissPopup } = useAIAgentSafetyPopup(
agent.id,
agent.has_sensitive_action,
agent.has_human_in_the_loop,
);
@@ -299,6 +300,7 @@ export function RunAgentModal({
</Dialog>
<AIAgentSafetyPopup
agentId={agent.id}
isOpen={isSafetyPopupOpen}
onAcknowledge={handleSafetyPopupAcknowledge}
/>

View File

@@ -8,14 +8,24 @@ import { ShieldCheckIcon } from "@phosphor-icons/react";
import { useCallback, useEffect, useState } from "react";
interface Props {
agentId: string;
onAcknowledge: () => void;
isOpen: boolean;
}
export function AIAgentSafetyPopup({ onAcknowledge, isOpen }: Props) {
export function AIAgentSafetyPopup({ agentId, onAcknowledge, isOpen }: Props) {
function handleAcknowledge() {
// Mark popup as shown so it won't appear again
storage.set(Key.AI_AGENT_SAFETY_POPUP_SHOWN, "true");
// Add this agent to the list of agents for which popup has been shown
const seenAgentsJson = storage.get(Key.AI_AGENT_SAFETY_POPUP_SHOWN);
const seenAgents: string[] = seenAgentsJson
? JSON.parse(seenAgentsJson)
: [];
if (!seenAgents.includes(agentId)) {
seenAgents.push(agentId);
storage.set(Key.AI_AGENT_SAFETY_POPUP_SHOWN, JSON.stringify(seenAgents));
}
onAcknowledge();
}
@@ -66,6 +76,7 @@ export function AIAgentSafetyPopup({ onAcknowledge, isOpen }: Props) {
}
export function useAIAgentSafetyPopup(
agentId: string,
hasSensitiveAction: boolean,
hasHumanInTheLoop: boolean,
) {
@@ -73,16 +84,18 @@ export function useAIAgentSafetyPopup(
const [hasChecked, setHasChecked] = useState(false);
useEffect(() => {
// Only check once after mount (to avoid SSR issues)
if (hasChecked) return;
const hasSeenPopup =
storage.get(Key.AI_AGENT_SAFETY_POPUP_SHOWN) === "true";
const seenAgentsJson = storage.get(Key.AI_AGENT_SAFETY_POPUP_SHOWN);
const seenAgents: string[] = seenAgentsJson
? JSON.parse(seenAgentsJson)
: [];
const hasSeenPopupForThisAgent = seenAgents.includes(agentId);
const isRelevantAgent = hasSensitiveAction || hasHumanInTheLoop;
setShouldShowPopup(!hasSeenPopup && isRelevantAgent);
setShouldShowPopup(!hasSeenPopupForThisAgent && isRelevantAgent);
setHasChecked(true);
}, [hasSensitiveAction, hasHumanInTheLoop, hasChecked]);
}, [agentId, hasSensitiveAction, hasHumanInTheLoop, hasChecked]);
const dismissPopup = useCallback(() => {
setShouldShowPopup(false);