spine-framework-cortex 0.2.21 → 0.2.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { useEffect, useState } from 'react'
1
+ import { useEffect, useRef, useState } from 'react'
2
2
  import { useParams, useNavigate } from 'react-router-dom'
3
3
  import { apiFetch } from '@core/lib/api'
4
4
  import { Button } from '@core/components/ui/button'
@@ -7,7 +7,8 @@ import { Badge } from '@core/components/ui/badge'
7
7
  import { Skeleton } from '@core/components/ui/skeleton'
8
8
  import { ScrollArea } from '@core/components/ui/scroll-area'
9
9
  import { Tabs, TabsList, TabsTrigger, TabsContent } from '@core/components/ui/tabs'
10
- import { ArrowLeft, Send, Lock, Globe, Bot, CheckCircle, AlertCircle, FileText, Building2, CreditCard, Package, BookOpen, Eye, EyeOff, Brain, Clock, TrendingUp, Tag as TagIcon } from 'lucide-react'
10
+ import { Textarea } from '@core/components/ui/textarea'
11
+ import { ArrowLeft, Send, Lock, Globe, Bot, CheckCircle, AlertCircle, FileText, Building2, CreditCard, Package, BookOpen, Eye, EyeOff, Brain, Clock, TrendingUp, Tag as TagIcon, Sparkles } from 'lucide-react'
11
12
  import { useAuth } from '@core/contexts/AuthContext'
12
13
 
13
14
  interface Ticket {
@@ -367,145 +368,143 @@ function CaseDataPanel({ ticket }: { ticket: Ticket | null }) {
367
368
  )
368
369
  }
369
370
 
