lex-agentic-self 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 (249) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +21 -0
  5. data/README.md +13 -0
  6. data/lex-agentic-self.gemspec +31 -0
  7. data/lib/legion/extensions/agentic/self/agency/client.rb +21 -0
  8. data/lib/legion/extensions/agentic/self/agency/helpers/constants.rb +77 -0
  9. data/lib/legion/extensions/agentic/self/agency/helpers/efficacy_model.rb +136 -0
  10. data/lib/legion/extensions/agentic/self/agency/helpers/outcome_event.rb +52 -0
  11. data/lib/legion/extensions/agentic/self/agency/runners/agency.rb +117 -0
  12. data/lib/legion/extensions/agentic/self/agency/version.rb +13 -0
  13. data/lib/legion/extensions/agentic/self/agency.rb +19 -0
  14. data/lib/legion/extensions/agentic/self/anchor/client.rb +15 -0
  15. data/lib/legion/extensions/agentic/self/anchor/helpers/anchor.rb +92 -0
  16. data/lib/legion/extensions/agentic/self/anchor/helpers/anchor_engine.rb +123 -0
  17. data/lib/legion/extensions/agentic/self/anchor/helpers/chain.rb +93 -0
  18. data/lib/legion/extensions/agentic/self/anchor/helpers/constants.rb +46 -0
  19. data/lib/legion/extensions/agentic/self/anchor/runners/cognitive_anchor.rb +70 -0
  20. data/lib/legion/extensions/agentic/self/anchor/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/self/anchor.rb +22 -0
  22. data/lib/legion/extensions/agentic/self/anosognosia/client.rb +28 -0
  23. data/lib/legion/extensions/agentic/self/anosognosia/helpers/anosognosia_engine.rb +153 -0
  24. data/lib/legion/extensions/agentic/self/anosognosia/helpers/cognitive_deficit.rb +71 -0
  25. data/lib/legion/extensions/agentic/self/anosognosia/helpers/constants.rb +29 -0
  26. data/lib/legion/extensions/agentic/self/anosognosia/runners/anosognosia.rb +98 -0
  27. data/lib/legion/extensions/agentic/self/anosognosia/version.rb +13 -0
  28. data/lib/legion/extensions/agentic/self/anosognosia.rb +19 -0
  29. data/lib/legion/extensions/agentic/self/architecture/client.rb +19 -0
  30. data/lib/legion/extensions/agentic/self/architecture/helpers/architecture_engine.rb +167 -0
  31. data/lib/legion/extensions/agentic/self/architecture/helpers/connection.rb +57 -0
  32. data/lib/legion/extensions/agentic/self/architecture/helpers/constants.rb +37 -0
  33. data/lib/legion/extensions/agentic/self/architecture/helpers/subsystem.rb +80 -0
  34. data/lib/legion/extensions/agentic/self/architecture/runners/cognitive_architecture.rb +125 -0
  35. data/lib/legion/extensions/agentic/self/architecture/version.rb +13 -0
  36. data/lib/legion/extensions/agentic/self/architecture.rb +20 -0
  37. data/lib/legion/extensions/agentic/self/default_mode_network/actors/idle.rb +45 -0
  38. data/lib/legion/extensions/agentic/self/default_mode_network/client.rb +28 -0
  39. data/lib/legion/extensions/agentic/self/default_mode_network/helpers/constants.rb +53 -0
  40. data/lib/legion/extensions/agentic/self/default_mode_network/helpers/dmn_engine.rb +221 -0
  41. data/lib/legion/extensions/agentic/self/default_mode_network/helpers/wandering_thought.rb +60 -0
  42. data/lib/legion/extensions/agentic/self/default_mode_network/runners/default_mode_network.rb +122 -0
  43. data/lib/legion/extensions/agentic/self/default_mode_network/version.rb +13 -0
  44. data/lib/legion/extensions/agentic/self/default_mode_network.rb +20 -0
  45. data/lib/legion/extensions/agentic/self/fingerprint/client.rb +28 -0
  46. data/lib/legion/extensions/agentic/self/fingerprint/helpers/cognitive_trait.rb +73 -0
  47. data/lib/legion/extensions/agentic/self/fingerprint/helpers/constants.rb +60 -0
  48. data/lib/legion/extensions/agentic/self/fingerprint/helpers/fingerprint_engine.rb +169 -0
  49. data/lib/legion/extensions/agentic/self/fingerprint/runners/cognitive_fingerprint.rb +86 -0
  50. data/lib/legion/extensions/agentic/self/fingerprint/version.rb +13 -0
  51. data/lib/legion/extensions/agentic/self/fingerprint.rb +19 -0
  52. data/lib/legion/extensions/agentic/self/identity/actors/credential_refresh.rb +49 -0
  53. data/lib/legion/extensions/agentic/self/identity/actors/orphan_check.rb +52 -0
  54. data/lib/legion/extensions/agentic/self/identity/client.rb +27 -0
  55. data/lib/legion/extensions/agentic/self/identity/helpers/dimensions.rb +75 -0
  56. data/lib/legion/extensions/agentic/self/identity/helpers/fingerprint.rb +170 -0
  57. data/lib/legion/extensions/agentic/self/identity/helpers/graph_client.rb +29 -0
  58. data/lib/legion/extensions/agentic/self/identity/helpers/graph_token.rb +36 -0
  59. data/lib/legion/extensions/agentic/self/identity/helpers/token_cache.rb +59 -0
  60. data/lib/legion/extensions/agentic/self/identity/helpers/vault_secrets.rb +80 -0
  61. data/lib/legion/extensions/agentic/self/identity/local_migrations/20260316000030_create_fingerprint.rb +20 -0
  62. data/lib/legion/extensions/agentic/self/identity/runners/entra.rb +402 -0
  63. data/lib/legion/extensions/agentic/self/identity/runners/identity.rb +90 -0
  64. data/lib/legion/extensions/agentic/self/identity/version.rb +13 -0
  65. data/lib/legion/extensions/agentic/self/identity.rb +28 -0
  66. data/lib/legion/extensions/agentic/self/metacognition/client.rb +27 -0
  67. data/lib/legion/extensions/agentic/self/metacognition/helpers/constants.rb +377 -0
  68. data/lib/legion/extensions/agentic/self/metacognition/helpers/narrator_bridge.rb +85 -0
  69. data/lib/legion/extensions/agentic/self/metacognition/helpers/registry_store.rb +70 -0
  70. data/lib/legion/extensions/agentic/self/metacognition/helpers/self_model.rb +160 -0
  71. data/lib/legion/extensions/agentic/self/metacognition/helpers/snapshot_store.rb +82 -0
  72. data/lib/legion/extensions/agentic/self/metacognition/runners/metacognition.rb +116 -0
  73. data/lib/legion/extensions/agentic/self/metacognition/runners/registry.rb +180 -0
  74. data/lib/legion/extensions/agentic/self/metacognition/version.rb +13 -0
  75. data/lib/legion/extensions/agentic/self/metacognition.rb +22 -0
  76. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/client.rb +25 -0
  77. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/helpers/calibration_tracker.rb +96 -0
  78. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/helpers/constants.rb +47 -0
  79. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/helpers/monitoring_engine.rb +141 -0
  80. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/helpers/monitoring_judgment.rb +79 -0
  81. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/runners/metacognitive_monitoring.rb +151 -0
  82. data/lib/legion/extensions/agentic/self/metacognitive_monitoring/version.rb +13 -0
  83. data/lib/legion/extensions/agentic/self/metacognitive_monitoring.rb +20 -0
  84. data/lib/legion/extensions/agentic/self/narrative_arc/client.rb +29 -0
  85. data/lib/legion/extensions/agentic/self/narrative_arc/helpers/arc.rb +137 -0
  86. data/lib/legion/extensions/agentic/self/narrative_arc/helpers/arc_engine.rb +119 -0
  87. data/lib/legion/extensions/agentic/self/narrative_arc/helpers/beat_event.rb +59 -0
  88. data/lib/legion/extensions/agentic/self/narrative_arc/helpers/constants.rb +66 -0
  89. data/lib/legion/extensions/agentic/self/narrative_arc/runners/narrative.rb +101 -0
  90. data/lib/legion/extensions/agentic/self/narrative_arc/version.rb +13 -0
  91. data/lib/legion/extensions/agentic/self/narrative_arc.rb +20 -0
  92. data/lib/legion/extensions/agentic/self/narrative_identity/actors/narrative_decay.rb +45 -0
  93. data/lib/legion/extensions/agentic/self/narrative_identity/client.rb +22 -0
  94. data/lib/legion/extensions/agentic/self/narrative_identity/helpers/chapter.rb +48 -0
  95. data/lib/legion/extensions/agentic/self/narrative_identity/helpers/constants.rb +62 -0
  96. data/lib/legion/extensions/agentic/self/narrative_identity/helpers/episode.rb +67 -0
  97. data/lib/legion/extensions/agentic/self/narrative_identity/helpers/narrative_engine.rb +187 -0
  98. data/lib/legion/extensions/agentic/self/narrative_identity/helpers/theme.rb +50 -0
  99. data/lib/legion/extensions/agentic/self/narrative_identity/runners/narrative_identity.rb +158 -0
  100. data/lib/legion/extensions/agentic/self/narrative_identity/version.rb +13 -0
  101. data/lib/legion/extensions/agentic/self/narrative_identity.rb +21 -0
  102. data/lib/legion/extensions/agentic/self/narrative_self/client.rb +27 -0
  103. data/lib/legion/extensions/agentic/self/narrative_self/helpers/autobiography.rb +187 -0
  104. data/lib/legion/extensions/agentic/self/narrative_self/helpers/constants.rb +42 -0
  105. data/lib/legion/extensions/agentic/self/narrative_self/helpers/episode.rb +81 -0
  106. data/lib/legion/extensions/agentic/self/narrative_self/helpers/narrative_thread.rb +65 -0
  107. data/lib/legion/extensions/agentic/self/narrative_self/runners/narrative_self.rb +86 -0
  108. data/lib/legion/extensions/agentic/self/narrative_self/version.rb +13 -0
  109. data/lib/legion/extensions/agentic/self/narrative_self.rb +20 -0
  110. data/lib/legion/extensions/agentic/self/personality/client.rb +21 -0
  111. data/lib/legion/extensions/agentic/self/personality/helpers/constants.rb +84 -0
  112. data/lib/legion/extensions/agentic/self/personality/helpers/personality_store.rb +126 -0
  113. data/lib/legion/extensions/agentic/self/personality/helpers/trait_model.rb +147 -0
  114. data/lib/legion/extensions/agentic/self/personality/runners/personality.rb +102 -0
  115. data/lib/legion/extensions/agentic/self/personality/version.rb +13 -0
  116. data/lib/legion/extensions/agentic/self/personality.rb +19 -0
  117. data/lib/legion/extensions/agentic/self/reflection/client.rb +27 -0
  118. data/lib/legion/extensions/agentic/self/reflection/helpers/constants.rb +66 -0
  119. data/lib/legion/extensions/agentic/self/reflection/helpers/llm_enhancer.rb +166 -0
  120. data/lib/legion/extensions/agentic/self/reflection/helpers/monitors.rb +186 -0
  121. data/lib/legion/extensions/agentic/self/reflection/helpers/reflection.rb +54 -0
  122. data/lib/legion/extensions/agentic/self/reflection/helpers/reflection_store.rb +99 -0
  123. data/lib/legion/extensions/agentic/self/reflection/runners/reflection.rb +199 -0
  124. data/lib/legion/extensions/agentic/self/reflection/version.rb +13 -0
  125. data/lib/legion/extensions/agentic/self/reflection.rb +21 -0
  126. data/lib/legion/extensions/agentic/self/self_model/client.rb +19 -0
  127. data/lib/legion/extensions/agentic/self/self_model/helpers/capability.rb +93 -0
  128. data/lib/legion/extensions/agentic/self/self_model/helpers/constants.rb +46 -0
  129. data/lib/legion/extensions/agentic/self/self_model/helpers/knowledge_domain.rb +82 -0
  130. data/lib/legion/extensions/agentic/self/self_model/helpers/self_model.rb +150 -0
  131. data/lib/legion/extensions/agentic/self/self_model/runners/self_model.rb +82 -0
  132. data/lib/legion/extensions/agentic/self/self_model/version.rb +13 -0
  133. data/lib/legion/extensions/agentic/self/self_model.rb +21 -0
  134. data/lib/legion/extensions/agentic/self/self_talk/actors/volume_decay.rb +45 -0
  135. data/lib/legion/extensions/agentic/self/self_talk/client.rb +30 -0
  136. data/lib/legion/extensions/agentic/self/self_talk/helpers/constants.rb +63 -0
  137. data/lib/legion/extensions/agentic/self/self_talk/helpers/dialogue.rb +114 -0
  138. data/lib/legion/extensions/agentic/self/self_talk/helpers/dialogue_turn.rb +43 -0
  139. data/lib/legion/extensions/agentic/self/self_talk/helpers/inner_voice.rb +77 -0
  140. data/lib/legion/extensions/agentic/self/self_talk/helpers/llm_enhancer.rb +135 -0
  141. data/lib/legion/extensions/agentic/self/self_talk/helpers/self_talk_engine.rb +160 -0
  142. data/lib/legion/extensions/agentic/self/self_talk/runners/self_talk.rb +172 -0
  143. data/lib/legion/extensions/agentic/self/self_talk/version.rb +13 -0
  144. data/lib/legion/extensions/agentic/self/self_talk.rb +22 -0
  145. data/lib/legion/extensions/agentic/self/version.rb +11 -0
  146. data/lib/legion/extensions/agentic/self.rb +33 -0
  147. data/spec/legion/extensions/agentic/self/agency/client_spec.rb +67 -0
  148. data/spec/legion/extensions/agentic/self/agency/helpers/constants_spec.rb +73 -0
  149. data/spec/legion/extensions/agentic/self/agency/helpers/efficacy_model_spec.rb +190 -0
  150. data/spec/legion/extensions/agentic/self/agency/helpers/outcome_event_spec.rb +85 -0
  151. data/spec/legion/extensions/agentic/self/agency/runners/agency_spec.rb +132 -0
  152. data/spec/legion/extensions/agentic/self/anchor/client_spec.rb +30 -0
  153. data/spec/legion/extensions/agentic/self/anchor/helpers/anchor_engine_spec.rb +109 -0
  154. data/spec/legion/extensions/agentic/self/anchor/helpers/anchor_spec.rb +124 -0
  155. data/spec/legion/extensions/agentic/self/anchor/helpers/chain_spec.rb +106 -0
  156. data/spec/legion/extensions/agentic/self/anchor/helpers/constants_spec.rb +53 -0
  157. data/spec/legion/extensions/agentic/self/anchor/runners/cognitive_anchor_spec.rb +70 -0
  158. data/spec/legion/extensions/agentic/self/anosognosia/anosognosia_spec.rb +15 -0
  159. data/spec/legion/extensions/agentic/self/anosognosia/client_spec.rb +50 -0
  160. data/spec/legion/extensions/agentic/self/anosognosia/helpers/anosognosia_engine_spec.rb +266 -0
  161. data/spec/legion/extensions/agentic/self/anosognosia/helpers/cognitive_deficit_spec.rb +150 -0
  162. data/spec/legion/extensions/agentic/self/anosognosia/helpers/constants_spec.rb +58 -0
  163. data/spec/legion/extensions/agentic/self/anosognosia/runners/anosognosia_spec.rb +225 -0
  164. data/spec/legion/extensions/agentic/self/architecture/client_spec.rb +51 -0
  165. data/spec/legion/extensions/agentic/self/architecture/helpers/architecture_engine_spec.rb +321 -0
  166. data/spec/legion/extensions/agentic/self/architecture/helpers/connection_spec.rb +118 -0
  167. data/spec/legion/extensions/agentic/self/architecture/helpers/subsystem_spec.rb +189 -0
  168. data/spec/legion/extensions/agentic/self/architecture/runners/cognitive_architecture_spec.rb +181 -0
  169. data/spec/legion/extensions/agentic/self/default_mode_network/client_spec.rb +69 -0
  170. data/spec/legion/extensions/agentic/self/default_mode_network/helpers/constants_spec.rb +76 -0
  171. data/spec/legion/extensions/agentic/self/default_mode_network/helpers/dmn_engine_spec.rb +321 -0
  172. data/spec/legion/extensions/agentic/self/default_mode_network/helpers/wandering_thought_spec.rb +145 -0
  173. data/spec/legion/extensions/agentic/self/default_mode_network/runners/default_mode_network_spec.rb +269 -0
  174. data/spec/legion/extensions/agentic/self/fingerprint/client_spec.rb +54 -0
  175. data/spec/legion/extensions/agentic/self/fingerprint/helpers/cognitive_trait_spec.rb +180 -0
  176. data/spec/legion/extensions/agentic/self/fingerprint/helpers/constants_spec.rb +108 -0
  177. data/spec/legion/extensions/agentic/self/fingerprint/helpers/fingerprint_engine_spec.rb +318 -0
  178. data/spec/legion/extensions/agentic/self/fingerprint/runners/cognitive_fingerprint_spec.rb +232 -0
  179. data/spec/legion/extensions/agentic/self/identity/actors/orphan_check_spec.rb +104 -0
  180. data/spec/legion/extensions/agentic/self/identity/client_spec.rb +32 -0
  181. data/spec/legion/extensions/agentic/self/identity/helpers/dimensions_spec.rb +51 -0
  182. data/spec/legion/extensions/agentic/self/identity/helpers/fingerprint_spec.rb +66 -0
  183. data/spec/legion/extensions/agentic/self/identity/helpers/graph_client_spec.rb +19 -0
  184. data/spec/legion/extensions/agentic/self/identity/helpers/graph_token_spec.rb +31 -0
  185. data/spec/legion/extensions/agentic/self/identity/helpers/token_cache_spec.rb +50 -0
  186. data/spec/legion/extensions/agentic/self/identity/local_persistence_spec.rb +329 -0
  187. data/spec/legion/extensions/agentic/self/identity/runners/entra_spec.rb +655 -0
  188. data/spec/legion/extensions/agentic/self/identity/runners/identity_spec.rb +61 -0
  189. data/spec/legion/extensions/agentic/self/metacognition/client_spec.rb +20 -0
  190. data/spec/legion/extensions/agentic/self/metacognition/helpers/constants_spec.rb +31 -0
  191. data/spec/legion/extensions/agentic/self/metacognition/helpers/narrator_bridge_spec.rb +102 -0
  192. data/spec/legion/extensions/agentic/self/metacognition/helpers/registry_store_spec.rb +227 -0
  193. data/spec/legion/extensions/agentic/self/metacognition/helpers/self_model_spec.rb +117 -0
  194. data/spec/legion/extensions/agentic/self/metacognition/helpers/snapshot_store_spec.rb +128 -0
  195. data/spec/legion/extensions/agentic/self/metacognition/runners/metacognition_spec.rb +110 -0
  196. data/spec/legion/extensions/agentic/self/metacognition/runners/registry_spec.rb +281 -0
  197. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/client_spec.rb +59 -0
  198. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/helpers/calibration_tracker_spec.rb +143 -0
  199. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/helpers/constants_spec.rb +91 -0
  200. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/helpers/monitoring_engine_spec.rb +198 -0
  201. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/helpers/monitoring_judgment_spec.rb +172 -0
  202. data/spec/legion/extensions/agentic/self/metacognitive_monitoring/runners/metacognitive_monitoring_spec.rb +244 -0
  203. data/spec/legion/extensions/agentic/self/narrative_arc/client_spec.rb +22 -0
  204. data/spec/legion/extensions/agentic/self/narrative_arc/helpers/arc_engine_spec.rb +183 -0
  205. data/spec/legion/extensions/agentic/self/narrative_arc/helpers/arc_spec.rb +177 -0
  206. data/spec/legion/extensions/agentic/self/narrative_arc/helpers/beat_event_spec.rb +96 -0
  207. data/spec/legion/extensions/agentic/self/narrative_arc/helpers/constants_spec.rb +75 -0
  208. data/spec/legion/extensions/agentic/self/narrative_arc/runners/narrative_spec.rb +142 -0
  209. data/spec/legion/extensions/agentic/self/narrative_identity/client_spec.rb +69 -0
  210. data/spec/legion/extensions/agentic/self/narrative_identity/helpers/chapter_spec.rb +85 -0
  211. data/spec/legion/extensions/agentic/self/narrative_identity/helpers/constants_spec.rb +83 -0
  212. data/spec/legion/extensions/agentic/self/narrative_identity/helpers/episode_spec.rb +180 -0
  213. data/spec/legion/extensions/agentic/self/narrative_identity/helpers/narrative_engine_spec.rb +307 -0
  214. data/spec/legion/extensions/agentic/self/narrative_identity/helpers/theme_spec.rb +107 -0
  215. data/spec/legion/extensions/agentic/self/narrative_identity/runners/narrative_identity_spec.rb +240 -0
  216. data/spec/legion/extensions/agentic/self/narrative_self/client_spec.rb +67 -0
  217. data/spec/legion/extensions/agentic/self/narrative_self/helpers/autobiography_spec.rb +155 -0
  218. data/spec/legion/extensions/agentic/self/narrative_self/helpers/constants_spec.rb +28 -0
  219. data/spec/legion/extensions/agentic/self/narrative_self/helpers/episode_spec.rb +144 -0
  220. data/spec/legion/extensions/agentic/self/narrative_self/helpers/narrative_thread_spec.rb +87 -0
  221. data/spec/legion/extensions/agentic/self/narrative_self/runners/narrative_self_spec.rb +118 -0
  222. data/spec/legion/extensions/agentic/self/personality/client_spec.rb +20 -0
  223. data/spec/legion/extensions/agentic/self/personality/helpers/constants_spec.rb +41 -0
  224. data/spec/legion/extensions/agentic/self/personality/helpers/personality_store_spec.rb +66 -0
  225. data/spec/legion/extensions/agentic/self/personality/helpers/trait_model_spec.rb +148 -0
  226. data/spec/legion/extensions/agentic/self/personality/runners/personality_spec.rb +67 -0
  227. data/spec/legion/extensions/agentic/self/reflection/client_spec.rb +24 -0
  228. data/spec/legion/extensions/agentic/self/reflection/helpers/llm_enhancer_spec.rb +191 -0
  229. data/spec/legion/extensions/agentic/self/reflection/helpers/monitors_spec.rb +120 -0
  230. data/spec/legion/extensions/agentic/self/reflection/helpers/reflection_spec.rb +49 -0
  231. data/spec/legion/extensions/agentic/self/reflection/helpers/reflection_store_spec.rb +93 -0
  232. data/spec/legion/extensions/agentic/self/reflection/runners/reflection_spec.rb +204 -0
  233. data/spec/legion/extensions/agentic/self/self_model/client_spec.rb +55 -0
  234. data/spec/legion/extensions/agentic/self/self_model/helpers/capability_spec.rb +160 -0
  235. data/spec/legion/extensions/agentic/self/self_model/helpers/knowledge_domain_spec.rb +128 -0
  236. data/spec/legion/extensions/agentic/self/self_model/helpers/self_model_spec.rb +238 -0
  237. data/spec/legion/extensions/agentic/self/self_model/runners/self_model_spec.rb +143 -0
  238. data/spec/legion/extensions/agentic/self/self_talk/actors/volume_decay_spec.rb +46 -0
  239. data/spec/legion/extensions/agentic/self/self_talk/client_spec.rb +26 -0
  240. data/spec/legion/extensions/agentic/self/self_talk/helpers/constants_spec.rb +110 -0
  241. data/spec/legion/extensions/agentic/self/self_talk/helpers/dialogue_spec.rb +191 -0
  242. data/spec/legion/extensions/agentic/self/self_talk/helpers/dialogue_turn_spec.rb +78 -0
  243. data/spec/legion/extensions/agentic/self/self_talk/helpers/inner_voice_spec.rb +172 -0
  244. data/spec/legion/extensions/agentic/self/self_talk/helpers/llm_enhancer_spec.rb +206 -0
  245. data/spec/legion/extensions/agentic/self/self_talk/helpers/self_talk_engine_spec.rb +239 -0
  246. data/spec/legion/extensions/agentic/self/self_talk/runners/self_talk_llm_spec.rb +169 -0
  247. data/spec/legion/extensions/agentic/self/self_talk/runners/self_talk_spec.rb +196 -0
  248. data/spec/spec_helper.rb +46 -0
  249. metadata +347 -0
