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,402 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Self
7
+ module Identity
8
+ module Runners
9
+ # Entra ID Application identity integration for Digital Workers.
10
+ #
11
+ # Permission model:
12
+ # - Entra app CREATION is done by the human owner (requires Application.ReadWrite.All
13
+ # which Legion does not have and should not have)
14
+ # - Legion gets Application.Read.All or Directory.Read.All for read operations
15
+ # - OIDC token validation uses the public JWKS endpoint (no special permission)
16
+ # - Write operations (transfer ownership, disable apps) update the Legion DB
17
+ # and emit events; the human completes the Entra side manually
18
+ module Entra
19
+ GRAPH_API_BASE = 'https://graph.microsoft.com/v1.0'
20
+ ENTRA_JWKS_URL_TEMPLATE = 'https://login.microsoftonline.com/%<tenant_id>s/discovery/v2.0/keys'
21
+ ENTRA_ISSUER_TEMPLATE = 'https://login.microsoftonline.com/%<tenant_id>s/v2.0'
22
+
23
+ # Validate a worker's identity by checking its Entra app registration exists
24
+ # and its OIDC token is valid.
25
+ # OIDC validation uses the public JWKS endpoint — no Graph API permission needed.
26
+ def validate_worker_identity(worker_id:, entra_app_id: nil, token: nil, tenant_id: nil, **)
27
+ worker = find_worker(worker_id)
28
+ return { valid: false, error: 'worker not found' } unless worker
29
+
30
+ app_id = entra_app_id || worker[:entra_app_id]
31
+ return { valid: false, error: 'no entra_app_id' } unless app_id
32
+
33
+ # If a token is provided and legion-crypt has JWKS support, validate it
34
+ if token && defined?(Legion::Crypt::JWT) && Legion::Crypt::JWT.respond_to?(:verify_with_jwks)
35
+ tid = tenant_id || resolve_tenant_id
36
+ return { valid: false, error: 'no tenant_id configured' } unless tid
37
+
38
+ jwks_url = format(ENTRA_JWKS_URL_TEMPLATE, tenant_id: tid)
39
+ issuer = format(ENTRA_ISSUER_TEMPLATE, tenant_id: tid)
40
+
41
+ claims = Legion::Crypt::JWT.verify_with_jwks(
42
+ token,
43
+ jwks_url: jwks_url,
44
+ issuers: [issuer],
45
+ audience: app_id
46
+ )
47
+
48
+ Legion::Logging.debug "[identity:entra] token validated: worker=#{worker_id} sub=#{claims[:sub]}"
49
+
50
+ return {
51
+ valid: true,
52
+ worker_id: worker_id,
53
+ entra_app_id: app_id,
54
+ owner_msid: worker[:owner_msid],
55
+ lifecycle: worker[:lifecycle_state],
56
+ claims: claims,
57
+ validated_at: Time.now.utc
58
+ }
59
+ end
60
+
61
+ # No token provided — return identity info without token validation
62
+ Legion::Logging.debug "[identity:entra] validate (no token): worker=#{worker_id} entra_app=#{app_id}"
63
+
64
+ {
65
+ valid: true,
66
+ worker_id: worker_id,
67
+ entra_app_id: app_id,
68
+ owner_msid: worker[:owner_msid],
69
+ lifecycle: worker[:lifecycle_state],
70
+ validated_at: Time.now.utc
71
+ }
72
+ rescue Legion::Crypt::JWT::ExpiredTokenError => e
73
+ { valid: false, error: 'token_expired', message: e.message }
74
+ rescue Legion::Crypt::JWT::InvalidTokenError => e
75
+ { valid: false, error: 'token_invalid', message: e.message }
76
+ rescue Legion::Crypt::JWT::Error => e
77
+ { valid: false, error: 'token_error', message: e.message }
78
+ end
79
+
80
+ # Sync the worker's owner from Entra app ownership.
81
+ # Requires: Application.Read.All or Directory.Read.All (read-only)
82
+ # Falls back to local record when Graph API credentials unavailable.
83
+ def sync_owner(worker_id:, **)
84
+ worker = find_worker(worker_id)
85
+ return { synced: false, error: 'worker not found' } unless worker
86
+
87
+ entra_object_id = worker[:entra_object_id]
88
+ return { synced: false, worker_id: worker_id, error: 'no entra_object_id', source: :local } unless entra_object_id
89
+
90
+ creds = resolve_graph_credentials
91
+ unless creds
92
+ Legion::Logging.debug "[identity:entra] sync_owner fallback to local: worker=#{worker_id}"
93
+ return { synced: true, worker_id: worker_id, source: :local,
94
+ owner_msid: worker[:owner_msid], synced_at: Time.now.utc }
95
+ end
96
+
97
+ token = Helpers::GraphToken.fetch(**creds)
98
+ conn = Helpers::GraphClient.connection(token: token)
99
+ resp = conn.get("applications/#{entra_object_id}/owners")
100
+
101
+ unless resp.success?
102
+ Legion::Logging.warn "[identity:entra] graph owner sync failed: #{resp.status}"
103
+ return { synced: false, worker_id: worker_id, source: :local, owner_msid: worker[:owner_msid] }
104
+ end
105
+
106
+ owners = resp.body['value'] || []
107
+ graph_owner_msid = owners.first&.dig('id')
108
+ changed = graph_owner_msid && graph_owner_msid != worker[:owner_msid].to_s
109
+
110
+ if changed && defined?(Legion::Data::Model::DigitalWorker)
111
+ Legion::Data::Model::DigitalWorker.where(worker_id: worker_id).update(owner_msid: graph_owner_msid)
112
+ if defined?(Legion::Events)
113
+ Legion::Events.emit('worker.owner_changed', { worker_id: worker_id, old: worker[:owner_msid],
114
+ new: graph_owner_msid })
115
+ end
116
+ end
117
+
118
+ { synced: true, source: :graph_api, worker_id: worker_id,
119
+ owner_msid: graph_owner_msid || worker[:owner_msid], changed: !changed.nil?, synced_at: Time.now.utc }
120
+ rescue Helpers::GraphToken::GraphTokenError, Faraday::Error => e
121
+ Legion::Logging.warn "[identity:entra] graph sync error: #{e.message}"
122
+ { synced: false, worker_id: worker_id, source: :local, error: e.message }
123
+ end
124
+
125
+ # Transfer ownership of a digital worker to a new human.
126
+ # Updates the Legion DB record and emits an audit event.
127
+ # The Entra app ownership change must be done by the human owner
128
+ # (requires Application.ReadWrite.All which Legion intentionally does not have).
129
+ def transfer_ownership(worker_id:, new_owner_msid:, transferred_by:, reason: nil, **)
130
+ worker = find_worker(worker_id)
131
+ return { transferred: false, error: 'worker not found' } unless worker
132
+
133
+ old_owner = worker[:owner_msid]
134
+ return { transferred: false, error: 'same owner' } if old_owner == new_owner_msid
135
+
136
+ # Update local record — this is the Legion side of the transfer
137
+ if defined?(Legion::Data) && defined?(Legion::Data::Model::DigitalWorker)
138
+ dw = Legion::Data::Model::DigitalWorker.first(worker_id: worker_id)
139
+ dw&.update(owner_msid: new_owner_msid, updated_at: Time.now.utc)
140
+ end
141
+
142
+ # Entra app ownership change requires Application.ReadWrite.All.
143
+ # Legion does not have this permission by design — the human owner
144
+ # must update Entra app ownership separately via Azure Portal or CLI.
145
+
146
+ audit = {
147
+ event: :ownership_transferred,
148
+ worker_id: worker_id,
149
+ from_owner: old_owner,
150
+ to_owner: new_owner_msid,
151
+ transferred_by: transferred_by,
152
+ reason: reason,
153
+ entra_action_required: 'update Entra app ownership via Azure Portal or az CLI',
154
+ at: Time.now.utc
155
+ }
156
+
157
+ Legion::Events.emit('worker.ownership_transferred', audit) if defined?(Legion::Events)
158
+ Legion::Logging.info "[identity:entra] ownership transferred (Legion DB): worker=#{worker_id} " \
159
+ "from=#{old_owner} to=#{new_owner_msid} by=#{transferred_by}"
160
+ Legion::Logging.warn '[identity:entra] Entra app ownership must be updated manually (requires Application.ReadWrite.All)'
161
+
162
+ { transferred: true }.merge(audit)
163
+ end
164
+
165
+ # Scan for orphaned workers: Entra apps that are disabled or owners no longer active.
166
+ # Requires: Application.Read.All or Directory.Read.All (read-only)
167
+ # Orphan REMEDIATION (disabling apps) requires human action since Legion
168
+ # does not have Application.ReadWrite.All.
169
+ # Falls back to local-only scan when Graph API credentials unavailable.
170
+ def check_orphans(**)
171
+ return { orphans: [], checked: 0, source: :unavailable } unless defined?(Legion::Data) && defined?(Legion::Data::Model::DigitalWorker)
172
+
173
+ active_workers = Legion::Data::Model::DigitalWorker.where(lifecycle_state: 'active').all
174
+ orphans = []
175
+ skipped = 0
176
+
177
+ creds = resolve_graph_credentials
178
+ conn = nil
179
+ if creds
180
+ token = Helpers::GraphToken.fetch(**creds)
181
+ conn = Helpers::GraphClient.connection(token: token)
182
+ end
183
+
184
+ active_workers.each do |worker|
185
+ if system_placeholder?(worker.entra_app_id, worker.worker_id)
186
+ skipped += 1
187
+ next
188
+ end
189
+
190
+ next unless conn
191
+
192
+ orphan_reason = check_worker_orphan_status(conn, worker)
193
+ if orphan_reason
194
+ orphans << worker
195
+ auto_pause_orphan(worker, reason: orphan_reason)
196
+ end
197
+ rescue Faraday::Error => e
198
+ Legion::Logging.warn "[identity:entra] graph error scanning #{worker.worker_id}: #{e.message}"
199
+ end
200
+
201
+ source = conn ? :graph_api : :local
202
+ Legion::Logging.debug "[identity:entra] orphan check (#{source}): scanned #{active_workers.size}, skipped #{skipped}"
203
+
204
+ {
205
+ orphans: orphans.map { |w| { worker_id: w.worker_id, owner_msid: w.owner_msid, reason: :entra_orphan } },
206
+ checked: active_workers.size - skipped,
207
+ skipped: skipped,
208
+ source: source,
209
+ checked_at: Time.now.utc
210
+ }
211
+ rescue Helpers::GraphToken::GraphTokenError => e
212
+ Legion::Logging.warn "[identity:entra] orphan check token error: #{e.message}"
213
+ { orphans: [], checked: 0, source: :local, error: e.message, checked_at: Time.now.utc }
214
+ end
215
+
216
+ # Map Entra security group OIDs to Legion governance roles
217
+ def resolve_governance_roles(groups:, **)
218
+ group_map = Legion::Settings.dig(:rbac, :entra, :group_map) || {}
219
+ default_role = Legion::Settings.dig(:rbac, :entra, :default_role) || 'governance-observer'
220
+ matched = Array(groups).filter_map { |oid| group_map[oid] }.uniq
221
+ matched = [default_role] if matched.empty?
222
+ { success: true, groups: groups, roles: matched }
223
+ end
224
+
225
+ def refresh_access_token(worker_id:, force: false, **)
226
+ require_relative '../helpers/token_cache'
227
+
228
+ unless force
229
+ cached = Helpers::TokenCache.fetch(worker_id: worker_id)
230
+ if cached && !Helpers::TokenCache.approaching_expiry?(worker_id: worker_id)
231
+ return { refreshed: false, worker_id: worker_id, source: :cache, expires_at: cached[:expires_at] }
232
+ end
233
+ end
234
+
235
+ secret = Helpers::VaultSecrets.read_client_secret(worker_id: worker_id)
236
+ return { refreshed: false, worker_id: worker_id, error: 'vault_unavailable' } unless secret
237
+
238
+ tenant_id = resolve_tenant_id
239
+ return { refreshed: false, worker_id: worker_id, error: 'no_tenant_id' } unless tenant_id
240
+
241
+ scope = Legion::Settings.dig(:identity, :entra, :token_scope) || 'https://graph.microsoft.com/.default'
242
+ url = "https://login.microsoftonline.com/#{tenant_id}/oauth2/v2.0/token"
243
+
244
+ require 'faraday'
245
+ resp = Faraday.post(url, {
246
+ grant_type: 'client_credentials',
247
+ client_id: secret[:client_id] || secret[:entra_app_id],
248
+ client_secret: secret[:client_secret],
249
+ scope: scope
250
+ })
251
+
252
+ unless resp.success?
253
+ Legion::Logging.warn "[identity] token refresh failed for #{worker_id}: #{resp.status}"
254
+ return { refreshed: false, worker_id: worker_id, error: 'token_request_failed' }
255
+ end
256
+
257
+ body = Legion::JSON.load(resp.body)
258
+ expires_in = body[:expires_in]&.to_i || 3600
259
+ Helpers::TokenCache.store(worker_id: worker_id, token: body[:access_token], expires_in: expires_in)
260
+
261
+ { refreshed: true, worker_id: worker_id, expires_at: Time.now + expires_in }
262
+ rescue StandardError => e
263
+ Legion::Logging.warn "[identity] token refresh error: #{e.message}"
264
+ { refreshed: false, worker_id: worker_id, error: e.message }
265
+ end
266
+
267
+ def rotate_client_secret(worker_id:, dry_run: false, **)
268
+ rotation_enabled = Legion::Settings.dig(:identity, :entra, :rotation_enabled)
269
+ buffer_days = Legion::Settings.dig(:identity, :entra, :rotation_buffer_days) || 30
270
+
271
+ secret = Helpers::VaultSecrets.read_client_secret(worker_id: worker_id)
272
+ return { rotated: false, worker_id: worker_id, error: 'vault_unavailable' } unless secret
273
+
274
+ expires_at = secret[:client_secret_expires_at]
275
+ return { rotated: false, worker_id: worker_id, action_required: false, reason: 'no_expiry_tracked' } unless expires_at
276
+
277
+ days_remaining = (Time.parse(expires_at.to_s) - Time.now) / 86_400
278
+ unless days_remaining < buffer_days
279
+ return { rotated: false, worker_id: worker_id, action_required: false,
280
+ days_remaining: days_remaining.round(1) }
281
+ end
282
+
283
+ unless rotation_enabled
284
+ Legion::Logging.warn "[identity] credential expiring for #{worker_id} in #{days_remaining.round(1)} days"
285
+ if defined?(Legion::Events)
286
+ Legion::Events.emit('worker.credential_expiry_warning', {
287
+ worker_id: worker_id, days_remaining: days_remaining.round(1)
288
+ })
289
+ end
290
+ return { rotated: false, worker_id: worker_id, action_required: true,
291
+ days_remaining: days_remaining.round(1) }
292
+ end
293
+
294
+ return { rotated: false, worker_id: worker_id, dry_run: true, would_rotate: true } if dry_run
295
+
296
+ # Graph API rotation would go here when permission is granted
297
+ { rotated: false, worker_id: worker_id, error: 'graph_api_rotation_not_implemented' }
298
+ end
299
+
300
+ def credential_refresh_cycle(**)
301
+ return { workers_checked: 0, error: 'data_unavailable' } unless defined?(Legion::Data::Model::DigitalWorker)
302
+
303
+ workers = Legion::Data::Model::DigitalWorker.where(lifecycle_state: 'active').all
304
+ results = { workers_checked: 0, refreshed: 0, warned: 0 }
305
+
306
+ workers.each do |worker|
307
+ next if system_placeholder?(worker.entra_app_id, worker.worker_id)
308
+
309
+ results[:workers_checked] += 1
310
+
311
+ token_result = refresh_access_token(worker_id: worker.worker_id)
312
+ results[:refreshed] += 1 if token_result[:refreshed]
313
+
314
+ rotation_result = rotate_client_secret(worker_id: worker.worker_id)
315
+ results[:warned] += 1 if rotation_result[:action_required]
316
+ end
317
+
318
+ results
319
+ rescue StandardError => e
320
+ Legion::Logging.warn "[identity] credential refresh cycle error: #{e.message}"
321
+ { workers_checked: 0, error: e.message }
322
+ end
323
+
324
+ private
325
+
326
+ def find_worker(worker_id)
327
+ if defined?(Legion::Data) && defined?(Legion::Data::Model::DigitalWorker)
328
+ worker = Legion::Data::Model::DigitalWorker.first(worker_id: worker_id)
329
+ return worker.to_hash if worker
330
+ end
331
+ nil
332
+ end
333
+
334
+ def system_placeholder?(entra_app_id, worker_id)
335
+ return true if entra_app_id.nil? || entra_app_id == 'system'
336
+ return true if entra_app_id == worker_id
337
+ return true if entra_app_id.start_with?('lex-')
338
+
339
+ false
340
+ end
341
+
342
+ def resolve_tenant_id
343
+ if defined?(Legion::Settings) &&
344
+ Legion::Settings[:identity]&.dig(:entra, :tenant_id)
345
+ return Legion::Settings[:identity][:entra][:tenant_id]
346
+ end
347
+
348
+ nil
349
+ end
350
+
351
+ def resolve_graph_credentials
352
+ tenant_id = resolve_tenant_id
353
+ return nil unless tenant_id
354
+
355
+ secret = Helpers::VaultSecrets.read_client_secret(worker_id: 'legion/identity')
356
+ return nil unless secret && secret[:client_id] && secret[:client_secret]
357
+
358
+ { tenant_id: tenant_id, client_id: secret[:client_id], client_secret: secret[:client_secret] }
359
+ rescue StandardError
360
+ nil
361
+ end
362
+
363
+ def check_worker_orphan_status(conn, worker)
364
+ # Check if the Entra app registration still exists
365
+ if worker.respond_to?(:entra_object_id) && worker.entra_object_id
366
+ app_resp = conn.get("applications/#{worker.entra_object_id}")
367
+ return :entra_app_deleted unless app_resp.success?
368
+ end
369
+
370
+ # Check if the owner account is still active
371
+ if worker.owner_msid
372
+ user_resp = conn.get("users/#{worker.owner_msid}")
373
+ return :owner_deleted unless user_resp.success?
374
+ return :owner_disabled if user_resp.body['accountEnabled'] == false
375
+ end
376
+
377
+ nil
378
+ end
379
+
380
+ def auto_pause_orphan(worker, reason:)
381
+ worker.update(lifecycle_state: 'paused', updated_at: Time.now.utc)
382
+
383
+ if defined?(Legion::Events)
384
+ Legion::Events.emit('worker.orphan_detected', {
385
+ worker_id: worker.worker_id,
386
+ owner_msid: worker.owner_msid,
387
+ reason: reason,
388
+ action: :auto_paused,
389
+ remediation: 'disable or reassign Entra app via Azure Portal',
390
+ at: Time.now.utc
391
+ })
392
+ end
393
+
394
+ Legion::Logging.warn "[identity:entra] orphan detected: worker=#{worker.worker_id} reason=#{reason} — auto-paused"
395
+ end
396
+ end
397
+ end
398
+ end
399
+ end
400
+ end
401
+ end
402
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Self
7
+ module Identity
8
+ module Runners
9
+ module Identity
10
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
11
+ Legion::Extensions::Helpers.const_defined?(:Lex)
12
+
13
+ def observe_behavior(dimension:, value:, **)
14
+ fingerprint = identity_fingerprint
15
+ fingerprint.observe(dimension, value)
16
+
17
+ Legion::Logging.debug "[identity] observe: dim=#{dimension} val=#{value.round(2)} " \
18
+ "obs=#{fingerprint.observation_count} maturity=#{fingerprint.maturity}"
19
+ {
20
+ dimension: dimension,
21
+ recorded: true,
22
+ observation_count: fingerprint.observation_count,
23
+ maturity: fingerprint.maturity
24
+ }
25
+ end
26
+
27
+ def observe_all(observations:, **)
28
+ fingerprint = identity_fingerprint
29
+ fingerprint.observe_all(observations)
30
+
31
+ Legion::Logging.debug "[identity] observe_all: dims=#{observations.keys.join(',')} " \
32
+ "obs=#{fingerprint.observation_count} maturity=#{fingerprint.maturity}"
33
+ {
34
+ dimensions_observed: observations.keys,
35
+ observation_count: fingerprint.observation_count,
36
+ maturity: fingerprint.maturity
37
+ }
38
+ end
39
+
40
+ def check_entropy(observations: {}, **)
41
+ fingerprint = identity_fingerprint
42
+ entropy = fingerprint.current_entropy(observations)
43
+ classification = Helpers::Dimensions.classify_entropy(entropy)
44
+ trend = fingerprint.entropy_trend
45
+
46
+ result = {
47
+ entropy: entropy,
48
+ classification: classification,
49
+ trend: trend,
50
+ in_range: Helpers::Dimensions::OPTIMAL_ENTROPY_RANGE.cover?(entropy)
51
+ }
52
+
53
+ case classification
54
+ when :high_entropy
55
+ result[:warning] = :possible_impersonation_or_drift
56
+ result[:action] = :enter_caution_mode
57
+ Legion::Logging.warn "[identity] high entropy detected: #{entropy.round(3)} trend=#{trend} - possible impersonation"
58
+ when :low_entropy
59
+ result[:warning] = :possible_automation
60
+ result[:action] = :trigger_verification
61
+ Legion::Logging.warn "[identity] low entropy detected: #{entropy.round(3)} trend=#{trend} - possible automation"
62
+ else
63
+ Legion::Logging.debug "[identity] entropy check: #{entropy.round(3)} classification=#{classification} trend=#{trend}"
64
+ end
65
+
66
+ result
67
+ end
68
+
69
+ def identity_status(**)
70
+ fingerprint = identity_fingerprint
71
+ Legion::Logging.debug "[identity] status: maturity=#{fingerprint.maturity} observations=#{fingerprint.observation_count}"
72
+ fingerprint.to_h
73
+ end
74
+
75
+ def identity_maturity(**)
76
+ { maturity: identity_fingerprint.maturity }
77
+ end
78
+
79
+ private
80
+
81
+ def identity_fingerprint
82
+ @identity_fingerprint ||= Helpers::Fingerprint.new
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ 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 Identity
8
+ VERSION = '0.4.0'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/self/identity/version'
4
+ require 'legion/extensions/agentic/self/identity/helpers/dimensions'
5
+ require 'legion/extensions/agentic/self/identity/helpers/fingerprint'
6
+ require 'legion/extensions/agentic/self/identity/helpers/vault_secrets'
7
+ require 'legion/extensions/agentic/self/identity/runners/identity'
8
+ require 'legion/extensions/agentic/self/identity/runners/entra'
9
+ require 'legion/extensions/agentic/self/identity/actors/orphan_check'
10
+ require 'legion/extensions/agentic/self/identity/client'
11
+
12
+ module Legion
13
+ module Extensions
14
+ module Agentic
15
+ module Self
16
+ module Identity
17
+ end
18
+ end
19
+ end
20
+
21
+ if defined?(Legion::Data::Local)
22
+ Legion::Data::Local.register_migrations(
23
+ name: :identity,
24
+ path: File.join(__dir__, 'identity', 'local_migrations')
25
+ )
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/self/metacognition/helpers/constants'
4
+ require 'legion/extensions/agentic/self/metacognition/helpers/self_model'
5
+ require 'legion/extensions/agentic/self/metacognition/helpers/snapshot_store'
6
+ require 'legion/extensions/agentic/self/metacognition/helpers/narrator_bridge'
7
+ require 'legion/extensions/agentic/self/metacognition/runners/metacognition'
8
+
9
+ module Legion
10
+ module Extensions
11
+ module Agentic
12
+ module Self
13
+ module Metacognition
14
+ class Client
15
+ include Runners::Metacognition
16
+
17
+ attr_reader :snapshot_store
18
+
19
+ def initialize(snapshot_store: nil, **)
20
+ @snapshot_store = snapshot_store || Helpers::SnapshotStore.new
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end