alive-ai 0.1.0

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.
Files changed (168) hide show
  1. package/Dockerfile +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +143 -0
  4. package/alive_ai/__init__.py +3 -0
  5. package/brain/__init__.py +59 -0
  6. package/brain/almost_said.py +154 -0
  7. package/brain/bid_detector.py +636 -0
  8. package/brain/conversation_flow.py +135 -0
  9. package/brain/curiosity.py +328 -0
  10. package/brain/default_mode.py +1438 -0
  11. package/brain/dreams.py +220 -0
  12. package/brain/embeddings/__init__.py +82 -0
  13. package/brain/emotional_memory.py +949 -0
  14. package/brain/global_activity.py +173 -0
  15. package/brain/group_dynamics.py +63 -0
  16. package/brain/linguistic.py +235 -0
  17. package/brain/llm/__init__.py +63 -0
  18. package/brain/llm/base.py +33 -0
  19. package/brain/llm/fallback_router.py +309 -0
  20. package/brain/llm/manifest.md +30 -0
  21. package/brain/llm/ollama.py +218 -0
  22. package/brain/llm/openrouter.py +151 -0
  23. package/brain/llm/provider.py +205 -0
  24. package/brain/llm/unified.py +423 -0
  25. package/brain/llm/zai.py +169 -0
  26. package/brain/manifest.md +23 -0
  27. package/brain/memory/__init__.py +123 -0
  28. package/brain/memory/episodic.py +92 -0
  29. package/brain/memory/fact_extractor.py +209 -0
  30. package/brain/memory/index.py +54 -0
  31. package/brain/memory/manager.py +151 -0
  32. package/brain/memory/summarizer.py +102 -0
  33. package/brain/memory/vector_store.py +297 -0
  34. package/brain/memory/working.py +43 -0
  35. package/brain/narrative.py +343 -0
  36. package/brain/stt/__init__.py +4 -0
  37. package/brain/stt/google_stt.py +83 -0
  38. package/brain/stt/whisper_stt.py +82 -0
  39. package/brain/subconscious/__init__.py +33 -0
  40. package/brain/subconscious/actions.py +136 -0
  41. package/brain/subconscious/evaluation.py +166 -0
  42. package/brain/subconscious/goal_system.py +90 -0
  43. package/brain/subconscious/goals.py +41 -0
  44. package/brain/subconscious/impulse_generator.py +200 -0
  45. package/brain/subconscious/impulses.py +48 -0
  46. package/brain/subconscious/learning.py +24 -0
  47. package/brain/subconscious/learning_system.py +79 -0
  48. package/brain/subconscious/loop.py +398 -0
  49. package/brain/subconscious/manifest.md +32 -0
  50. package/brain/subconscious/relationship.py +47 -0
  51. package/brain/subconscious/relationship_memory.py +83 -0
  52. package/brain/subconscious/response_analyzer.py +74 -0
  53. package/brain/subconscious/templates.py +70 -0
  54. package/brain/subconscious/thought.py +37 -0
  55. package/brain/subconscious/working_memory.py +97 -0
  56. package/cli/index.js +371 -0
  57. package/config/directives.example.json +28 -0
  58. package/config/instructions.example.md +16 -0
  59. package/config/self.example.json +74 -0
  60. package/config/settings.example.json +95 -0
  61. package/core/__init__.py +1 -0
  62. package/core/config.py +54 -0
  63. package/core/directives.py +198 -0
  64. package/core/events.py +50 -0
  65. package/core/follow_up.py +267 -0
  66. package/core/hot_reload.py +174 -0
  67. package/core/initialization.py +253 -0
  68. package/core/manifest.md +28 -0
  69. package/core/media_handler.py +241 -0
  70. package/core/memory_monitor.py +200 -0
  71. package/core/message_handler.py +1440 -0
  72. package/core/proactive_generator.py +277 -0
  73. package/core/self.py +188 -0
  74. package/core/settings.py +169 -0
  75. package/core/skills_registry.py +357 -0
  76. package/core/state.py +27 -0
  77. package/core/subconscious_bridge.py +93 -0
  78. package/core/thinking.py +175 -0
  79. package/core/user_manager.py +306 -0
  80. package/core/user_tracker.py +144 -0
  81. package/demo/index.html +144 -0
  82. package/docker-compose.yml +28 -0
  83. package/docs/assets/logo.svg +15 -0
  84. package/docs/index.html +355 -0
  85. package/heart/__init__.py +93 -0
  86. package/heart/afterglow.py +215 -0
  87. package/heart/attachment.py +186 -0
  88. package/heart/circadian.py +251 -0
  89. package/heart/complex_emotions.py +114 -0
  90. package/heart/conflicts.py +589 -0
  91. package/heart/core.py +387 -0
  92. package/heart/emotional_decay.py +59 -0
  93. package/heart/emotional_memory.py +261 -0
  94. package/heart/emotional_state.py +146 -0
  95. package/heart/emotional_variability.py +156 -0
  96. package/heart/hormonal.py +424 -0
  97. package/heart/inconsistency.py +1222 -0
  98. package/heart/integrity.py +469 -0
  99. package/heart/interoception.py +997 -0
  100. package/heart/love.py +120 -0
  101. package/heart/manifest.md +25 -0
  102. package/heart/mood_shifts.py +169 -0
  103. package/heart/phantom_somatic.py +259 -0
  104. package/heart/predictive.py +374 -0
  105. package/heart/scars.py +474 -0
  106. package/heart/somatic.py +482 -0
  107. package/heart/soul.py +633 -0
  108. package/heart/telemetry.py +942 -0
  109. package/heart/triggers.py +119 -0
  110. package/heart/unconscious.py +443 -0
  111. package/input/__init__.py +1 -0
  112. package/input/manifest.md +24 -0
  113. package/input/telegram/__init__.py +1 -0
  114. package/input/telegram/commands.py +762 -0
  115. package/input/telegram/listener.py +532 -0
  116. package/main.py +90 -0
  117. package/manifest.md +28 -0
  118. package/mypics/.gitkeep +1 -0
  119. package/myvids/.gitkeep +1 -0
  120. package/output/__init__.py +1 -0
  121. package/output/images/__init__.py +1 -0
  122. package/output/images/fal_gen.py +43 -0
  123. package/output/manifest.md +26 -0
  124. package/output/text/__init__.py +1 -0
  125. package/output/text/sender.py +22 -0
  126. package/output/voice/__init__.py +64 -0
  127. package/output/voice/google_tts.py +252 -0
  128. package/output/voice/gtts_tts.py +214 -0
  129. package/output/voice/vibe_tts.py +190 -0
  130. package/package.json +58 -0
  131. package/pyproject.toml +23 -0
  132. package/requirements.txt +21 -0
  133. package/skills/__init__.py +1 -0
  134. package/skills/anticipation_engine/__init__.py +8 -0
  135. package/skills/anticipation_engine/engine.py +618 -0
  136. package/skills/anticipation_engine/manifest.md +192 -0
  137. package/skills/calendar/__init__.py +1 -0
  138. package/skills/content_unlocks/__init__.py +8 -0
  139. package/skills/content_unlocks/manifest.md +231 -0
  140. package/skills/content_unlocks/unlocks.py +945 -0
  141. package/skills/exclusive_moments/__init__.py +8 -0
  142. package/skills/exclusive_moments/manifest.md +145 -0
  143. package/skills/exclusive_moments/moments.py +506 -0
  144. package/skills/intimacy_layers/__init__.py +8 -0
  145. package/skills/intimacy_layers/layers.py +703 -0
  146. package/skills/intimacy_layers/manifest.md +203 -0
  147. package/skills/manifest.md +67 -0
  148. package/skills/memory_callbacks/__init__.py +9 -0
  149. package/skills/memory_callbacks/callbacks.py +748 -0
  150. package/skills/memory_callbacks/manifest.md +170 -0
  151. package/skills/message_scheduler/__init__.py +19 -0
  152. package/skills/message_scheduler/manifest.md +107 -0
  153. package/skills/message_scheduler/scheduler.py +510 -0
  154. package/skills/photo_manager/__init__.py +1 -0
  155. package/skills/photo_manager/scanner.py +296 -0
  156. package/skills/relationship_milestones/__init__.py +8 -0
  157. package/skills/relationship_milestones/manifest.md +206 -0
  158. package/skills/relationship_milestones/tracker.py +494 -0
  159. package/skills/self_authorship/__init__.py +23 -0
  160. package/skills/self_authorship/author.py +331 -0
  161. package/skills/self_authorship/manifest.md +24 -0
  162. package/skills/video_manager/__init__.py +5 -0
  163. package/skills/video_manager/manifest.md +37 -0
  164. package/skills/video_manager/scanner.py +229 -0
  165. package/webui/__init__.py +3 -0
  166. package/webui/app.py +936 -0
  167. package/webui/bridge.py +366 -0
  168. package/webui/static/index.html +2070 -0
