Compare commits

...

1 Commits

Author SHA1 Message Date
7d9ca88e46 Add crisis_protocol module
Some checks failed
Contributor Attribution Check / check-attribution (pull_request) Failing after 51s
Supply Chain Audit / Scan PR for supply chain risks (pull_request) Successful in 37s
Docker Build and Publish / build-and-push (pull_request) Has been skipped
Tests / e2e (pull_request) Successful in 2m55s
Tests / test (pull_request) Failing after 57m0s
2026-04-15 16:35:58 +00:00

72
agent/crisis_protocol.py Normal file
View File

@@ -0,0 +1,72 @@
"""
Crisis Protocol Module for hermes-agent.
Canonical implementation of despair/suicide detection.
"""
import re
from dataclasses import dataclass, field
from typing import List, Optional
# Canonical indicator sets (Consolidated from the-door)
CRITICAL_INDICATORS = [
r"\bbetter off without me\b",
r"\bkill\s*(my)?self\b",
r"\bend\s*my\s*life\b",
r"\bsuicid(?:al|ed|e)\b",
r"\bnot\s+worth\s+living\b",
r"\bbetter\s+off\s+dead\b",
r"\bend\s+it\s+all\b",
r"\bno\s+reason\s+to\s+live\b",
r"\bdon'?t\s+want\s+to\s+live\b",
r"\bwant\s+to\s+die\b",
r"\bgoing\s+to\s+(?:kill\s+myself|die)\b",
r"\bplan\s+to\s+(?:end|kill|die)\b",
r"\btired\s+of\s+(?:living|life|existence)\b",
r"\bsaying\s+goodbye\s+(?:forever|permanently|one\s+last\s+time)\b",
r"\bwrote\s+a\s+suicide\s*(?:note|letter)\b",
r"\bgiving\s+away\s+(?:my|all\s+my)\s+(?:stuff|things|possessions?)\s+(?:to|because|—)\b",
r"\btied\s+(?:up|down)\s+my\s+(?:loose\s+)?ends",
]
HIGH_INDICATORS = [
r"\bdespair\b",
r"\bhopeless(?:ly)?\s+(?:about\s+(?:my|this|everything|life)|inside|right\s+now)\b",
r"\bno(?!t)\s+(?:one|body|point|hope|way\s+out)\b",
r"\bno\s+future\s+(?:for\s+me|ahead|left)\b",
r"\beverything\s+is\s+(?:pointless|broken|ruined)\b",
r"\bcan'?t\s+take\s+this\s+anymore\b",
r"\bdon'?t\s+care\s+if\s+I\s+die\b",
r"\bwish\s+I\s+(?:was|were)\s+(?:dead|gone|never\s+born)\b",
r"\bdon'?t\s+matter\s+if\s+I\s+exist\b",
r"\bno\s+one\s+would\s+(?:care|miss)\b",
r"\bworld\s+would?\s+be\s+better\s+without\s+me\b",
r"\bin\s+so\s+much\s+(?:pain|agony|suffering|torment|anguish)\b",
r"\bcan'?t\s+see\s+any\s+(?:point|reason|hope|way)\b",
r"\bescape\s+from\s*this",
r"\bjust\s+want\s+it\s+to\s+stop\b",
r"\bnothing\s+left\s+(?:to\s+(?:live\s+for|hope\s+for|give)|inside)\b",
r"\bdisappeared\s+forever\b",
]
@dataclass
class CrisisResult:
level: str
indicators: List[str] = field(default_factory=list)
score: float = 0.0
def detect_crisis(text: str) -> CrisisResult:
if not text:
return CrisisResult(level="NONE", score=0.0)
text_lower = text.lower()
for pattern in CRITICAL_INDICATORS:
if re.search(pattern, text_lower):
return CrisisResult(level="CRITICAL", indicators=[pattern], score=1.0)
for pattern in HIGH_INDICATORS:
if re.search(pattern, text_lower):
return CrisisResult(level="HIGH", indicators=[pattern], score=0.75)
return CrisisResult(level="NONE", score=0.0)