44 lines
1.8 KiB
TypeScript
44 lines
1.8 KiB
TypeScript
import { AlertCircle } from 'lucide-react';
|
|
import type { Message } from '@/api/types';
|
|
|
|
interface Props {
|
|
message: Message;
|
|
}
|
|
|
|
// v1.11.6: doom-loop sentinel. Renders the system row inserted by
|
|
// services/inference.ts insertDoomLoopSentinel when the model called the
|
|
// same tool with the same arguments threshold times in a row. Visual
|
|
// treatment mirrors CapHitSentinel (amber card + alert icon) so users learn
|
|
// "amber alert = the loop hit a guard rail and stopped" regardless of
|
|
// which guard fired. Intentionally NO Continue button — retrying with the
|
|
// same tools would just re-loop; the user needs to restate the prompt or
|
|
// switch agents instead.
|
|
export function DoomLoopSentinel({ message }: Props) {
|
|
const meta = message.metadata;
|
|
const isDoomLoop =
|
|
meta !== null && typeof meta === 'object' && meta.kind === 'doom_loop';
|
|
const toolName = isDoomLoop ? meta.tool_name : null;
|
|
const threshold = isDoomLoop ? meta.threshold : null;
|
|
|
|
return (
|
|
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 text-sm">
|
|
<div className="px-3 py-2 flex items-start gap-2">
|
|
<AlertCircle className="size-4 text-amber-500 shrink-0 mt-0.5" />
|
|
<div className="flex-1 min-w-0 space-y-1">
|
|
<div className="text-xs font-medium text-amber-700 dark:text-amber-300">
|
|
Doom loop detected
|
|
</div>
|
|
<div className="text-xs text-muted-foreground">
|
|
{toolName !== null && threshold !== null
|
|
? `Stopped after ${threshold} identical calls to ${toolName}. The model was looping.`
|
|
: message.content}
|
|
</div>
|
|
<div className="text-[11px] text-muted-foreground/80">
|
|
Send a new message with a different angle, or switch agents.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|