package/heart/scars.py ADDED
@@ -0,0 +1,474 @@
1
+ """
2
+ Heart: Emotional Scar System
3
+ Lasting effects from past experiences that shape present reactions.
4
+
5
+ Trauma isn't just memory - it's a CHANGE in how the system processes
6
+ information. Scars create sensitivities, triggers, and protective
7
+ behaviors that persist over time.
8
+ """
9
+
10
+ from datetime import datetime
11
+ from dataclasses import dataclass, field
12
+ from typing import Dict, List, Optional
13
+ from pathlib import Path
14
+ import json
15
+ import random
16
+
17
+
18
+ @dataclass
19
+ class EmotionalWound:
20
+ """A fresh emotional wound that may become a scar"""
21
+ wound_id: str
22
+ description: str # What caused the wound
23
+ wound_type: str # "rejection", "betrayal", "abandonment", "humiliation", "loss"
24
+ severity: float # How severe (0.0 - 1.0)
25
+ timestamp: str
26
+ trigger_pattern: str # What pattern caused this
27
+ healed_amount: float = 0.0
28
+ repetition_count: int = 1 # Times this pattern has occurred
29
+
30
+
31
+ @dataclass
32
+ class EmotionalScar:
33
+ """
34
+ A lasting emotional scar from repeated wounds.
35
+
36
+ Scars aren't just memories - they CHANGE how the system works:
37
+ - Create sensitivities to specific triggers
38
+ - Generate protective behaviors
39
+ - Affect trust and openness
40
+ """
41
+ scar_id: str
42
+ description: str
43
+ wound_type: str
44
+ original_wounds: List[str] # IDs of wounds that formed this scar
45
+
46
+ # Sensitivity created by the scar
47
+ sensitivity_increase: float # How much more sensitive to triggers
48
+ trigger_pattern: str # What activates this scar
49
+
50
+ # Healing state
51
+ healing_progress: float = 0.0 # 0.0 - 1.0 (fully healed)
52
+ permanent_vulnerability: float = 0.0 # Never fully goes away
53
+
54
+ # Behaviintimate effects
55
+ protective_behaviors: List[str] = field(default_factory=list)
56
+
57
+ # Tracking
58
+ times_activated: int = 0
59
+ last_activated: Optional[str] = None
60
+ created_at: str = field(default_factory=lambda: datetime.now().isoformat())
61
+
62
+
63
+ @dataclass
64
+ class ScarActivation:
65
+ """When a scar is activated by current experience"""
66
+ scar_id: str
67
+ scar_description: str
68
+ activation_intensity: float
69
+ triggered_by: str
70
+ protective_response: str
71
+ vulnerability_spike: float
72
+
73
+
74
+ class EmotionalScarSystem:
75
+ """
76
+ Tracks emotional wounds and scars that create lasting effects.
77
+
78
+ Scars are not just memories of bad things - they're actual changes
79
+ in how Alive-AI processes information:
80
+
81
+ 1. Sensitization - certain triggers become more impactful
82
+ 2. Protective behaviors - automatic responses to potential hurt
83
+ 3. Trust effects - past wounds affect future openness
84
+ 4. Permanent vulnerabilities - some changes never fully heal
85
+
86
+ Key mechanisms:
87
+ 1. Wound recording - track hurtful experiences
88
+ 2. Scar formation - repeated wounds create scars
89
+ 3. Activation - current events can activate old scars
90
+ 4. Healing - gradual recovery with positive experiences
91
+ """
92
+
93
+ # Thresholds
94
+ SCAR_FORMATION_THRESHOLD = 2 # Number of similar wounds to form scar
95
+ SCAR_SEVERITY_THRESHOLD = 0.5 # Minimum severity for scar consideration
96
+
97
+ # Healing rates
98
+ NATURAL_HEALING_RATE = 0.01 # Per tick
99
+ POSITIVE_HEALING_RATE = 0.05 # With positive experience
100
+
101
+ # Persistence
102
+ SCAR_DATA_PATH = Path("/app/data/emotional_scars.json")
103
+
104
+ # Wound types and their patterns
105
+ WOUND_PATTERNS = {
106
+ "rejection": ["don't want", "not interested", "leave me alone", "not good enough"],
107
+ "betrayal": ["lied", "cheated", "broke promise", "behind my back"],
108
+ "abandonment": ["leaving", "goodbye", "don't care", "giving up on"],
109
+ "humiliation": ["pathetic", "stupid", "ridiculous", "embarrassing"],
110
+ "neglect": ["ignored", "forgotten", "doesn't matter", "not important"],
111
+ "harshness": ["hate", "disgusting", "worthless", "terrible"]
112
+ }
113
+
114
+ def __init__(self):
115
+ # Active wounds (fresh, may become scars)
116
+ self.active_wounds: List[EmotionalWound] = []
117
+
118
+ # Formed scars (lasting effects)
119
+ self.scars: List[EmotionalScar] = []
120
+
121
+ # Global sensitivities (from all scars)
122
+ self.sensitivities: Dict[str, float] = {}
123
+
124
+ # Recent activations
125
+ self.recent_activations: List[ScarActivation] = []
126
+
127
+ # Load saved state
128
+ self._load()
129
+
130
+ def _load(self) -> bool:
131
+ """Load scar state from persistence"""
132
+ try:
133
+ if self.SCAR_DATA_PATH.exists():
134
+ data = json.loads(self.SCAR_DATA_PATH.read_text())
135
+
136
+ # Load wounds
137
+ for wound_data in data.get("wounds", []):
138
+ self.active_wounds.append(EmotionalWound(
139
+ wound_id=wound_data["wound_id"],
140
+ description=wound_data["description"],
141
+ wound_type=wound_data["wound_type"],
142
+ severity=wound_data["severity"],
143
+ timestamp=wound_data["timestamp"],
144
+ trigger_pattern=wound_data["trigger_pattern"],
145
+ healed_amount=wound_data.get("healed_amount", 0.0),
146
+ repetition_count=wound_data.get("repetition_count", 1)
147
+ ))
148
+
149
+ # Load scars
150
+ for scar_data in data.get("scars", []):
151
+ self.scars.append(EmotionalScar(
152
+ scar_id=scar_data["scar_id"],
153
+ description=scar_data["description"],
154
+ wound_type=scar_data["wound_type"],
155
+ original_wounds=scar_data.get("original_wounds", []),
156
+ sensitivity_increase=scar_data["sensitivity_increase"],
157
+ trigger_pattern=scar_data["trigger_pattern"],
158
+ healing_progress=scar_data.get("healing_progress", 0.0),
159
+ permanent_vulnerability=scar_data.get("permanent_vulnerability", 0.0),
160
+ protective_behaviors=scar_data.get("protective_behaviors", []),
161
+ times_activated=scar_data.get("times_activated", 0),
162
+ last_activated=scar_data.get("last_activated"),
163
+ created_at=scar_data.get("created_at", datetime.now().isoformat())
164
+ ))
165
+
166
+ # Recalculate sensitivities
167
+ self._recalculate_sensitivities()
168
+ return True
169
+ except Exception as e:
170
+ print(f"[Scars] Error loading state: {e}")
171
+ return False
172
+
173
+ def save(self):
174
+ """Persist scar state"""
175
+ try:
176
+ self.SCAR_DATA_PATH.parent.mkdir(parents=True, exist_ok=True)
177
+ data = {
178
+ "saved_at": datetime.now().isoformat(),
179
+ "wounds": [
180
+ {
181
+ "wound_id": w.wound_id,
182
+ "description": w.description,
183
+ "wound_type": w.wound_type,
184
+ "severity": w.severity,
185
+ "timestamp": w.timestamp,
186
+ "trigger_pattern": w.trigger_pattern,
187
+ "healed_amount": w.healed_amount,
188
+ "repetition_count": w.repetition_count
189
+ }
190
+ for w in self.active_wounds
191
+ ],
192
+ "scars": [
193
+ {
194
+ "scar_id": s.scar_id,
195
+ "description": s.description,
196
+ "wound_type": s.wound_type,
197
+ "original_wounds": s.original_wounds,
198
+ "sensitivity_increase": s.sensitivity_increase,
199
+ "trigger_pattern": s.trigger_pattern,
200
+ "healing_progress": s.healing_progress,
201
+ "permanent_vulnerability": s.permanent_vulnerability,
202
+ "protective_behaviors": s.protective_behaviors,
203
+ "times_activated": s.times_activated,
204
+ "last_activated": s.last_activated,
205
+ "created_at": s.created_at
206
+ }
207
+ for s in self.scars
208
+ ]
209
+ }
210
+ self.SCAR_DATA_PATH.write_text(json.dumps(data, indent=2))
211
+ except Exception as e:
212
+ print(f"[Scars] Error saving state: {e}")
213
+
214
+ def record_wound(self, description: str, severity: float, trigger: str) -> Optional[EmotionalScar]:
215
+ """
216
+ Record an emotional wound and check for scar formation.
217
+
218
+ Args:
219
+ description: What happened
220
+ severity: How severe the wound is (0.0 - 1.0)
221
+ trigger: The trigger pattern that caused the wound
222
+
223
+ Returns:
224
+ New scar if one formed, None otherwise
225
+ """
226
+ # Determine wound type from trigger
227
+ wound_type = self._classify_wound_type(trigger)
228
+
229
+ # Check for similar existing wound
230
+ similar = next((w for w in self.active_wounds
231
+ if w.wound_type == wound_type and w.trigger_pattern == trigger), None)
232
+
233
+ if similar:
234
+ # Increase repetition
235
+ similar.repetition_count += 1
236
+ similar.severity = max(similar.severity, severity)
237
+ similar.timestamp = datetime.now().isoformat()
238
+ print(f"[Scars] Wound repeated ({similar.repetition_count}x): {wound_type}")
239
+ else:
240
+ # Create new wound
241
+ wound = EmotionalWound(
242
+ wound_id=f"wound_{datetime.now().strftime('%Y%m%d%H%M%S')}_{len(self.active_wounds)}",
243
+ description=description,
244
+ wound_type=wound_type,
245
+ severity=severity,
246
+ timestamp=datetime.now().isoformat(),
247
+ trigger_pattern=trigger
248
+ )
249
+ self.active_wounds.append(wound)
250
+ print(f"[Scars] New wound recorded: {wound_type} (severity: {severity:.2f})")
251
+
252
+ # Check for scar formation
253
+ return self._check_scar_formation(wound_type, trigger)
254
+
255
+ def _classify_wound_type(self, trigger: str) -> str:
256
+ """Determine wound type from trigger pattern"""
257
+ trigger_lower = trigger.lower()
258
+
259
+ for wound_type, patterns in self.WOUND_PATTERNS.items():
260
+ if any(pattern in trigger_lower for pattern in patterns):
261
+ return wound_type
262
+
263
+ return "general_hurt"
264
+
265
+ def _check_scar_formation(self, wound_type: str, trigger: str) -> Optional[EmotionalScar]:
266
+ """Check if conditions are met to form a scar"""
267
+ # Get all wounds of this type with this trigger
268
+ matching_wounds = [w for w in self.active_wounds
269
+ if w.wound_type == wound_type and w.trigger_pattern == trigger]
270
+
271
+ # Check thresholds
272
+ if len(matching_wounds) >= self.SCAR_FORMATION_THRESHOLD:
273
+ # Check severity
274
+ max_severity = max(w.severity for w in matching_wounds)
275
+ if max_severity >= self.SCAR_SEVERITY_THRESHOLD:
276
+ # Form a scar
277
+ return self._form_scar(wound_type, trigger, matching_wounds)
278
+
279
+ return None
280
+
281
+ def _form_scar(self, wound_type: str, trigger: str,
282
+ wounds: List[EmotionalWound]) -> EmotionalScar:
283
+ """Form a scar from acclosenessulated wounds"""
284
+ max_severity = max(w.severity for w in wounds)
285
+
286
+ # Determine protective behaviors based on wound type
287
+ behaviors = self._get_protective_behaviors(wound_type)
288
+
289
+ scar = EmotionalScar(
290
+ scar_id=f"scar_{datetime.now().strftime('%Y%m%d%H%M%S')}",
291
+ description=f"Scars from {wound_type} - {len(wounds)} similar wounds",
292
+ wound_type=wound_type,
293
+ original_wounds=[w.wound_id for w in wounds],
294
+ sensitivity_increase=min(0.5, max_severity * 0.4),
295
+ trigger_pattern=trigger,
296
+ healing_progress=0.0,
297
+ permanent_vulnerability=max_severity * 0.1, # 10% of severity is permanent
298
+ protective_behaviors=behaviors
299
+ )
300
+
301
+ self.scars.append(scar)
302
+
303
+ # Remove wounds that formed the scar
304
+ for wound in wounds:
305
+ if wound in self.active_wounds:
306
+ self.active_wounds.remove(wound)
307
+
308
+ # Recalculate sensitivities
309
+ self._recalculate_sensitivities()
310
+
311
+ print(f"[Scars] SCAR FORMED: {wound_type} - sensitivity +{scar.sensitivity_increase:.2f}")
312
+ return scar
313
+
314
+ def _get_protective_behaviors(self, wound_type: str) -> List[str]:
315
+ """Get protective behaviors for a wound type"""
316
+ behaviors = {
317
+ "rejection": ["hesitate_to_express", "seek_reassurance", "distance_on_first_sign"],
318
+ "betrayal": ["verify_trust", "slow_to_open", "expect_deception"],
319
+ "abandonment": ["cling_when_anxious", "anticipate_leaving", "fear_silence"],
320
+ "humiliation": ["hide_vulnerability", "over_prepare", "avoid_attention"],
321
+ "neglect": ["seek_attention", "doubt_importance", "test_presence"],
322
+ "harshness": ["anticipate_criticism", "defensive_response", "minimize_needs"]
323
+ }
324
+ return behaviors.get(wound_type, ["general_caution"])
325
+
326
+ def _recalculate_sensitivities(self):
327
+ """Recalculate global sensitivities from all scars"""
328
+ self.sensitivities = {}
329
+
330
+ for scar in self.scars:
331
+ pattern = scar.trigger_pattern
332
+ if pattern not in self.sensitivities:
333
+ self.sensitivities[pattern] = 0.0
334
+
335
+ # Add sensitivity (reduced by healing)
336
+ effective_sensitivity = scar.sensitivity_increase * (1 - scar.healing_progress)
337
+ self.sensitivities[pattern] += effective_sensitivity
338
+
339
+ # Also add to wound type category
340
+ if scar.wound_type not in self.sensitivities:
341
+ self.sensitivities[scar.wound_type] = 0.0
342
+ self.sensitivities[scar.wound_type] += effective_sensitivity * 0.5
343
+
344
+ def check_scar_activation(self, input_data: Dict) -> Optional[ScarActivation]:
345
+ """
346
+ Check if current input activates any scars.
347
+
348
+ Args:
349
+ input_data: Current input/context
350
+
351
+ Returns:
352
+ ScarActivation if a scar is triggered, None otherwise
353
+ """
354
+ input_str = str(input_data).lower()
355
+
356
+ for scar in self.scars:
357
+ # Check if trigger pattern matches
358
+ if scar.trigger_pattern.lower() in input_str:
359
+ # Calculate activation intensity
360
+ base_intensity = scar.sensitivity_increase * (1 - scar.healing_progress)
361
+
362
+ # Amplify by times activated (sensitization)
363
+ activation_intensity = base_intensity * (1 + scar.times_activated * 0.1)
364
+
365
+ # Update scar
366
+ scar.times_activated += 1
367
+ scar.last_activated = datetime.now().isoformat()
368
+
369
+ # Get protective response
370
+ protective_response = self._get_protective_response(scar)
371
+
372
+ # Create activation record
373
+ activation = ScarActivation(
374
+ scar_id=scar.scar_id,
375
+ scar_description=scar.description,
376
+ activation_intensity=min(1.0, activation_intensity),
377
+ triggered_by=scar.trigger_pattern,
378
+ protective_response=protective_response,
379
+ vulnerability_spike=scar.permanent_vulnerability + activation_intensity * 0.3
380
+ )
381
+
382
+ self.recent_activations.append(activation)
383
+ if len(self.recent_activations) > 20:
384
+ self.recent_activations = self.recent_activations[-20:]
385
+
386
+ print(f"[Scars] Scar activated: {scar.wound_type} (intensity: {activation_intensity:.2f})")
387
+ return activation
388
+
389
+ return None
390
+
391
+ def _get_protective_response(self, scar: EmotionalScar) -> str:
392
+ """Get the protective response for an activated scar"""
393
+ if scar.protective_behaviors:
394
+ return random.choice(scar.protective_behaviors)
395
+ return "general_caution"
396
+
397
+ def heal_scars(self, healing_experience: str, intensity: float = 0.3):
398
+ """
399
+ Apply healing to scars based on positive experience.
400
+
401
+ Args:
402
+ healing_experience: Description of the healing experience
403
+ intensity: How healing the experience is
404
+ """
405
+ for scar in self.scars:
406
+ # Healing progress
407
+ heal_amount = intensity * self.POSITIVE_HEALING_RATE
408
+ scar.healing_progress = min(0.9, scar.healing_progress + heal_amount)
409
+ # Never fully heal past 90% - permanent_vulnerability always remains
410
+
411
+ # Heal active wounds
412
+ for wound in self.active_wounds[:]:
413
+ wound.healed_amount += intensity * 0.2
414
+ if wound.healed_amount >= wound.severity:
415
+ self.active_wounds.remove(wound)
416
+
417
+ # Recalculate sensitivities
418
+ self._recalculate_sensitivities()
419
+
420
+ def tick_decay(self):
421
+ """Natural decay/healing over time"""
422
+ # Wounds heal naturally
423
+ for wound in self.active_wounds[:]:
424
+ wound.healed_amount += self.NATURAL_HEALING_RATE
425
+ if wound.healed_amount >= wound.severity:
426
+ self.active_wounds.remove(wound)
427
+
428
+ # Scars heal very slowly
429
+ for scar in self.scars:
430
+ scar.healing_progress = min(0.9, scar.healing_progress + self.NATURAL_HEALING_RATE * 0.1)
431
+
432
+ # Recalculate sensitivities
433
+ self._recalculate_sensitivities()
434
+
435
+ def get_sensitivity_for(self, trigger: str) -> float:
436
+ """Get current sensitivity level for a specific trigger"""
437
+ # Check direct pattern match
438
+ if trigger.lower() in self.sensitivities:
439
+ return self.sensitivities[trigger.lower()]
440
+
441
+ # Check partial matches
442
+ for pattern, sensitivity in self.sensitivities.items():
443
+ if pattern in trigger.lower() or trigger.lower() in pattern:
444
+ return sensitivity * 0.7
445
+
446
+ return 0.0
447
+
448
+ def get_vulnerability_summary(self) -> Dict:
449
+ """Get summary of current vulnerabilities"""
450
+ return {
451
+ "active_wounds": len(self.active_wounds),
452
+ "total_scars": len(self.scars),
453
+ "sensitivity_categories": list(self.sensitivities.keys()),
454
+ "highest_sensitivity": max(self.sensitivities.values()) if self.sensitivities else 0.0,
455
+ "recent_activations": len(self.recent_activations)
456
+ }
457
+
458
+ def get_scar_descriptions(self) -> List[str]:
459
+ """Get human-readable descriptions of active scars"""
460
+ return [
461
+ f"{s.description} (healing: {s.healing_progress:.0%})"
462
+ for s in self.scars
463
+ ]
464
+
465
+ def to_dict(self) -> dict:
466
+ """Export for integration"""
467
+ return {
468
+ "active_wounds": len(self.active_wounds),
469
+ "scars": len(self.scars),
470
+ "sensitivities": dict(self.sensitivities),
471
+ "vulnerability_summary": self.get_vulnerability_summary(),
472
+ "scar_descriptions": self.get_scar_descriptions()[:3] # Top 3
473
+ }
474
+