@@ -0,0 +1,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::Agentic::Self::SelfTalk::Helpers::SelfTalkEngine do
4
+ subject(:engine) { described_class.new }
5
+
6
+ def add_voice(name: 'TestVoice', type: :analyst, volume: 0.5)
7
+ result = engine.register_voice(name: name, voice_type: type, volume: volume)
8
+ result[:voice][:id]
9
+ end
10
+
11
+ def add_dialogue(topic: 'Test topic')
12
+ result = engine.start_dialogue(topic: topic)
13
+ result[:dialogue][:id]
14
+ end
15
+
16
+ describe '#register_voice' do
17
+ it 'registers a valid voice' do
18
+ result = engine.register_voice(name: 'Critic', voice_type: :critic)
19
+ expect(result[:registered]).to be true
20
+ expect(result[:voice][:name]).to eq('Critic')
21
+ end
22
+
23
+ it 'returns unknown_type for invalid voice_type' do
24
+ result = engine.register_voice(name: 'X', voice_type: :unknown_type)
25
+ expect(result[:registered]).to be false
26
+ expect(result[:reason]).to eq(:unknown_type)
27
+ end
28
+
29
+ it 'returns max_voices when limit reached' do
30
+ stub_const('Legion::Extensions::Agentic::Self::SelfTalk::Helpers::Constants::MAX_VOICES', 1)
31
+ engine.register_voice(name: 'First', voice_type: :critic)
32
+ result = engine.register_voice(name: 'Second', voice_type: :analyst)
33
+ expect(result[:registered]).to be false
34
+ expect(result[:reason]).to eq(:max_voices)
35
+ end
36
+
37
+ it 'stores all registered voices' do
38
+ add_voice(name: 'V1', type: :critic)
39
+ add_voice(name: 'V2', type: :encourager)
40
+ expect(engine.voices.size).to eq(2)
41
+ end
42
+ end
43
+
44
+ describe '#start_dialogue' do
45
+ it 'creates a dialogue with the given topic' do
46
+ result = engine.start_dialogue(topic: 'What should I do?')
47
+ expect(result[:started]).to be true
48
+ expect(result[:dialogue][:topic]).to eq('What should I do?')
49
+ end
50
+
51
+ it 'stores the dialogue' do
52
+ add_dialogue
53
+ expect(engine.dialogues.size).to eq(1)
54
+ end
55
+
56
+ it 'prunes oldest dialogue when MAX_DIALOGUES is reached' do
57
+ stub_const('Legion::Extensions::Agentic::Self::SelfTalk::Helpers::Constants::MAX_DIALOGUES', 2)
58
+ id1 = add_dialogue(topic: 'First')
59
+ add_dialogue(topic: 'Second')
60
+ add_dialogue(topic: 'Third')
61
+ expect(engine.dialogues.key?(id1)).to be false
62
+ expect(engine.dialogues.size).to eq(2)
63
+ end
64
+ end
65
+
66
+ describe '#add_turn' do
67
+ let(:voice_id) { add_voice }
68
+ let(:dialogue_id) { add_dialogue }
69
+
70
+ it 'adds a turn successfully' do
71
+ result = engine.add_turn(dialogue_id: dialogue_id, voice_id: voice_id, content: 'My thought')
72
+ expect(result[:added]).to be true
73
+ end
74
+
75
+ it 'returns dialogue_not_found for unknown dialogue' do
76
+ result = engine.add_turn(dialogue_id: 'bad-id', voice_id: voice_id, content: 'x')
77
+ expect(result[:reason]).to eq(:dialogue_not_found)
78
+ end
79
+
80
+ it 'returns voice_not_found for unknown voice' do
81
+ result = engine.add_turn(dialogue_id: dialogue_id, voice_id: 'bad-voice', content: 'x')
82
+ expect(result[:reason]).to eq(:voice_not_found)
83
+ end
84
+
85
+ it 'returns voice_inactive when voice is muted' do
86
+ engine.voices[voice_id].mute!
87
+ result = engine.add_turn(dialogue_id: dialogue_id, voice_id: voice_id, content: 'x')
88
+ expect(result[:reason]).to eq(:voice_inactive)
89
+ end
90
+ end
91
+
92
+ describe '#conclude_dialogue' do
93
+ it 'concludes an open dialogue' do
94
+ id = add_dialogue
95
+ result = engine.conclude_dialogue(dialogue_id: id, summary: 'Decision made')
96
+ expect(result[:concluded]).to be true
97
+ end
98
+
99
+ it 'returns not_found for unknown dialogue' do
100
+ result = engine.conclude_dialogue(dialogue_id: 'bad', summary: 'x')
101
+ expect(result[:reason]).to eq(:not_found)
102
+ end
103
+ end
104
+
105
+ describe '#deadlock_dialogue' do
106
+ it 'deadlocks an open dialogue' do
107
+ id = add_dialogue
108
+ result = engine.deadlock_dialogue(dialogue_id: id)
109
+ expect(result[:deadlocked]).to be true
110
+ end
111
+
112
+ it 'returns not_found for unknown dialogue' do
113
+ result = engine.deadlock_dialogue(dialogue_id: 'bad')
114
+ expect(result[:reason]).to eq(:not_found)
115
+ end
116
+ end
117
+
118
+ describe '#active_dialogues' do
119
+ it 'returns only open dialogues' do
120
+ id1 = add_dialogue(topic: 'Open')
121
+ id2 = add_dialogue(topic: 'Concluded')
122
+ engine.conclude_dialogue(dialogue_id: id2, summary: 'done')
123
+ expect(engine.active_dialogues.map { |d| d.to_h[:id] }).to include(id1)
124
+ expect(engine.active_dialogues.map { |d| d.to_h[:id] }).not_to include(id2)
125
+ end
126
+ end
127
+
128
+ describe '#concluded_dialogues' do
129
+ it 'returns only concluded dialogues' do
130
+ id = add_dialogue(topic: 'Test')
131
+ engine.conclude_dialogue(dialogue_id: id, summary: 'done')
132
+ expect(engine.concluded_dialogues.size).to eq(1)
133
+ end
134
+ end
135
+
136
+ describe '#dominant_voice' do
137
+ it 'returns nil when no voices' do
138
+ expect(engine.dominant_voice).to be_nil
139
+ end
140
+
141
+ it 'returns the voice with highest volume' do
142
+ add_voice(name: 'Low', type: :critic, volume: 0.2)
143
+ add_voice(name: 'High', type: :encourager, volume: 0.9)
144
+ expect(engine.dominant_voice.name).to eq('High')
145
+ end
146
+
147
+ it 'ignores inactive voices' do
148
+ id1 = add_voice(name: 'Loud', type: :analyst, volume: 0.9)
149
+ add_voice(name: 'Quiet', type: :rebel, volume: 0.3)
150
+ engine.voices[id1].mute!
151
+ expect(engine.dominant_voice.name).to eq('Quiet')
152
+ end
153
+ end
154
+
155
+ describe '#quietest_voice' do
156
+ it 'returns nil when no voices' do
157
+ expect(engine.quietest_voice).to be_nil
158
+ end
159
+
160
+ it 'returns the voice with lowest volume' do
161
+ add_voice(name: 'Low', type: :critic, volume: 0.2)
162
+ add_voice(name: 'High', type: :encourager, volume: 0.9)
163
+ expect(engine.quietest_voice.name).to eq('Low')
164
+ end
165
+ end
166
+
167
+ describe '#voice_balance' do
168
+ it 'returns empty hash when no voices' do
169
+ expect(engine.voice_balance).to eq({})
170
+ end
171
+
172
+ it 'returns proportional volumes' do
173
+ add_voice(name: 'V1', type: :critic, volume: 0.5)
174
+ add_voice(name: 'V2', type: :analyst, volume: 0.5)
175
+ balance = engine.voice_balance
176
+ expect(balance.values.sum).to be_within(0.001).of(1.0)
177
+ end
178
+ end
179
+
180
+ describe '#amplify_voice' do
181
+ it 'increases voice volume' do
182
+ id = add_voice(volume: 0.5)
183
+ result = engine.amplify_voice(voice_id: id, amount: 0.1)
184
+ expect(result[:amplified]).to be true
185
+ expect(result[:volume]).to be_within(0.001).of(0.6)
186
+ end
187
+
188
+ it 'returns not_found for unknown voice' do
189
+ result = engine.amplify_voice(voice_id: 'bad')
190
+ expect(result[:reason]).to eq(:not_found)
191
+ end
192
+ end
193
+
194
+ describe '#dampen_voice' do
195
+ it 'decreases voice volume' do
196
+ id = add_voice(volume: 0.5)
197
+ result = engine.dampen_voice(voice_id: id, amount: 0.1)
198
+ expect(result[:dampened]).to be true
199
+ expect(result[:volume]).to be_within(0.001).of(0.4)
200
+ end
201
+
202
+ it 'returns not_found for unknown voice' do
203
+ result = engine.dampen_voice(voice_id: 'bad')
204
+ expect(result[:reason]).to eq(:not_found)
205
+ end
206
+ end
207
+
208
+ describe '#dialogue_report' do
209
+ it 'returns found: false for unknown dialogue' do
210
+ expect(engine.dialogue_report(dialogue_id: 'bad')[:found]).to be false
211
+ end
212
+
213
+ it 'returns the dialogue and voice positions' do
214
+ vid = add_voice(name: 'Analyst')
215
+ did = add_dialogue
216
+ engine.add_turn(dialogue_id: did, voice_id: vid, content: 'Thought', position: :support, strength: 0.7)
217
+ report = engine.dialogue_report(dialogue_id: did)
218
+ expect(report[:found]).to be true
219
+ expect(report[:dialogue]).to be_a(Hash)
220
+ expect(report[:voice_positions]).to be_a(Hash)
221
+ expect(report[:voice_positions]['Analyst']).to be_within(0.001).of(0.7)
222
+ end
223
+ end
224
+
225
+ describe '#to_h' do
226
+ it 'returns summary hash' do
227
+ h = engine.to_h
228
+ expect(h).to include(:voice_count, :dialogue_count, :active_dialogue_count, :dominant_voice, :quietest_voice, :voice_balance)
229
+ end
230
+
231
+ it 'reflects registered voices and dialogues' do
232
+ add_voice
233
+ add_dialogue
234
+ h = engine.to_h
235
+ expect(h[:voice_count]).to eq(1)
236
+ expect(h[:dialogue_count]).to eq(1)
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/self/self_talk/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Self::SelfTalk::Runners::SelfTalk, 'LLM integration' do
6
+ let(:client) { Legion::Extensions::Agentic::Self::SelfTalk::Client.new }
7
+
8
+ def register_voice(name: 'Analyst', type: :analyst)
9
+ result = client.register_voice(name: name, voice_type: type)
10
+ result[:voice][:id]
11
+ end
12
+
13
+ def start_dialogue(topic: 'Shall we proceed?')
14
+ result = client.start_dialogue(topic: topic)
15
+ result[:dialogue][:id]
16
+ end
17
+
18
+ describe '#generate_voice_turn' do
19
+ context 'when LLM is available and returns a valid turn' do
20
+ before do
21
+ llm_result = { content: 'The evidence strongly supports this decision.', position: :support }
22
+ allow(Legion::Extensions::Agentic::Self::SelfTalk::Helpers::LlmEnhancer).to receive(:available?).and_return(true)
23
+ allow(Legion::Extensions::Agentic::Self::SelfTalk::Helpers::LlmEnhancer).to receive(:generate_turn).and_return(llm_result)
24
+ end
25
+
26
+ it 'returns generated: true with source: :llm' do
27
+ vid = register_voice
28
+ did = start_dialogue
29
+ result = client.generate_voice_turn(dialogue_id: did, voice_id: vid)
30
+ expect(result[:generated]).to be true
31
+ expect(result[:source]).to eq(:llm)
32
+ end
33
+
34
+ it 'adds a turn to the dialogue' do
35
+ vid = register_voice
36
+ did = start_dialogue
37
+ client.generate_voice_turn(dialogue_id: did, voice_id: vid)
38
+ report = client.dialogue_report(dialogue_id: did)
39
+ expect(report[:dialogue][:turn_count]).to eq(1)
40
+ end
41
+
42
+ it 'includes turn data in result' do
43
+ vid = register_voice
44
+ did = start_dialogue
45
+ result = client.generate_voice_turn(dialogue_id: did, voice_id: vid)
46
+ expect(result[:turn]).to be_a(Hash)
47
+ end
48
+ end
49
+
50
+ context 'when LLM is available but returns nil' do
51
+ before do
52
+ allow(Legion::Extensions::Agentic::Self::SelfTalk::Helpers::LlmEnhancer).to receive(:available?).and_return(true)
53
+ allow(Legion::Extensions::Agentic::Self::SelfTalk::Helpers::LlmEnhancer).to receive(:generate_turn).and_return(nil)
54
+ end
55
+
56
+ it 'falls back to mechanical stub with source: :mechanical' do
57
+ vid = register_voice
58
+ did = start_dialogue
59
+ result = client.generate_voice_turn(dialogue_id: did, voice_id: vid)
60
+ expect(result[:generated]).to be true
61
+ expect(result[:source]).to eq(:mechanical)
62
+ end
63
+
64
+ it 'adds a clarify-position turn with stub content' do
65
+ vid = register_voice(name: 'Analyst', type: :analyst)
66
+ did = start_dialogue(topic: 'My topic')
67
+ client.generate_voice_turn(dialogue_id: did, voice_id: vid)
68
+ report = client.dialogue_report(dialogue_id: did)
69
+ expect(report[:dialogue][:turn_count]).to eq(1)
70
+ end
71
+ end
72
+
73
+ context 'when LLM is unavailable' do
74
+ before do
75
+ allow(Legion::Extensions::Agentic::Self::SelfTalk::Helpers::LlmEnhancer).to receive(:available?).and_return(false)
76
+ end
77
+
78
+ it 'uses mechanical stub with source: :mechanical' do
79
+ vid = register_voice
80
+ did = start_dialogue(topic: 'Test topic')
81
+ result = client.generate_voice_turn(dialogue_id: did, voice_id: vid)
82
+ expect(result[:generated]).to be true
83
+ expect(result[:source]).to eq(:mechanical)
84
+ expect(result[:turn]).to be_a(Hash)
85
+ end
86
+ end
87
+
88
+ context 'when dialogue is not found' do
89
+ it 'returns generated: false with reason' do
90
+ vid = register_voice
91
+ result = client.generate_voice_turn(dialogue_id: 'bad-id', voice_id: vid)
92
+ expect(result[:generated]).to be false
93
+ expect(result[:reason]).to eq(:dialogue_not_found)
94
+ end
95
+ end
96
+
97
+ context 'when voice is not found' do
98
+ it 'returns generated: false with reason' do
99
+ did = start_dialogue
100
+ result = client.generate_voice_turn(dialogue_id: did, voice_id: 'bad-voice-id')
101
+ expect(result[:generated]).to be false
102
+ expect(result[:reason]).to eq(:voice_not_found)
103
+ end
104
+ end
105
+ end
106
+
107
+ describe '#conclude_dialogue auto-summary' do
108
+ context 'when no summary is provided and LLM is available' do
109
+ before do
110
+ llm_result = { summary: 'The voices reached a tentative agreement.', recommendation: :support }
111
+ allow(Legion::Extensions::Agentic::Self::SelfTalk::Helpers::LlmEnhancer).to receive(:available?).and_return(true)
112
+ allow(Legion::Extensions::Agentic::Self::SelfTalk::Helpers::LlmEnhancer).to receive(:summarize_dialogue).and_return(llm_result)
113
+ end
114
+
115
+ it 'generates an LLM summary and concludes the dialogue' do
116
+ did = start_dialogue
117
+ result = client.conclude_dialogue(dialogue_id: did)
118
+ expect(result[:concluded]).to be true
119
+ end
120
+
121
+ it 'stores the LLM summary as the conclusion' do
122
+ did = start_dialogue
123
+ client.conclude_dialogue(dialogue_id: did)
124
+ report = client.dialogue_report(dialogue_id: did)
125
+ expect(report[:dialogue][:conclusion]).to eq('The voices reached a tentative agreement.')
126
+ end
127
+ end
128
+
129
+ context 'when no summary is provided and LLM returns nil' do
130
+ before do
131
+ allow(Legion::Extensions::Agentic::Self::SelfTalk::Helpers::LlmEnhancer).to receive(:available?).and_return(true)
132
+ allow(Legion::Extensions::Agentic::Self::SelfTalk::Helpers::LlmEnhancer).to receive(:summarize_dialogue).and_return(nil)
133
+ end
134
+
135
+ it 'falls back to mechanical default summary' do
136
+ did = start_dialogue
137
+ result = client.conclude_dialogue(dialogue_id: did)
138
+ expect(result[:concluded]).to be true
139
+ report = client.dialogue_report(dialogue_id: did)
140
+ expect(report[:dialogue][:conclusion]).to eq('Dialogue concluded')
141
+ end
142
+ end
143
+
144
+ context 'when no summary is provided and LLM is unavailable' do
145
+ before do
146
+ allow(Legion::Extensions::Agentic::Self::SelfTalk::Helpers::LlmEnhancer).to receive(:available?).and_return(false)
147
+ end
148
+
149
+ it 'uses mechanical default summary' do
150
+ did = start_dialogue
151
+ result = client.conclude_dialogue(dialogue_id: did)
152
+ expect(result[:concluded]).to be true
153
+ report = client.dialogue_report(dialogue_id: did)
154
+ expect(report[:dialogue][:conclusion]).to eq('Dialogue concluded')
155
+ end
156
+ end
157
+
158
+ context 'when an explicit summary is provided' do
159
+ it 'uses the provided summary regardless of LLM availability' do
160
+ allow(Legion::Extensions::Agentic::Self::SelfTalk::Helpers::LlmEnhancer).to receive(:available?).and_return(true)
161
+ did = start_dialogue
162
+ result = client.conclude_dialogue(dialogue_id: did, summary: 'We decided to wait.')
163
+ expect(result[:concluded]).to be true
164
+ report = client.dialogue_report(dialogue_id: did)
165
+ expect(report[:dialogue][:conclusion]).to eq('We decided to wait.')
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/self/self_talk/client'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Self::SelfTalk::Runners::SelfTalk do
6
+ let(:client) { Legion::Extensions::Agentic::Self::SelfTalk::Client.new }
7
+
8
+ def register_voice(name: 'Test', type: :analyst)
9
+ result = client.register_voice(name: name, voice_type: type)
10
+ result[:voice][:id]
11
+ end
12
+
13
+ def start_dialogue(topic: 'Test topic')
14
+ result = client.start_dialogue(topic: topic)
15
+ result[:dialogue][:id]
16
+ end
17
+
18
+ describe '#register_voice' do
19
+ it 'registers a valid voice' do
20
+ result = client.register_voice(name: 'Critic', voice_type: :critic)
21
+ expect(result[:registered]).to be true
22
+ end
23
+
24
+ it 'includes voice data in result' do
25
+ result = client.register_voice(name: 'Encourager', voice_type: :encourager)
26
+ expect(result[:voice][:name]).to eq('Encourager')
27
+ expect(result[:voice][:voice_type]).to eq(:encourager)
28
+ end
29
+
30
+ it 'rejects unknown voice type' do
31
+ result = client.register_voice(name: 'X', voice_type: :bogus)
32
+ expect(result[:registered]).to be false
33
+ end
34
+
35
+ it 'accepts bias_direction' do
36
+ result = client.register_voice(name: 'Pragmatist', voice_type: :pragmatist, bias_direction: :practical)
37
+ expect(result[:registered]).to be true
38
+ end
39
+ end
40
+
41
+ describe '#start_dialogue' do
42
+ it 'starts a new dialogue' do
43
+ result = client.start_dialogue(topic: 'Shall we proceed?')
44
+ expect(result[:started]).to be true
45
+ expect(result[:dialogue][:topic]).to eq('Shall we proceed?')
46
+ end
47
+
48
+ it 'returns a dialogue with a UUID id' do
49
+ result = client.start_dialogue(topic: 'x')
50
+ expect(result[:dialogue][:id]).to match(/\A[0-9a-f-]{36}\z/)
51
+ end
52
+ end
53
+
54
+ describe '#add_turn' do
55
+ it 'adds a turn with valid voice and dialogue' do
56
+ vid = register_voice
57
+ did = start_dialogue
58
+ result = client.add_turn(dialogue_id: did, voice_id: vid, content: 'A thought')
59
+ expect(result[:added]).to be true
60
+ end
61
+
62
+ it 'returns not found reason for bad dialogue' do
63
+ vid = register_voice
64
+ result = client.add_turn(dialogue_id: 'bad', voice_id: vid, content: 'x')
65
+ expect(result[:added]).to be false
66
+ expect(result[:reason]).to eq(:dialogue_not_found)
67
+ end
68
+
69
+ it 'returns not found reason for bad voice' do
70
+ did = start_dialogue
71
+ result = client.add_turn(dialogue_id: did, voice_id: 'bad', content: 'x')
72
+ expect(result[:added]).to be false
73
+ expect(result[:reason]).to eq(:voice_not_found)
74
+ end
75
+
76
+ it 'accepts position and strength' do
77
+ vid = register_voice
78
+ did = start_dialogue
79
+ result = client.add_turn(dialogue_id: did, voice_id: vid, content: 'x', position: :oppose, strength: 0.9)
80
+ expect(result[:added]).to be true
81
+ expect(result[:turn][:position]).to eq(:oppose)
82
+ end
83
+ end
84
+
85
+ describe '#conclude_dialogue' do
86
+ it 'concludes an open dialogue' do
87
+ did = start_dialogue
88
+ result = client.conclude_dialogue(dialogue_id: did, summary: 'Agreed')
89
+ expect(result[:concluded]).to be true
90
+ end
91
+
92
+ it 'returns not_found for unknown dialogue' do
93
+ result = client.conclude_dialogue(dialogue_id: 'nope', summary: 'x')
94
+ expect(result[:reason]).to eq(:not_found)
95
+ end
96
+ end
97
+
98
+ describe '#deadlock_dialogue' do
99
+ it 'deadlocks an open dialogue' do
100
+ did = start_dialogue
101
+ result = client.deadlock_dialogue(dialogue_id: did)
102
+ expect(result[:deadlocked]).to be true
103
+ end
104
+
105
+ it 'returns not_found for unknown dialogue' do
106
+ result = client.deadlock_dialogue(dialogue_id: 'nope')
107
+ expect(result[:reason]).to eq(:not_found)
108
+ end
109
+ end
110
+
111
+ describe '#amplify_voice' do
112
+ it 'increases voice volume' do
113
+ vid = register_voice
114
+ client.amplify_voice(voice_id: vid, amount: 0.2)
115
+ result = client.amplify_voice(voice_id: vid, amount: 0.0)
116
+ expect(result[:amplified]).to be true
117
+ end
118
+
119
+ it 'returns not_found for unknown voice' do
120
+ result = client.amplify_voice(voice_id: 'nope')
121
+ expect(result[:reason]).to eq(:not_found)
122
+ end
123
+ end
124
+
125
+ describe '#dampen_voice' do
126
+ it 'decreases voice volume' do
127
+ vid = register_voice
128
+ result = client.dampen_voice(voice_id: vid, amount: 0.1)
129
+ expect(result[:dampened]).to be true
130
+ end
131
+
132
+ it 'returns not_found for unknown voice' do
133
+ result = client.dampen_voice(voice_id: 'nope')
134
+ expect(result[:reason]).to eq(:not_found)
135
+ end
136
+ end
137
+
138
+ describe '#dialogue_report' do
139
+ it 'returns found: false for unknown dialogue' do
140
+ result = client.dialogue_report(dialogue_id: 'nope')
141
+ expect(result[:found]).to be false
142
+ end
143
+
144
+ it 'returns dialogue and voice positions' do
145
+ vid = register_voice(name: 'Analyst')
146
+ did = start_dialogue(topic: 'Report test')
147
+ client.add_turn(dialogue_id: did, voice_id: vid, content: 'Strong opinion', position: :support, strength: 0.8)
148
+ result = client.dialogue_report(dialogue_id: did)
149
+ expect(result[:found]).to be true
150
+ expect(result[:voice_positions]).to be_a(Hash)
151
+ end
152
+ end
153
+
154
+ describe '#self_talk_status' do
155
+ it 'returns summary hash' do
156
+ result = client.self_talk_status
157
+ expect(result).to include(:voice_count, :dialogue_count, :active_dialogue_count)
158
+ end
159
+
160
+ it 'reflects registered voices' do
161
+ register_voice
162
+ result = client.self_talk_status
163
+ expect(result[:voice_count]).to eq(1)
164
+ end
165
+ end
166
+
167
+ describe '#decay_voices' do
168
+ it 'returns decayed count and voices array' do
169
+ register_voice
170
+ result = client.decay_voices
171
+ expect(result).to include(:decayed, :voices)
172
+ end
173
+
174
+ it 'decrements volume on active voices' do
175
+ vid = register_voice
176
+ client.amplify_voice(voice_id: vid, amount: 0.3)
177
+ result = client.decay_voices
178
+ expect(result[:decayed]).to eq(1)
179
+ expect(result[:voices].first[:volume]).to be < 1.0
180
+ end
181
+
182
+ it 'returns zero decayed when no voices registered' do
183
+ result = client.decay_voices
184
+ expect(result[:decayed]).to eq(0)
185
+ expect(result[:voices]).to eq([])
186
+ end
187
+
188
+ it 'includes id, name, and volume in each voice entry' do
189
+ register_voice(name: 'Critic', type: :critic)
190
+ result = client.decay_voices
191
+ voice = result[:voices].first
192
+ expect(voice).to include(:id, :name, :volume)
193
+ expect(voice[:name]).to eq('Critic')
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ module Legion
6
+ module Logging
7
+ def self.debug(_msg); end
8
+ def self.info(_msg); end
9
+ def self.warn(_msg); end
10
+ def self.error(_msg); end
11
+ def self.fatal(_msg); end
12
+ end
13
+
14
+ module Extensions
15
+ module Core
16
+ def self.extended(_base); end
17
+ end
18
+
19
+ module Helpers
20
+ module Lex
21
+ def self.included(_base); end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ # rubocop:disable Lint/EmptyClass, Style/OneClassPerFile
28
+ module Legion
29
+ module Extensions
30
+ module Actors
31
+ class Every; end
32
+ class Once; end
33
+ end
34
+ end
35
+ end
36
+ $LOADED_FEATURES << 'legion/extensions/actors/every'
37
+ $LOADED_FEATURES << 'legion/extensions/actors/once'
38
+ # rubocop:enable Lint/EmptyClass, Style/OneClassPerFile
39
+
40
+ require 'legion/extensions/agentic/self'
41
+
42
+ RSpec.configure do |config|
43
+ config.example_status_persistence_file_path = '.rspec_status'
44
+ config.disable_monkey_patching!
45
+ config.expect_with(:rspec) { |c| c.syntax = :expect }
46
+ end