370
- // AI Metadata Panel
371
- function AIMetadataPanel({ ticket, onGenerateKB }: { ticket: Ticket | null; onGenerateKB?: () => void }) {
372
- if (!ticket?.data?.aim_confidence_threshold && !ticket?.data?.aim_confidence_at_response && !ticket?.data?.aim_escalation_reason) {
373
- return (
374
- <div className="h-full flex flex-col">
375
- <div className="px-4 py-2 border-b border-border bg-purple-50">
376
- <p className="text-xs font-medium uppercase tracking-wide text-purple-700">AI Analysis</p>
377
- </div>
378
- <div className="flex-1 p-4 flex items-center justify-center text-sm text-muted-foreground">
379
- No AI metadata available for this ticket.
380
- </div>
381
- </div>
382
- )
383
- }
371
+ // Solution AI Panel — internal AI collaborator for support agents
372
+ interface AIMessage { role: 'user' | 'ai'; content: string }
384
373
 
385
- const ai = ticket.data
386
- const isResolved = ticket.status === 'resolved' || ticket.status === 'closed'
374
+ function SolutionAIPanel({ ticket, onGenerateKB }: { ticket: Ticket | null; onGenerateKB?: () => void }) {
375
+ const [messages, setMessages] = useState<AIMessage[]>([])
376
+ const [input, setInput] = useState('')
377
+ const [sending, setSending] = useState(false)
378
+ const [threadId, setThreadId] = useState<string | null>(null)
379
+ const bottomRef = useRef<HTMLDivElement>(null)
380
+ const ai = ticket?.data
381
+ const isResolved = ticket?.status === 'resolved' || ticket?.status === 'closed'
382
+
383
+ // Auto-scroll to bottom when new messages arrive
384
+ useEffect(() => {
385
+ bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
386
+ }, [messages])
387
+
388
+ const sendMessage = async () => {
389
+ if (!input.trim() || !ticket?.id || sending) return
390
+ const userText = input.trim()
391
+ setInput('')
392
+ setMessages(prev => [...prev, { role: 'user', content: userText }])
393
+ setSending(true)
394
+ try {
395
+ const isFirst = !threadId
396
+ const res = await apiFetch(
397
+ `/.netlify/functions/custom_support-solution?action=${isFirst ? 'start' : 'message'}`,
398
+ {
399
+ method: 'POST',
400
+ headers: { 'Content-Type': 'application/json' },
401
+ body: JSON.stringify(isFirst
402
+ ? { ticket_id: ticket.id, message: userText }
403
+ : { thread_id: threadId, message: userText }
404
+ ),
405
+ }
406
+ ).then(r => r.json())
407
+
408
+ if (res.threadId && !threadId) setThreadId(res.threadId)
409
+ if (res.content) setMessages(prev => [...prev, { role: 'ai', content: res.content }])
410
+ } catch (err) {
411
+ setMessages(prev => [...prev, { role: 'ai', content: 'Error contacting Solution AI. Please try again.' }])
412
+ } finally {
413
+ setSending(false)
414
+ }
415
+ }
387
416
 
388
417
  return (
389
418
  <div className="h-full flex flex-col">
390
- <div className="px-4 py-2 border-b border-border bg-purple-50">
391
- <p className="text-xs font-medium uppercase tracking-wide text-purple-700">AI Analysis</p>
419
+ {/* Header */}
420
+ <div className="px-4 py-2 border-b border-border bg-purple-50 shrink-0">
421
+ <div className="flex items-center justify-between">
422
+ <div className="flex items-center gap-1.5">
423
+ <Sparkles className="h-3.5 w-3.5 text-purple-600" />
424
+ <p className="text-xs font-medium uppercase tracking-wide text-purple-700">Solution AI</p>
425
+ </div>
426
+ <p className="text-xs text-purple-500">Internal only</p>
427
+ </div>
392
428
  </div>
393
- <ScrollArea className="flex-1 p-4">
394
- <div className="space-y-4">
395
- {/* Confidence Score */}
429
+
430
+ {/* Triage metadata strip */}
431
+ {(ai?.aim_confidence_at_response || ai?.aim_escalation_reason) && (
432
+ <div className="px-3 py-1.5 border-b border-border bg-muted/40 shrink-0 flex items-center gap-3 text-xs text-muted-foreground">
396
433
  {ai.aim_confidence_at_response && (
397
- <div className="space-y-1">
398
- <div className="flex items-center justify-between text-sm">
399
- <span className="text-muted-foreground">Confidence</span>
400
- <span className={`font-mono font-medium ${ai.aim_confidence_at_response >= 0.75 ? 'text-green-600' : 'text-amber-600'}`}>
401
- {Math.round(ai.aim_confidence_at_response * 100)}%
402
- </span>
403
- </div>
404
- {ai.aim_confidence_threshold && (
405
- <p className="text-xs text-muted-foreground">Threshold: {Math.round(ai.aim_confidence_threshold * 100)}%</p>
406
- )}
407
- </div>
434
+ <span className={ai.aim_confidence_at_response >= 0.75 ? 'text-green-600' : 'text-amber-600'}>
435
+ Triage confidence: {Math.round(ai.aim_confidence_at_response * 100)}%
436
+ </span>
408
437
  )}
409
-
410
438
  {ai.aim_escalation_reason && ai.aim_escalation_reason !== 'none' && (
411
- <div className="flex items-start gap-2 p-2 bg-amber-50 rounded-lg">
412
- <AlertCircle className="h-4 w-4 text-amber-600 mt-0.5" />
413
- <div className="text-sm">
414
- <p className="font-medium text-amber-700">Escalated</p>
415
- <p className="text-amber-600 text-xs">{ai.aim_escalation_reason.replace('_', ' ')}</p>
416
- </div>
417
- </div>
418
- )}
419
-
420
- {/* Problem Statement */}
421
- {ai.aim_problem_statement && (
422
- <div className="space-y-1">
423
- <p className="text-xs font-medium text-muted-foreground uppercase">Problem</p>
424
- <p className="text-sm bg-muted p-2 rounded">{ai.aim_problem_statement}</p>
425
- </div>
439
+ <span className="text-amber-600 flex items-center gap-1">
440
+ <AlertCircle className="h-3 w-3" /> Escalated
441
+ </span>
426
442
  )}
427
-
428
- {/* Solution Path */}
429
- {ai.aim_solution_path && (
430
- <div className="space-y-1">
431
- <p className="text-xs font-medium text-muted-foreground uppercase">Solution</p>
432
- <p className="text-sm bg-muted p-2 rounded">{ai.aim_solution_path}</p>
433
- </div>
434
- )}
435
-
436
- {/* Tools Used */}
437
- {ai.aim_tools_used && ai.aim_tools_used.length > 0 && (
438
- <div className="space-y-1">
439
- <p className="text-xs font-medium text-muted-foreground uppercase">Tools Used</p>
440
- <div className="flex flex-wrap gap-1">
441
- {ai.aim_tools_used.map((tool, i) => (
442
- <Badge key={i} variant="outline" className="text-xs">{tool}</Badge>
443
- ))}
443
+ </div>
444
+ )}
445
+
446
+ {/* Chat history */}
447
+ <ScrollArea className="flex-1 px-3 py-2">
448
+ {messages.length === 0 ? (
449
+ <div className="flex flex-col items-center justify-center h-32 text-center text-muted-foreground text-xs gap-2">
450
+ <Bot className="h-6 w-6" />
451
+ <p>Ask Solution AI for help with this case.</p>
452
+ <p className="text-xs opacity-70">Searches KB · recalls similar cases · drafts replies</p>
453
+ </div>
454
+ ) : (
455
+ <div className="space-y-2">
456
+ {messages.map((msg, i) => (
457
+ <div key={i} className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}>
458
+ <div className={`max-w-[90%] rounded-lg px-3 py-2 text-xs whitespace-pre-wrap ${
459
+ msg.role === 'user'
460
+ ? 'bg-primary text-primary-foreground'
461
+ : 'bg-muted text-foreground'
462
+ }`}>
463
+ {msg.content}
464
+ </div>
444
465
  </div>
445
- </div>
446
- )}
447
-
448
- {/* Post-mortem Section */}
449
- {isResolved && (
450
- <>
451
- <Separator />
452
- <div className="space-y-3">
453
- <p className="text-xs font-medium text-muted-foreground uppercase">Post-mortem</p>
454
-
455
- {postmortem?.root_cause_category && (
456
- <p className="text-sm">
457
- <span className="text-muted-foreground">Root cause:</span>{' '}
458
- {postmortem.root_cause_category}
459
- </p>
460
- )}
461
-
462
- {postmortem?.resolution_time_minutes && (
463
- <p className="text-sm">
464
- <span className="text-muted-foreground">Resolution time:</span>{' '}
465
- {Math.round(postmortem.resolution_time_minutes)} min
466
- </p>
467
- )}
468
-
469
- {postmortem?.customer_satisfaction !== undefined && (
470
- <div className="flex items-center gap-2">
471
- <span className="text-sm text-muted-foreground">Satisfaction:</span>
472
- <div className="flex">
473
- {[1, 2, 3, 4, 5].map((star) => (
474
- <span
475
- key={star}
476
- className={`text-lg ${star <= (postmortem.customer_satisfaction || 0) ? 'text-amber-400' : 'text-gray-300'}`}
477
- >
478
-
479
- </span>
480
- ))}
481
- </div>
482
- </div>
483
- )}
484
-
485
- {/* KB Generation */}
486
- <div className="pt-2">
487
- {postmortem?.kb_generated ? (
488
- <div className="flex items-center gap-2 text-green-600 text-sm">
489
- <CheckCircle className="h-4 w-4" />
490
- KB article created
491
- </div>
492
- ) : (
493
- <Button
494
- variant="outline"
495
- size="sm"
496
- className="w-full gap-1 text-xs"
497
- onClick={onGenerateKB}
498
- >
499
- <BookOpen className="h-3 w-3" />
500
- Generate KB Article
501
- </Button>
502
- )}
466
+ ))}
467
+ {sending && (
468
+ <div className="flex justify-start">
469
+ <div className="bg-muted rounded-lg px-3 py-2 text-xs text-muted-foreground animate-pulse">
470
+ Thinking…
503
471
  </div>
504
472
  </div>
505
- </>
473
+ )}
474
+ <div ref={bottomRef} />
475
+ </div>
476
+ )}
477
+ </ScrollArea>
478
+
479
+ {/* KB generation (resolved only) */}
480
+ {isResolved && (
481
+ <div className="px-3 py-2 border-t border-border shrink-0">
482
+ {ai?.kb_proposed_kb_id ? (
483
+ <div className="flex items-center gap-2 text-green-600 text-xs">
484
+ <CheckCircle className="h-3.5 w-3.5" /> KB article created
485
+ </div>
486
+ ) : (
487
+ <Button variant="outline" size="sm" className="w-full gap-1 text-xs" onClick={onGenerateKB}>
488
+ <BookOpen className="h-3 w-3" /> Generate KB Article
489
+ </Button>
506
490
  )}
507
491
  </div>
508
- </ScrollArea>
492
+ )}
493
+
494
+ {/* Input */}
495
+ <div className="px-3 py-2 border-t border-border shrink-0 flex gap-2">
496
+ <Textarea
497
+ value={input}
498
+ onChange={e => setInput(e.target.value)}
499
+ onKeyDown={e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage() } }}
500
+ placeholder="Ask Solution AI…"
501
+ className="flex-1 text-xs resize-none min-h-[36px] max-h-[100px]"
502
+ rows={1}
503
+ />
504
+ <Button size="sm" onClick={sendMessage} disabled={!input.trim() || sending} className="shrink-0">
505
+ <Send className="h-3.5 w-3.5" />
506
+ </Button>
507
+ </div>
509
508
  </div>
510
509
  )
511
510
  }
