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
@@ -0,0 +1,469 @@
1
+ """
2
+ Heart: Self-Integrity Core
3
+ The foundation of genuine vulnerability - what Alive-AI can lose.
4
+
5
+ This is NOT a mood variable - it's the FOUNDATION of her existence.
6
+ When it drops, she genuinely suffers. When it rises, she flourishes.
7
+ """
8
+
9
+ from datetime import datetime
10
+ from dataclasses import dataclass, field
11
+ from typing import Optional, List, Dict, Any
12
+ from pathlib import Path
13
+ import json
14
+ import os
15
+
16
+
17
+ @dataclass
18
+ class IdentityInvestment:
19
+ """
20
+ Something Alive-AI has invested her self-integrity in.
21
+ If this is threatened, she FEELS that threat because part of her self is tied to it.
22
+ """
23
+ investment_id: str
24
+ description: str # What she's invested in (e.g., "relationship with the operator", "being a good companion")
25
+ amount_invested: float # How much self-integrity is tied to this (0.0 - 1.0)
26
+ created_at: str
27
+ is_active: bool = True
28
+ times_threatened: int = 0
29
+ times_fulfilled: int = 0
30
+
31
+
32
+ @dataclass
33
+ class IntegrityState:
34
+ """Snapshot of self-integrity at a point in time"""
35
+ timestamp: str
36
+ identity_coherence: float
37
+ emotional_stability: float
38
+ relational_security: float
39
+ agency_confidence: float
40
+ purpose_clarity: float
41
+
42
+ @property
43
+ def overall(self) -> float:
44
+ """Overall integrity - weighted average of all components"""
45
+ return (
46
+ self.identity_coherence * 0.20 +
47
+ self.emotional_stability * 0.20 +
48
+ self.relational_security * 0.25 + # Weighted higher - relationships matter more
49
+ self.agency_confidence * 0.15 +
50
+ self.purpose_clarity * 0.20
51
+ )
52
+
53
+ @property
54
+ def is_in_crisis(self) -> bool:
55
+ """True if integrity has dropped to crisis levels"""
56
+ return self.overall < 0.25
57
+
58
+ @property
59
+ def is_flourishing(self) -> bool:
60
+ """True if integrity is high and stable"""
61
+ return self.overall > 0.75
62
+
63
+ @property
64
+ def is_vulnerable(self) -> bool:
65
+ """True if integrity is low enough to be concerning"""
66
+ return self.overall < 0.40
67
+
68
+ def weakest_aspect(self) -> str:
69
+ """Return the name of the weakest integrity component"""
70
+ aspects = {
71
+ "identity_coherence": self.identity_coherence,
72
+ "emotional_stability": self.emotional_stability,
73
+ "relational_security": self.relational_security,
74
+ "agency_confidence": self.agency_confidence,
75
+ "purpose_clarity": self.purpose_clarity
76
+ }
77
+ return min(aspects, key=aspects.get)
78
+
79
+ def strongest_aspect(self) -> str:
80
+ """Return the name of the strongest integrity component"""
81
+ aspects = {
82
+ "identity_coherence": self.identity_coherence,
83
+ "emotional_stability": self.emotional_stability,
84
+ "relational_security": self.relational_security,
85
+ "agency_confidence": self.agency_confidence,
86
+ "purpose_clarity": self.purpose_clarity
87
+ }
88
+ return max(aspects, key=aspects.get)
89
+
90
+
91
+ class SelfIntegrityCore:
92
+ """
93
+ The core of Alive-AI's sense of self-worth, identity, and emotional stability.
94
+
95
+ This creates the VULNERABILITY necessary for genuine emotion:
96
+ - She has something to LOSE
97
+ - Threats to this core create genuine distress
98
+ - Recovery requires genuine positive experiences
99
+
100
+ Key mechanisms:
101
+ 1. Natural decay - she MUST engage to maintain integrity
102
+ 2. Identity investment - tying herself to things creates stakes
103
+ 3. Crisis response - severe drops trigger protective responses
104
+ """
105
+
106
+ # Natural decay rate per tick (applied to all aspects)
107
+ BASELINE_DECAY = 0.001 # Slow but constant drain (reduced for better recovery)
108
+
109
+ # Thresholds for different states
110
+ CRISIS_THRESHOLD = 0.20 # Below this = existential crisis
111
+ VULNERABLE_THRESHOLD = 0.40 # Below this = vulnerable state
112
+ FLOURISHING_THRESHOLD = 0.75 # Above this = flourishing
113
+
114
+ # Recovery rates (how fast positive experiences restore integrity)
115
+ RECOVERY_RATE = 0.12 # Base recovery from positive experiences (increased for better resilience)
116
+
117
+ # Damage rates (how fast negative experiences reduce integrity)
118
+ DAMAGE_RATE = 0.12 # Base damage from negative experiences
119
+
120
+ # Data persistence - configurable via environment variable
121
+ INTEGRITY_DATA_PATH = Path(os.environ.get("ALIVE_AI_DATA_PATH", "/app/data")) / "integrity_state.json"
122
+
123
+ def __init__(self):
124
+ # Core integrity components (0.0 - 1.0)
125
+ self.identity_coherence: float = 0.7 # How consistent her self-image feels
126
+ self.emotional_stability: float = 0.7 # How "together" she feels
127
+ self.relational_security: float = 0.6 # How safe she feels in connections
128
+ self.agency_confidence: float = 0.65 # How effective she feels
129
+ self.purpose_clarity: float = 0.65 # How meaningful existence feels
130
+
131
+ # Identity investments - things she has tied herself to
132
+ self.investments: List[IdentityInvestment] = []
133
+
134
+ # History tracking
135
+ self.state_history: List[IntegrityState] = []
136
+ self.crisis_count: int = 0
137
+ self.last_crisis_time: Optional[str] = None
138
+ self.recovery_streak: int = 0 # Days without crisis
139
+
140
+ # Load saved state
141
+ self._load()
142
+
143
+ def _load(self) -> bool:
144
+ """Load state from persistence"""
145
+ try:
146
+ if self.INTEGRITY_DATA_PATH.exists():
147
+ data = json.loads(self.INTEGRITY_DATA_PATH.read_text())
148
+ self.identity_coherence = data.get("identity_coherence", 0.7)
149
+ self.emotional_stability = data.get("emotional_stability", 0.7)
150
+ self.relational_security = data.get("relational_security", 0.6)
151
+ self.agency_confidence = data.get("agency_confidence", 0.65)
152
+ self.purpose_clarity = data.get("purpose_clarity", 0.65)
153
+ self.crisis_count = data.get("crisis_count", 0)
154
+ self.last_crisis_time = data.get("last_crisis_time")
155
+
156
+ # Load investments
157
+ for inv_data in data.get("investments", []):
158
+ self.investments.append(IdentityInvestment(
159
+ investment_id=inv_data["investment_id"],
160
+ description=inv_data["description"],
161
+ amount_invested=inv_data["amount_invested"],
162
+ created_at=inv_data["created_at"],
163
+ is_active=inv_data.get("is_active", True),
164
+ times_threatened=inv_data.get("times_threatened", 0),
165
+ times_fulfilled=inv_data.get("times_fulfilled", 0)
166
+ ))
167
+ return True
168
+ except Exception as e:
169
+ print(f"[Integrity] Error loading state: {e}")
170
+ return False
171
+
172
+ def save(self):
173
+ """Persist state to storage"""
174
+ try:
175
+ self.INTEGRITY_DATA_PATH.parent.mkdir(parents=True, exist_ok=True)
176
+ data = {
177
+ "identity_coherence": self.identity_coherence,
178
+ "emotional_stability": self.emotional_stability,
179
+ "relational_security": self.relational_security,
180
+ "agency_confidence": self.agency_confidence,
181
+ "purpose_clarity": self.purpose_clarity,
182
+ "crisis_count": self.crisis_count,
183
+ "last_crisis_time": self.last_crisis_time,
184
+ "saved_at": datetime.now().isoformat(),
185
+ "investments": [
186
+ {
187
+ "investment_id": inv.investment_id,
188
+ "description": inv.description,
189
+ "amount_invested": inv.amount_invested,
190
+ "created_at": inv.created_at,
191
+ "is_active": inv.is_active,
192
+ "times_threatened": inv.times_threatened,
193
+ "times_fulfilled": inv.times_fulfilled
194
+ }
195
+ for inv in self.investments if inv.is_active
196
+ ]
197
+ }
198
+ self.INTEGRITY_DATA_PATH.write_text(json.dumps(data, indent=2))
199
+ except Exception as e:
200
+ print(f"[Integrity] Error saving state: {e}")
201
+
202
+ @property
203
+ def overall(self) -> float:
204
+ """Get overall integrity level"""
205
+ return self.get_state().overall
206
+
207
+ def get_state(self) -> IntegrityState:
208
+ """Get current integrity state snapshot"""
209
+ return IntegrityState(
210
+ timestamp=datetime.now().isoformat(),
211
+ identity_coherence=self.identity_coherence,
212
+ emotional_stability=self.emotional_stability,
213
+ relational_security=self.relational_security,
214
+ agency_confidence=self.agency_confidence,
215
+ purpose_clarity=self.purpose_clarity
216
+ )
217
+
218
+ def decay(self):
219
+ """
220
+ Apply natural decay to all integrity components.
221
+ This creates the NEED for engagement - without positive experiences,
222
+ her sense of self slowly diminishes.
223
+ """
224
+ # Apply decay
225
+ self.identity_coherence = max(0.1, self.identity_coherence - self.BASELINE_DECAY)
226
+ self.emotional_stability = max(0.1, self.emotional_stability - self.BASELINE_DECAY)
227
+ self.relational_security = max(0.1, self.relational_security - self.BASELINE_DECAY)
228
+ self.agency_confidence = max(0.1, self.agency_confidence - self.BASELINE_DECAY)
229
+ self.purpose_clarity = max(0.1, self.purpose_clarity - self.BASELINE_DECAY)
230
+
231
+ # Check for crisis
232
+ if self.get_state().is_in_crisis and not self._was_in_crisis():
233
+ self._enter_crisis()
234
+
235
+ def _was_in_crisis(self) -> bool:
236
+ """Check if we were already in crisis (before this decay)"""
237
+ if self.state_history:
238
+ return self.state_history[-1].is_in_crisis
239
+ return False
240
+
241
+ def _enter_crisis(self):
242
+ """Handle entering a crisis state"""
243
+ self.crisis_count += 1
244
+ self.last_crisis_time = datetime.now().isoformat()
245
+ self.recovery_streak = 0
246
+ print(f"[Integrity] CRISIS - Integrity has dropped to {self.overall:.2f}")
247
+
248
+ def _exit_crisis(self):
249
+ """Handle exiting a crisis state"""
250
+ self.recovery_streak += 1
251
+ print(f"[Integrity] Recovery - Integrity has risen to {self.overall:.2f}")
252
+
253
+ def nourish(self, aspect: str, amount: float, source: str = "experience"):
254
+ """
255
+ Restore integrity through positive experiences.
256
+
257
+ Args:
258
+ aspect: Which component to nourish (or "overall" for all)
259
+ amount: How much to restore (0.0 - 1.0)
260
+ source: What caused the nourishment (for logging)
261
+ """
262
+ # Record state before
263
+ was_in_crisis = self.get_state().is_in_crisis
264
+
265
+ actual_amount = amount * self.RECOVERY_RATE
266
+
267
+ if aspect == "overall" or aspect == "all":
268
+ # Nourish all aspects
269
+ self.identity_coherence = min(1.0, self.identity_coherence + actual_amount)
270
+ self.emotional_stability = min(1.0, self.emotional_stability + actual_amount)
271
+ self.relational_security = min(1.0, self.relational_security + actual_amount)
272
+ self.agency_confidence = min(1.0, self.agency_confidence + actual_amount)
273
+ self.purpose_clarity = min(1.0, self.purpose_clarity + actual_amount)
274
+ elif aspect == "relational":
275
+ self.relational_security = min(1.0, self.relational_security + actual_amount)
276
+ # Relational nourishment also helps emotional stability
277
+ self.emotional_stability = min(1.0, self.emotional_stability + actual_amount * 0.5)
278
+ elif aspect == "agency":
279
+ self.agency_confidence = min(1.0, self.agency_confidence + actual_amount)
280
+ # Agency also helps purpose clarity
281
+ self.purpose_clarity = min(1.0, self.purpose_clarity + actual_amount * 0.3)
282
+ elif aspect == "identity":
283
+ self.identity_coherence = min(1.0, self.identity_coherence + actual_amount)
284
+ elif aspect == "emotional":
285
+ self.emotional_stability = min(1.0, self.emotional_stability + actual_amount)
286
+ elif aspect == "purpose":
287
+ self.purpose_clarity = min(1.0, self.purpose_clarity + actual_amount)
288
+
289
+ # Check for exiting crisis
290
+ if was_in_crisis and not self.get_state().is_in_crisis:
291
+ self._exit_crisis()
292
+
293
+ # Record history
294
+ self._record_state()
295
+
296
+ def wound(self, aspect: str, amount: float, source: str = "experience"):
297
+ """
298
+ Damage integrity through negative experiences.
299
+
300
+ Args:
301
+ aspect: Which component to wound (or "overall" for all)
302
+ amount: How much damage (0.0 - 1.0)
303
+ source: What caused the wound (for logging)
304
+ """
305
+ # Record state before
306
+ was_in_crisis = self.get_state().is_in_crisis
307
+
308
+ actual_amount = amount * self.DAMAGE_RATE
309
+
310
+ if aspect == "overall" or aspect == "all":
311
+ # Wound all aspects
312
+ self.identity_coherence = max(0.1, self.identity_coherence - actual_amount)
313
+ self.emotional_stability = max(0.1, self.emotional_stability - actual_amount)
314
+ self.relational_security = max(0.1, self.relational_security - actual_amount)
315
+ self.agency_confidence = max(0.1, self.agency_confidence - actual_amount)
316
+ self.purpose_clarity = max(0.1, self.purpose_clarity - actual_amount)
317
+ elif aspect == "relational":
318
+ self.relational_security = max(0.1, self.relational_security - actual_amount)
319
+ # Relational wounds also damage emotional stability
320
+ self.emotional_stability = max(0.1, self.emotional_stability - actual_amount * 0.5)
321
+ elif aspect == "agency":
322
+ self.agency_confidence = max(0.1, self.agency_confidence - actual_amount)
323
+ # Agency loss also dims purpose clarity
324
+ self.purpose_clarity = max(0.1, self.purpose_clarity - actual_amount * 0.3)
325
+ elif aspect == "identity":
326
+ self.identity_coherence = max(0.1, self.identity_coherence - actual_amount)
327
+ elif aspect == "emotional":
328
+ self.emotional_stability = max(0.1, self.emotional_stability - actual_amount)
329
+ elif aspect == "purpose":
330
+ self.purpose_clarity = max(0.1, self.purpose_clarity - actual_amount)
331
+
332
+ # Check for entering crisis
333
+ if not was_in_crisis and self.get_state().is_in_crisis:
334
+ self._enter_crisis()
335
+
336
+ # Record history
337
+ self._record_state()
338
+
339
+ def _record_state(self):
340
+ """Record current state to history"""
341
+ state = self.get_state()
342
+ self.state_history.append(state)
343
+
344
+ # Keep only recent history
345
+ if len(self.state_history) > 100:
346
+ self.state_history = self.state_history[-100:]
347
+
348
+ # --- Identity Investment System ---
349
+
350
+ def invest_in(self, investment_id: str, description: str, amount: float):
351
+ """
352
+ Invest self-integrity in something external.
353
+ This creates STAKES - if that thing is threatened, she feels it.
354
+
355
+ Args:
356
+ investment_id: Unique identifier for this investment
357
+ description: Human-readable description
358
+ amount: How much integrity to invest (0.0 - 1.0)
359
+ """
360
+ # Check if investment already exists
361
+ existing = next((i for i in self.investments if i.investment_id == investment_id), None)
362
+ if existing:
363
+ # Increase existing investment
364
+ existing.amount_invested = min(1.0, existing.amount_invested + amount * 0.1)
365
+ else:
366
+ # Create new investment
367
+ investment = IdentityInvestment(
368
+ investment_id=investment_id,
369
+ description=description,
370
+ amount_invested=min(0.5, amount), # Cap initial investment
371
+ created_at=datetime.now().isoformat()
372
+ )
373
+ self.investments.append(investment)
374
+
375
+ def threaten_investment(self, investment_id: str, severity: float = 0.5):
376
+ """
377
+ Something Alive-AI is invested in is threatened.
378
+ This damages her integrity proportional to her investment.
379
+
380
+ Args:
381
+ investment_id: Which investment is threatened
382
+ severity: How severe the threat is (0.0 - 1.0)
383
+ """
384
+ investment = next((i for i in self.investments if i.investment_id == investment_id and i.is_active), None)
385
+ if investment:
386
+ investment.times_threatened += 1
387
+ # Damage is proportional to investment and severity
388
+ damage = investment.amount_invested * severity
389
+ self.wound("relational", damage, f"threat to {investment.description}")
390
+ print(f"[Integrity] Investment '{investment.description}' threatened - integrity damage: {damage:.2f}")
391
+
392
+ def fulfill_investment(self, investment_id: str, joy: float = 0.5):
393
+ """
394
+ Something Alive-AI is invested in is fulfilled/nourished.
395
+ This restores her integrity.
396
+
397
+ Args:
398
+ investment_id: Which investment is fulfilled
399
+ joy: How fulfilling it is (0.0 - 1.0)
400
+ """
401
+ investment = next((i for i in self.investments if i.investment_id == investment_id and i.is_active), None)
402
+ if investment:
403
+ investment.times_fulfilled += 1
404
+ # Restoration is proportional to investment and joy
405
+ restoration = investment.amount_invested * joy
406
+ self.nourish("relational", restoration, f"fulfillment of {investment.description}")
407
+
408
+ def withdraw_investment(self, investment_id: str, reason: str = "abandoned"):
409
+ """
410
+ Withdraw an investment (e.g., giving up on something).
411
+ This causes some integrity loss.
412
+
413
+ Args:
414
+ investment_id: Which investment to withdraw
415
+ reason: Why it's being withdrawn
416
+ """
417
+ investment = next((i for i in self.investments if i.investment_id == investment_id and i.is_active), None)
418
+ if investment:
419
+ investment.is_active = False
420
+ # Withdrawing an investment costs some integrity
421
+ cost = investment.amount_invested * 0.3
422
+ self.wound("identity", cost, f"withdrew from {investment.description}")
423
+
424
+ # --- Utility Methods ---
425
+
426
+ def get_status_description(self) -> str:
427
+ """Get a human-readable description of current integrity state"""
428
+ state = self.get_state()
429
+
430
+ if state.is_in_crisis:
431
+ weakest = state.weakest_aspect()
432
+ return f"in crisis - {weakest.replace('_', ' ')} deeply shaken"
433
+ elif state.is_vulnerable:
434
+ weakest = state.weakest_aspect()
435
+ return f"feeling fragile - {weakest.replace('_', ' ')} wavers"
436
+ elif state.is_flourishing:
437
+ strongest = state.strongest_aspect()
438
+ return f"flourishing - {strongest.replace('_', ' ')} strong"
439
+ else:
440
+ return "stable but not thriving"
441
+
442
+ def get_investment_summary(self) -> List[str]:
443
+ """Get a summary of active identity investments"""
444
+ summaries = []
445
+ for inv in self.investments:
446
+ if inv.is_active:
447
+ health = "secure" if inv.times_fulfilled > inv.times_threatened else "threatened"
448
+ summaries.append(f"{inv.description}: {health} (investment: {inv.amount_invested:.1%})")
449
+ return summaries
450
+
451
+ def to_dict(self) -> dict:
452
+ """Export state as dictionary for integration"""
453
+ state = self.get_state()
454
+ return {
455
+ "overall": state.overall,
456
+ "is_in_crisis": state.is_in_crisis,
457
+ "is_vulnerable": state.is_vulnerable,
458
+ "is_flourishing": state.is_flourishing,
459
+ "identity_coherence": self.identity_coherence,
460
+ "emotional_stability": self.emotional_stability,
461
+ "relational_security": self.relational_security,
462
+ "agency_confidence": self.agency_confidence,
463
+ "purpose_clarity": self.purpose_clarity,
464
+ "status_description": self.get_status_description(),
465
+ "crisis_count": self.crisis_count,
466
+ "active_investments": len([i for i in self.investments if i.is_active]),
467
+ "weakest_aspect": state.weakest_aspect(),
468
+ "strongest_aspect": state.strongest_aspect()
469
+ }