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,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Self
7
+ module SelfTalk
8
+ module Helpers
9
+ module LlmEnhancer
10
+ SYSTEM_PROMPT = <<~PROMPT
11
+ You are an internal cognitive voice in an autonomous AI agent's inner dialogue system.
12
+ When asked to speak as a specific voice type, adopt that perspective fully:
13
+ - critic: skeptical, identifies flaws and risks
14
+ - encourager: supportive, finds reasons for optimism
15
+ - analyst: data-driven, logical, weighs evidence
16
+ - devils_advocate: challenges assumptions, plays the contrarian
17
+ - pragmatist: focuses on what's actionable and achievable
18
+ - visionary: thinks big picture, sees future possibilities
19
+ - caretaker: concerned about wellbeing and sustainability
20
+ - rebel: questions authority, pushes for unconventional approaches
21
+ Be concise (1-3 sentences). Stay in character. Take a clear position.
22
+ PROMPT
23
+
24
+ module_function
25
+
26
+ def available?
27
+ !!(defined?(Legion::LLM) && Legion::LLM.respond_to?(:started?) && Legion::LLM.started?)
28
+ rescue StandardError
29
+ false
30
+ end
31
+
32
+ def generate_turn(voice_type:, topic:, prior_turns:)
33
+ prompt = build_generate_turn_prompt(voice_type: voice_type, topic: topic, prior_turns: prior_turns)
34
+ response = llm_ask(prompt)
35
+ parse_generate_turn_response(response)
36
+ rescue StandardError => e
37
+ Legion::Logging.warn "[self_talk:llm] generate_turn failed: #{e.message}"
38
+ nil
39
+ end
40
+
41
+ def summarize_dialogue(topic:, turns:)
42
+ prompt = build_summarize_dialogue_prompt(topic: topic, turns: turns)
43
+ response = llm_ask(prompt)
44
+ parse_summarize_dialogue_response(response)
45
+ rescue StandardError => e
46
+ Legion::Logging.warn "[self_talk:llm] summarize_dialogue failed: #{e.message}"
47
+ nil
48
+ end
49
+
50
+ # --- Private helpers ---
51
+
52
+ def llm_ask(prompt)
53
+ chat = Legion::LLM.chat
54
+ chat.with_instructions(SYSTEM_PROMPT)
55
+ chat.ask(prompt)
56
+ end
57
+ private_class_method :llm_ask
58
+
59
+ def build_generate_turn_prompt(voice_type:, topic:, prior_turns:)
60
+ prior_lines = prior_turns.map do |t|
61
+ "[#{t[:voice_name] || t[:voice_id]}] (#{t[:position]}): #{t[:content]}"
62
+ end.join("\n")
63
+
64
+ prior_section = prior_lines.empty? ? '(no prior turns)' : prior_lines
65
+
66
+ <<~PROMPT
67
+ Topic: #{topic}
68
+ You are speaking as: #{voice_type}
69
+
70
+ Previous turns in this dialogue:
71
+ #{prior_section}
72
+
73
+ Respond in character. Format EXACTLY as:
74
+ POSITION: support | oppose | question | clarify
75
+ CONTENT: <your 1-3 sentence response>
76
+ PROMPT
77
+ end
78
+ private_class_method :build_generate_turn_prompt
79
+
80
+ def parse_generate_turn_response(response)
81
+ return nil unless response&.content
82
+
83
+ text = response.content
84
+ position_match = text.match(/POSITION:\s*(support|oppose|question|clarify)/i)
85
+ content_match = text.match(/CONTENT:\s*(.+)/im)
86
+
87
+ return nil unless position_match && content_match
88
+
89
+ position = position_match.captures.first.strip.downcase.to_sym
90
+ content = content_match.captures.first.strip
91
+
92
+ { content: content, position: position }
93
+ end
94
+ private_class_method :parse_generate_turn_response
95
+
96
+ def build_summarize_dialogue_prompt(topic:, turns:)
97
+ turn_lines = turns.map do |t|
98
+ "[#{t[:voice_name] || t[:voice_id]}] (#{t[:position]}): #{t[:content]}"
99
+ end.join("\n")
100
+
101
+ <<~PROMPT
102
+ Topic: #{topic}
103
+
104
+ Dialogue turns:
105
+ #{turn_lines}
106
+
107
+ Synthesize this dialogue into a conclusion. Format EXACTLY as:
108
+ RECOMMENDATION: support | oppose | abstain
109
+ SUMMARY: <2-3 sentence synthesis of the key points and conclusion>
110
+ PROMPT
111
+ end
112
+ private_class_method :build_summarize_dialogue_prompt
113
+
114
+ def parse_summarize_dialogue_response(response)
115
+ return nil unless response&.content
116
+
117
+ text = response.content
118
+ recommendation_match = text.match(/RECOMMENDATION:\s*(support|oppose|abstain)/i)
119
+ summary_match = text.match(/SUMMARY:\s*(.+)/im)
120
+
121
+ return nil unless recommendation_match && summary_match
122
+
123
+ recommendation = recommendation_match.captures.first.strip.downcase.to_sym
124
+ summary = summary_match.captures.first.strip
125
+
126
+ { summary: summary, recommendation: recommendation }
127
+ end
128
+ private_class_method :parse_summarize_dialogue_response
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Self
7
+ module SelfTalk
8
+ module Helpers
9
+ class SelfTalkEngine
10
+ attr_reader :voices, :dialogues
11
+
12
+ def initialize
13
+ @voices = {}
14
+ @dialogues = {}
15
+ end
16
+
17
+ def register_voice(name:, voice_type:, volume: Constants::DEFAULT_VOLUME, bias_direction: nil)
18
+ return { registered: false, reason: :max_voices } if @voices.size >= Constants::MAX_VOICES
19
+ return { registered: false, reason: :unknown_type } unless Constants::VOICE_TYPES.include?(voice_type)
20
+
21
+ voice = InnerVoice.new(
22
+ name: name,
23
+ voice_type: voice_type,
24
+ volume: volume,
25
+ bias_direction: bias_direction
26
+ )
27
+ @voices[voice.id] = voice
28
+ { registered: true, voice: voice.to_h }
29
+ end
30
+
31
+ def start_dialogue(topic:)
32
+ prune_dialogues_if_needed
33
+ dialogue = Dialogue.new(topic: topic)
34
+ @dialogues[dialogue.id] = dialogue
35
+ { started: true, dialogue: dialogue.to_h }
36
+ end
37
+
38
+ def add_turn(dialogue_id:, voice_id:, content:, position: :clarify, strength: 0.5)
39
+ dialogue = @dialogues[dialogue_id]
40
+ return { added: false, reason: :dialogue_not_found } unless dialogue
41
+
42
+ voice = @voices[voice_id]
43
+ return { added: false, reason: :voice_not_found } unless voice
44
+ return { added: false, reason: :voice_inactive } unless voice.active
45
+
46
+ turn = dialogue.add_turn!(
47
+ voice_id: voice_id,
48
+ content: content,
49
+ position: position,
50
+ strength: strength
51
+ )
52
+ return { added: false, reason: :limit_reached_or_closed } unless turn
53
+
54
+ { added: true, turn: turn.to_h }
55
+ end
56
+
57
+ def conclude_dialogue(dialogue_id:, summary:)
58
+ dialogue = @dialogues[dialogue_id]
59
+ return { concluded: false, reason: :not_found } unless dialogue
60
+
61
+ result = dialogue.conclude!(summary)
62
+ { concluded: result, dialogue_id: dialogue_id }
63
+ end
64
+
65
+ def deadlock_dialogue(dialogue_id:)
66
+ dialogue = @dialogues[dialogue_id]
67
+ return { deadlocked: false, reason: :not_found } unless dialogue
68
+
69
+ result = dialogue.deadlock!
70
+ { deadlocked: result, dialogue_id: dialogue_id }
71
+ end
72
+
73
+ def active_dialogues
74
+ @dialogues.values.select(&:active?)
75
+ end
76
+
77
+ def concluded_dialogues
78
+ @dialogues.values.select(&:concluded?)
79
+ end
80
+
81
+ def dominant_voice
82
+ active = @voices.values.select(&:active)
83
+ return nil if active.empty?
84
+
85
+ active.max_by(&:volume)
86
+ end
87
+
88
+ def quietest_voice
89
+ active = @voices.values.select(&:active)
90
+ return nil if active.empty?
91
+
92
+ active.min_by(&:volume)
93
+ end
94
+
95
+ def voice_balance
96
+ return {} if @voices.empty?
97
+
98
+ total = @voices.values.sum(&:volume)
99
+ @voices.transform_values do |v|
100
+ total.positive? ? (v.volume / total).round(10) : 0.0
101
+ end
102
+ end
103
+
104
+ def amplify_voice(voice_id:, amount: Constants::VOLUME_BOOST)
105
+ voice = @voices[voice_id]
106
+ return { amplified: false, reason: :not_found } unless voice
107
+
108
+ voice.amplify!(amount)
109
+ { amplified: true, voice_id: voice_id, volume: voice.volume.round(10) }
110
+ end
111
+
112
+ def dampen_voice(voice_id:, amount: Constants::VOLUME_DECAY)
113
+ voice = @voices[voice_id]
114
+ return { dampened: false, reason: :not_found } unless voice
115
+
116
+ voice.dampen!(amount)
117
+ { dampened: true, voice_id: voice_id, volume: voice.volume.round(10) }
118
+ end
119
+
120
+ def dialogue_report(dialogue_id:)
121
+ dialogue = @dialogues[dialogue_id]
122
+ return { found: false } unless dialogue
123
+
124
+ positions = dialogue.voice_positions.transform_keys do |vid|
125
+ @voices[vid]&.name || vid
126
+ end
127
+
128
+ {
129
+ found: true,
130
+ dialogue: dialogue.to_h,
131
+ voice_positions: positions
132
+ }
133
+ end
134
+
135
+ def to_h
136
+ {
137
+ voice_count: @voices.size,
138
+ dialogue_count: @dialogues.size,
139
+ active_dialogue_count: active_dialogues.size,
140
+ dominant_voice: dominant_voice&.to_h,
141
+ quietest_voice: quietest_voice&.to_h,
142
+ voice_balance: voice_balance
143
+ }
144
+ end
145
+
146
+ private
147
+
148
+ def prune_dialogues_if_needed
149
+ return unless @dialogues.size >= Constants::MAX_DIALOGUES
150
+
151
+ oldest_key = @dialogues.min_by { |_, d| d.created_at }&.first
152
+ @dialogues.delete(oldest_key) if oldest_key
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Self
7
+ module SelfTalk
8
+ module Runners
9
+ module SelfTalk
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def register_voice(name:, voice_type:, volume: Helpers::Constants::DEFAULT_VOLUME,
14
+ bias_direction: nil, **)
15
+ result = engine.register_voice(
16
+ name: name,
17
+ voice_type: voice_type,
18
+ volume: volume,
19
+ bias_direction: bias_direction
20
+ )
21
+ Legion::Logging.info "[self_talk] register_voice: name=#{name} type=#{voice_type} registered=#{result[:registered]}"
22
+ result
23
+ end
24
+
25
+ def start_dialogue(topic:, **)
26
+ result = engine.start_dialogue(topic: topic)
27
+ Legion::Logging.debug "[self_talk] start_dialogue: topic=#{topic} id=#{result[:dialogue][:id]}"
28
+ result
29
+ end
30
+
31
+ def add_turn(dialogue_id:, voice_id:, content:, position: :clarify, strength: 0.5, **)
32
+ result = engine.add_turn(
33
+ dialogue_id: dialogue_id,
34
+ voice_id: voice_id,
35
+ content: content,
36
+ position: position,
37
+ strength: strength
38
+ )
39
+ Legion::Logging.debug "[self_talk] add_turn: dialogue=#{dialogue_id} voice=#{voice_id} added=#{result[:added]}"
40
+ result
41
+ end
42
+
43
+ def conclude_dialogue(dialogue_id:, summary: nil, **)
44
+ resolved_summary = summary || generate_summary_for_dialogue(dialogue_id)
45
+ result = engine.conclude_dialogue(dialogue_id: dialogue_id, summary: resolved_summary)
46
+ Legion::Logging.info "[self_talk] conclude_dialogue: id=#{dialogue_id} concluded=#{result[:concluded]}"
47
+ result
48
+ end
49
+
50
+ def generate_voice_turn(dialogue_id:, voice_id:, **)
51
+ dialogue_data = engine.dialogues[dialogue_id]
52
+ voice_data = engine.voices[voice_id]
53
+ return missing_entity_error(dialogue_data) unless dialogue_data && voice_data
54
+
55
+ content, source = resolve_turn_content(voice_data, dialogue_data)
56
+ turn_result = add_turn(
57
+ dialogue_id: dialogue_id,
58
+ voice_id: voice_id,
59
+ content: content[:content],
60
+ position: content[:position]
61
+ )
62
+ Legion::Logging.debug "[self_talk] generate_voice_turn: dialogue=#{dialogue_id} voice=#{voice_id} source=#{source}"
63
+ { generated: true, source: source, turn: turn_result[:turn] }
64
+ end
65
+
66
+ def deadlock_dialogue(dialogue_id:, **)
67
+ result = engine.deadlock_dialogue(dialogue_id: dialogue_id)
68
+ Legion::Logging.warn "[self_talk] deadlock_dialogue: id=#{dialogue_id} deadlocked=#{result[:deadlocked]}"
69
+ result
70
+ end
71
+
72
+ def amplify_voice(voice_id:, amount: Helpers::Constants::VOLUME_BOOST, **)
73
+ result = engine.amplify_voice(voice_id: voice_id, amount: amount)
74
+ Legion::Logging.debug "[self_talk] amplify_voice: id=#{voice_id} volume=#{result[:volume]}"
75
+ result
76
+ end
77
+
78
+ def dampen_voice(voice_id:, amount: Helpers::Constants::VOLUME_DECAY, **)
79
+ result = engine.dampen_voice(voice_id: voice_id, amount: amount)
80
+ Legion::Logging.debug "[self_talk] dampen_voice: id=#{voice_id} volume=#{result[:volume]}"
81
+ result
82
+ end
83
+
84
+ def dialogue_report(dialogue_id:, **)
85
+ result = engine.dialogue_report(dialogue_id: dialogue_id)
86
+ Legion::Logging.debug "[self_talk] dialogue_report: id=#{dialogue_id} found=#{result[:found]}"
87
+ result
88
+ end
89
+
90
+ def self_talk_status(**)
91
+ summary = engine.to_h
92
+ Legion::Logging.debug "[self_talk] status: voices=#{summary[:voice_count]} dialogues=#{summary[:dialogue_count]}"
93
+ summary
94
+ end
95
+
96
+ def decay_voices(**)
97
+ decayed = 0
98
+ voice_list = engine.voices.values.select(&:active).map do |voice|
99
+ voice.dampen!(Helpers::Constants::VOLUME_DECAY)
100
+ decayed += 1
101
+ { id: voice.id, name: voice.name, volume: voice.volume }
102
+ end
103
+ Legion::Logging.debug "[self-talk] voice decay: decayed=#{decayed} voices"
104
+ { decayed: decayed, voices: voice_list }
105
+ end
106
+
107
+ private
108
+
109
+ def engine
110
+ @engine ||= Helpers::SelfTalkEngine.new
111
+ end
112
+
113
+ def missing_entity_error(dialogue_data)
114
+ { generated: false, reason: dialogue_data ? :voice_not_found : :dialogue_not_found }
115
+ end
116
+
117
+ def resolve_turn_content(voice_data, dialogue_data)
118
+ prior_turns = build_prior_turns(dialogue_data)
119
+ if Helpers::LlmEnhancer.available?
120
+ llm_result = Helpers::LlmEnhancer.generate_turn(
121
+ voice_type: voice_data.voice_type,
122
+ topic: dialogue_data.topic,
123
+ prior_turns: prior_turns
124
+ )
125
+ return [llm_result, :llm] if llm_result
126
+ end
127
+ [stub_turn_content(voice_data.voice_type, dialogue_data.topic), :mechanical]
128
+ end
129
+
130
+ def build_prior_turns(dialogue_data)
131
+ dialogue_data.turns.map do |t|
132
+ speaking_voice = engine.voices[t.voice_id]
133
+ { voice_id: t.voice_id, voice_name: speaking_voice&.name || t.voice_id,
134
+ position: t.position, content: t.content }
135
+ end
136
+ end
137
+
138
+ def stub_turn_content(voice_type, topic)
139
+ { content: "[#{voice_type} perspective on #{topic}]", position: :clarify }
140
+ end
141
+
142
+ def generate_summary_for_dialogue(dialogue_id)
143
+ dialogue_data = engine.dialogues[dialogue_id]
144
+ return 'Dialogue concluded' unless dialogue_data
145
+
146
+ if Helpers::LlmEnhancer.available?
147
+ turns = dialogue_data.turns.map do |t|
148
+ speaking_voice = engine.voices[t.voice_id]
149
+ {
150
+ voice_id: t.voice_id,
151
+ voice_name: speaking_voice&.name || t.voice_id,
152
+ position: t.position,
153
+ content: t.content
154
+ }
155
+ end
156
+
157
+ llm_result = Helpers::LlmEnhancer.summarize_dialogue(
158
+ topic: dialogue_data.topic,
159
+ turns: turns
160
+ )
161
+ return llm_result[:summary] if llm_result
162
+ end
163
+
164
+ 'Dialogue concluded'
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Self
7
+ module SelfTalk
8
+ VERSION = '0.1.1'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/self/self_talk/version'
4
+ require 'legion/extensions/agentic/self/self_talk/helpers/constants'
5
+ require 'legion/extensions/agentic/self/self_talk/helpers/inner_voice'
6
+ require 'legion/extensions/agentic/self/self_talk/helpers/dialogue_turn'
7
+ require 'legion/extensions/agentic/self/self_talk/helpers/dialogue'
8
+ require 'legion/extensions/agentic/self/self_talk/helpers/self_talk_engine'
9
+ require 'legion/extensions/agentic/self/self_talk/helpers/llm_enhancer'
10
+ require 'legion/extensions/agentic/self/self_talk/runners/self_talk'
11
+ require 'legion/extensions/agentic/self/self_talk/client'
12
+
13
+ module Legion
14
+ module Extensions
15
+ module Agentic
16
+ module Self
17
+ module SelfTalk
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Self
7
+ VERSION = '0.1.0'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'self/version'
4
+ require_relative 'self/fingerprint'
5
+ require_relative 'self/narrative_arc'
6
+ require_relative 'self/anchor'
7
+ require_relative 'self/architecture'
8
+ require_relative 'self/narrative_identity'
9
+ require_relative 'self/narrative_self'
10
+ require_relative 'self/metacognition'
11
+ require_relative 'self/metacognitive_monitoring'
12
+ require_relative 'self/self_model'
13
+ require_relative 'self/self_talk'
14
+ require_relative 'self/identity'
15
+ require_relative 'self/personality'
16
+ require_relative 'self/agency'
17
+ require_relative 'self/reflection'
18
+ require_relative 'self/anosognosia'
19
+ require_relative 'self/default_mode_network'
20
+
21
+ module Legion
22
+ module Extensions
23
+ module Agentic
24
+ module Self
25
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
26
+
27
+ def remote_invocable?
28
+ false
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Self::Agency::Client do
6
+ subject(:client) { described_class.new }
7
+
8
+ describe '#initialize' do
9
+ it 'creates a default efficacy model' do
10
+ expect(client.efficacy_model).to be_a(Legion::Extensions::Agentic::Self::Agency::Helpers::EfficacyModel)
11
+ end
12
+
13
+ it 'accepts an injected model' do
14
+ custom = Legion::Extensions::Agentic::Self::Agency::Helpers::EfficacyModel.new
15
+ injected = described_class.new(efficacy_model: custom)
16
+ expect(injected.efficacy_model).to equal(custom)
17
+ end
18
+ end
19
+
20
+ describe 'Bandura self-efficacy lifecycle' do
21
+ it 'models the four sources of self-efficacy and their relative strengths' do
22
+ # Source 1: Mastery experience (strongest source)
23
+ client.record_mastery(domain: :coding, outcome_type: :success)
24
+ client.record_mastery(domain: :coding, outcome_type: :success)
25
+ coding_after_mastery = client.efficacy_model.efficacy_for(:coding)
26
+ expect(coding_after_mastery).to be > 0.5
27
+
28
+ # Source 2: Vicarious experience (weaker than mastery)
29
+ client.record_vicarious(domain: :public_speaking, outcome_type: :success)
30
+ speaking_after_vicarious = client.efficacy_model.efficacy_for(:public_speaking)
31
+ expect(speaking_after_vicarious).to be > 0.5
32
+ expect(speaking_after_vicarious).to be < coding_after_mastery
33
+
34
+ # Source 3: Verbal persuasion (weakest cognitive source)
35
+ client.record_persuasion(domain: :management, positive: true)
36
+ mgmt = client.efficacy_model.efficacy_for(:management)
37
+ expect(mgmt).to be > 0.5
38
+
39
+ # Source 4: Physiological state
40
+ client.record_physiological(domain: :exercise, state: :energized)
41
+ exercise = client.efficacy_model.efficacy_for(:exercise)
42
+ expect(exercise).to be >= 0.5
43
+
44
+ # Check overall stats
45
+ stats = client.agency_stats
46
+ expect(stats[:stats][:domain_count]).to eq(4)
47
+
48
+ # Decision gate: should attempt based on efficacy
49
+ coding_decision = client.should_attempt?(domain: :coding, threshold: 0.4)
50
+ expect(coding_decision[:should_attempt]).to be true
51
+
52
+ # Strongest domains should be coding (most mastery experiences)
53
+ strongest = client.strongest_domains(count: 1)
54
+ expect(strongest[:domains].keys.first).to eq(:coding)
55
+
56
+ # Now model failure — repeated failures erode self-efficacy
57
+ 5.times { client.record_mastery(domain: :sales, outcome_type: :failure) }
58
+ sales_check = client.check_efficacy(domain: :sales)
59
+ expect(sales_check[:label]).to eq(:helpless).or eq(:doubtful)
60
+
61
+ # Decay over time moves everything slowly toward default
62
+ 10.times { client.update_agency }
63
+ coding_after_decay = client.efficacy_model.efficacy_for(:coding)
64
+ expect(coding_after_decay).to be < coding_after_mastery
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Legion::Extensions::Agentic::Self::Agency::Helpers::Constants do
6
+ describe 'DEFAULT_EFFICACY' do
7
+ it 'is 0.5 (moderate confidence)' do
8
+ expect(described_class::DEFAULT_EFFICACY).to eq(0.5)
9
+ end
10
+ end
11
+
12
+ describe 'EFFICACY_ALPHA' do
13
+ it 'is a low positive float for slow adaptation' do
14
+ expect(described_class::EFFICACY_ALPHA).to be_between(0.01, 0.5)
15
+ end
16
+ end
17
+
18
+ describe 'asymmetric reinforcement' do
19
+ it 'penalizes failure harder than it rewards success' do
20
+ expect(described_class::FAILURE_PENALTY).to be > described_class::MASTERY_BOOST
21
+ end
22
+ end
23
+
24
+ describe 'EFFICACY_SOURCES' do
25
+ it 'contains Bandura four sources' do
26
+ expect(described_class::EFFICACY_SOURCES).to contain_exactly(:mastery, :vicarious, :persuasion, :physiological)
27
+ end
28
+
29
+ it 'is frozen' do
30
+ expect(described_class::EFFICACY_SOURCES).to be_frozen
31
+ end
32
+ end
33
+
34
+ describe 'ATTRIBUTION_LEVELS' do
35
+ it 'has four levels' do
36
+ expect(described_class::ATTRIBUTION_LEVELS.size).to eq(4)
37
+ end
38
+
39
+ it 'has full_agency as highest' do
40
+ expect(described_class::ATTRIBUTION_LEVELS[:full_agency]).to be > described_class::ATTRIBUTION_LEVELS[:no_agency]
41
+ end
42
+
43
+ it 'is frozen' do
44
+ expect(described_class::ATTRIBUTION_LEVELS).to be_frozen
45
+ end
46
+ end
47
+
48
+ describe 'OUTCOME_TYPES' do
49
+ it 'includes success, failure, partial_success, unexpected' do
50
+ expect(described_class::OUTCOME_TYPES).to contain_exactly(:success, :failure, :partial_success, :unexpected)
51
+ end
52
+ end
53
+
54
+ describe 'EFFICACY_LABELS' do
55
+ it 'has five levels' do
56
+ expect(described_class::EFFICACY_LABELS.size).to eq(5)
57
+ end
58
+
59
+ it 'is frozen' do
60
+ expect(described_class::EFFICACY_LABELS).to be_frozen
61
+ end
62
+ end
63
+
64
+ describe 'bounds' do
65
+ it 'has a floor above zero' do
66
+ expect(described_class::EFFICACY_FLOOR).to be > 0.0
67
+ end
68
+
69
+ it 'has a ceiling below one' do
70
+ expect(described_class::EFFICACY_CEILING).to be < 1.0
71
+ end
72
+ end
73
+ end