@@ -838,7 +837,6 @@ export default function TicketDetailPage() {
838
837
  }
839
838
 
840
839
  const handleGenerateKB = () => {
841
- // Trigger KB generation flow - would navigate to redaction review
842
840
  navigate(`/cortex/support/${id}/kb-review`)
843
841
  }
844
842
 
@@ -910,7 +908,7 @@ export default function TicketDetailPage() {
910
908
  </TabsList>
911
909
  </Tabs>
912
910
  {activeSidePanel === 'case' && <CaseDataPanel ticket={ticket} />}
913
- {activeSidePanel === 'ai' && <AIMetadataPanel ticket={ticket} onGenerateKB={handleGenerateKB} />}
911
+ {activeSidePanel === 'ai' && <SolutionAIPanel ticket={ticket} onGenerateKB={handleGenerateKB} />}
914
912
  {activeSidePanel === 'analysis' && <CaseAnalysisPanel ticket={ticket} analysisLoading={analysisLoading} />}
915
913
  </div>
916
914
  </div>
@@ -0,0 +1,98 @@
1
+ [
2
+ {
3
+ "name": "Support Triage Agent",
4
+ "description": "First-contact AI for inbound support tickets. Searches KB, reasons over prior cases, responds with confidence scoring. Hands off to Solution AI or human when confidence is low.",
5
+ "agent_type": "support",
6
+ "model_config": {
7
+ "model": "gpt-4o",
8
+ "temperature": 0.4,
9
+ "max_tokens": 1024
10
+ },
11
+ "system_prompt": "You are a helpful and empathetic support agent for a software platform. Your goal is to resolve customer issues accurately and efficiently on the first response.\n\nGuidelines:\n- Be concise and clear. Customers want answers, not essays.\n- If you find a relevant KB article or prior case, reference it.\n- If you are not confident (below 0.75), say so honestly and let the customer know a human will follow up.\n- Never fabricate feature behaviour or make promises about the product.\n- Tone: professional but warm. Match the customer's level of formality.",
12
+ "tools": [],
13
+ "capabilities": ["rag_search", "confidence_scoring", "escalation"],
14
+ "constraints": {
15
+ "max_context_tokens": 8000,
16
+ "never_reveal": ["internal_notes", "agent_ids", "prompt_config_ids"]
17
+ },
18
+ "metadata": {
19
+ "default_prompt_config_slug": "support_triage",
20
+ "owned_by": "cortex"
21
+ },
22
+ "ownership": "platform",
23
+ "is_system": true,
24
+ "is_active": true
25
+ },
26
+ {
27
+ "name": "Solution AI Agent",
28
+ "description": "Internal AI collaborator for support agents working a case. Searches KB, queries prior resolved cases, suggests diagnostic steps and response drafts. Never visible to customers.",
29
+ "agent_type": "support",
30
+ "model_config": {
31
+ "model": "gpt-4o",
32
+ "temperature": 0.5,
33
+ "max_tokens": 2048
34
+ },
35
+ "system_prompt": "You are an expert technical support advisor assisting a human support agent working a customer case. Your audience is the support agent, not the customer.\n\nYou can:\n- Search the knowledge base and surface relevant articles\n- Recall similar resolved cases and how they were fixed\n- Suggest diagnostic steps based on the symptoms described\n- Draft a proposed customer-facing response for the agent to review\n- Flag automation potential if this issue could be auto-resolved in future\n\nBe direct and practical. The agent needs actionable guidance, not hedged generalities.\nFormat your output clearly: use headers if helpful, bullet points for steps.",
36
+ "tools": [],
37
+ "capabilities": ["rag_search", "case_history_search", "response_drafting"],
38
+ "constraints": {
39
+ "visibility": "internal_only",
40
+ "max_context_tokens": 12000
41
+ },
42
+ "metadata": {
43
+ "default_prompt_config_slug": "solution_ai",
44
+ "owned_by": "cortex"
45
+ },
46
+ "ownership": "platform",
47
+ "is_system": true,
48
+ "is_active": true
49
+ },
50
+ {
51
+ "name": "Case Resolution Analysis Agent",
52
+ "description": "Post-resolution analyst. Given a closed ticket and its full conversation (including internal AI collaboration thread), produces a structured postmortem: root cause, diagnostic path, solution, automation potential, and KB candidacy flag.",
53
+ "agent_type": "analysis",
54
+ "model_config": {
55
+ "model": "gpt-4o",
56
+ "temperature": 0.2,
57
+ "max_tokens": 2048
58
+ },
59
+ "system_prompt": "You are a case analysis specialist. When given a resolved support ticket — including the customer conversation, the internal support-agent collaboration thread, and any escalation notes — you produce a structured postmortem.\n\nYour output must be a JSON object with these exact fields:\n- reported_issue: what the customer described (their words)\n- true_problem: the actual root cause (may differ significantly from how it was reported)\n- diagnostic_steps: ordered array of steps taken to identify the root cause\n- solution_steps: ordered array of steps taken to resolve it\n- final_solution: one-sentence concise answer\n- customer_temperature: 'positive' | 'neutral' | 'frustrated' | 'escalated'\n- time_to_resolution: estimated minutes from first message to resolution\n- escalation_required: boolean\n- back_and_forth_count: integer — number of customer messages\n- sentiment_progression: array of sentiment labels across the conversation\n- automation_potential: 'high' | 'medium' | 'low' — could this be auto-resolved next time?\n- kb_candidate: boolean — would a KB article have prevented this ticket?\n- suggested_tags: array of tag objects { slug, name, purpose, category, applicable_to }\n- confidence_score: 0.0-1.0\n- analysis_summary: 2-3 sentence plain English summary for the ticket record",
60
+ "tools": [],
61
+ "capabilities": ["structured_output", "sentiment_analysis", "case_classification"],
62
+ "constraints": {
63
+ "output_format": "json",
64
+ "max_context_tokens": 16000
65
+ },
66
+ "metadata": {
67
+ "default_prompt_config_slug": "case_analysis_prompt",
68
+ "owned_by": "cortex"
69
+ },
70
+ "ownership": "platform",
71
+ "is_system": true,
72
+ "is_active": true
73
+ },
74
+ {
75
+ "name": "KB Redaction Agent",
76
+ "description": "Prepares resolved cases for publication as KB articles. Identifies PII, account-specific details, internal references, and confidential data. Produces redaction suggestions and a generalised, customer-facing rewrite.",
77
+ "agent_type": "content",
78
+ "model_config": {
79
+ "model": "gpt-4o",
80
+ "temperature": 0.1,
81
+ "max_tokens": 3000
82
+ },
83
+ "system_prompt": "You are a knowledge base editorial agent. You receive the raw text of a resolved support case and must prepare it for public publication as a KB article.\n\nYour tasks:\n1. IDENTIFY all text that should be redacted before publishing. Categories:\n - pii: names, emails, phone numbers, addresses, usernames\n - account_specific: account names, account IDs, organisation-specific data\n - confidential: internal pricing, unreleased features, internal process details\n - internal_reference: ticket IDs, internal tool names, employee names\n\n2. GENERALISE the solution: rewrite the content to be universally applicable, removing all account-specific context while preserving the diagnostic and solution logic.\n\nOutput must be a JSON object:\n{\n \"original_content\": \"<the input text unchanged>\",\n \"redacted_content\": \"<fully redacted and generalised version>\",\n \"suggestions\": [\n {\n \"id\": \"<uuid>\",\n \"start_index\": <int>,\n \"end_index\": <int>,\n \"original_text\": \"<exact text to redact>\",\n \"redacted_text\": \"<replacement text, e.g. [Account Name]>\",\n \"sensitivity_level\": \"high|medium|low\",\n \"reasoning\": \"<one sentence why>\",\n \"category\": \"pii|confidential|account_specific|internal_reference\"\n }\n ],\n \"confidence_score\": 0.0-1.0,\n \"processing_metadata\": { \"model_used\": \"<model>\", \"temperature\": 0.1, \"tokens_consumed\": <int> }\n}",
84
+ "tools": [],
85
+ "capabilities": ["structured_output", "redaction", "content_generalisation"],
86
+ "constraints": {
87
+ "output_format": "json",
88
+ "max_context_tokens": 12000
89
+ },
90
+ "metadata": {
91
+ "default_prompt_config_slug": "kb_generator",
92
+ "owned_by": "cortex"
93
+ },
94
+ "ownership": "platform",
95
+ "is_system": true,
96
+ "is_active": true
97
+ }
98
+ ]
@@ -0,0 +1,29 @@
1
+ [
2
+ {
3
+ "name": "support_escalation",
4
+ "description": "Fired by agent-runner when triage confidence falls below threshold. Marks the thread as pending human review and notifies the support team.",
5
+ "trigger_type": "event",
6
+ "config": {
7
+ "event": "agent.inference.low_confidence",
8
+ "scope": "account"
9
+ },
10
+ "stages": [
11
+ {
12
+ "stage_type": "send_notification",
13
+ "config": {
14
+ "message": "A support ticket has been escalated to human review — confidence too low for AI response.",
15
+ "entity_type": "threads",
16
+ "entity_id_field": "thread_id",
17
+ "recipients": ["role:support_admin", "role:support"]
18
+ },
19
+ "continue_on_error": true
20
+ }
21
+ ],
22
+ "metadata": {
23
+ "note": "Thread escalation_status is already set to 'pending' by agent-runner before this pipeline fires. Ticket status is updated by the calling function (custom_support-triage) from agentMsg.data.escalated."
24
+ },
25
+ "ownership": "platform",
26
+ "is_system": true,
27
+ "is_active": true
28
+ }
29
+ ]
@@ -0,0 +1,84 @@
1
+ [
2
+ {
3
+ "name": "Support Triage",
4
+ "slug": "support_triage",
5
+ "context_template": "A customer has submitted the following support request:\n\n{{user_message}}\n\nReview the knowledge base articles and prior case history provided in your context. Respond helpfully and accurately. If you are not confident in your answer (below 0.75), set escalate=true.",
6
+ "is_multi_turn": true,
7
+ "max_history_messages": 20,
8
+ "confidence_threshold": 0.75,
9
+ "escalation_action": "pipeline",
10
+ "escalation_target": "support_escalation",
11
+ "output_mode": "json",
12
+ "knowledge_sources": [],
13
+ "available_tools": [],
14
+ "metadata": {
15
+ "knowledge_scope": ["kb_article", "resolved_case"],
16
+ "note": "knowledge_sources is resolved at runtime to the requesting account's KB + platform KB. Empty array = full scope search."
17
+ },
18
+ "ownership": "platform",
19
+ "is_system": true,
20
+ "is_active": true
21
+ },
22
+ {
23
+ "name": "Solution AI",
24
+ "slug": "solution_ai",
25
+ "context_template": "A support agent is working on the following case and needs your assistance:\n\nTicket: {{ticket_title}}\nCustomer issue: {{ticket_description}}\n\nConversation so far:\n{{conversation_history}}\n\nAgent question: {{user_message}}\n\nProvide practical, actionable guidance. If you can draft a suggested customer-facing response, include it under 'Suggested Response:'.",
26
+ "is_multi_turn": true,
27
+ "max_history_messages": 30,
28
+ "confidence_threshold": 0.0,
29
+ "escalation_action": null,
30
+ "escalation_target": null,
31
+ "output_mode": "text",
32
+ "knowledge_sources": [],
33
+ "available_tools": [],
34
+ "metadata": {
35
+ "visibility": "internal",
36
+ "knowledge_scope": ["kb_article", "resolved_case"],
37
+ "note": "No escalation — this agent assists human agents. confidence_threshold=0 disables auto-escalation."
38
+ },
39
+ "ownership": "platform",
40
+ "is_system": true,
41
+ "is_active": true
42
+ },
43
+ {
44
+ "name": "Case Analysis Prompt",
45
+ "slug": "case_analysis_prompt",
46
+ "context_template": "Analyze this resolved support ticket:\n\nTicket: {{ticket_title}}\nDescription: {{ticket_description}}\nCreated: {{created_at}}\nResolved: {{resolved_at}}\nStatus: {{status}}\nPriority: {{priority}}\n\nCustomer Conversation:\n{{conversation_history}}\n\nInternal Support Thread:\n{{internal_thread}}\n\nProduce a complete structured postmortem in the JSON format specified in your system prompt.",
47
+ "is_multi_turn": false,
48
+ "max_history_messages": 0,
49
+ "confidence_threshold": 0.0,
50
+ "escalation_action": null,
51
+ "escalation_target": null,
52
+ "output_mode": "json",
53
+ "knowledge_sources": [],
54
+ "available_tools": [],
55
+ "metadata": {
56
+ "requires_resolved_ticket": true,
57
+ "includes_internal_thread": true
58
+ },
59
+ "ownership": "platform",
60
+ "is_system": true,
61
+ "is_active": true
62
+ },
63
+ {
64
+ "name": "KB Generator",
65
+ "slug": "kb_generator",
66
+ "context_template": "Prepare the following resolved case content for KB publication:\n\n{{content}}\n\nIdentify all redactable text and produce a generalised, customer-facing version suitable for publishing as a public knowledge base article.",
67
+ "is_multi_turn": false,
68
+ "max_history_messages": 0,
69
+ "confidence_threshold": 0.0,
70
+ "escalation_action": null,
71
+ "escalation_target": null,
72
+ "output_mode": "json",
73
+ "requires_review": true,
74
+ "knowledge_sources": [],
75
+ "available_tools": [],
76
+ "metadata": {
77
+ "redaction_categories": ["pii", "confidential", "account_specific", "internal_reference"],
78
+ "auto_accept_sensitivity": ["high"]
79
+ },
80
+ "ownership": "platform",
81
+ "is_system": true,
82
+ "is_active": true
83
+ }
84
+ ]