ciris-agent 1.7.7__py3-none-any.whl

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 (986) hide show
  1. ciris_adapters/README.md +113 -0
  2. ciris_adapters/__init__.py +30 -0
  3. ciris_adapters/ciris_covenant_metrics/README.md +144 -0
  4. ciris_adapters/ciris_covenant_metrics/__init__.py +36 -0
  5. ciris_adapters/ciris_covenant_metrics/adapter.py +249 -0
  6. ciris_adapters/ciris_covenant_metrics/manifest.json +152 -0
  7. ciris_adapters/ciris_covenant_metrics/services.py +403 -0
  8. ciris_adapters/ciris_hosted_tools/__init__.py +24 -0
  9. ciris_adapters/ciris_hosted_tools/adapter.py +169 -0
  10. ciris_adapters/ciris_hosted_tools/manifest.json +94 -0
  11. ciris_adapters/ciris_hosted_tools/services.py +744 -0
  12. ciris_adapters/external_data_sql/README.md +559 -0
  13. ciris_adapters/external_data_sql/__init__.py +43 -0
  14. ciris_adapters/external_data_sql/adapter.py +144 -0
  15. ciris_adapters/external_data_sql/configurable.py +315 -0
  16. ciris_adapters/external_data_sql/dialects/__init__.py +37 -0
  17. ciris_adapters/external_data_sql/dialects/base.py +133 -0
  18. ciris_adapters/external_data_sql/dialects/mysql.py +63 -0
  19. ciris_adapters/external_data_sql/dialects/postgresql.py +59 -0
  20. ciris_adapters/external_data_sql/dialects/sqlite.py +62 -0
  21. ciris_adapters/external_data_sql/example_config.json +88 -0
  22. ciris_adapters/external_data_sql/example_privacy_schema.yaml +127 -0
  23. ciris_adapters/external_data_sql/manifest.json +195 -0
  24. ciris_adapters/external_data_sql/privacy_schema_loader.py +189 -0
  25. ciris_adapters/external_data_sql/protocol.py +101 -0
  26. ciris_adapters/external_data_sql/schemas.py +146 -0
  27. ciris_adapters/external_data_sql/service.py +1547 -0
  28. ciris_adapters/external_data_sql/service_old.py +492 -0
  29. ciris_adapters/home_assistant/__init__.py +63 -0
  30. ciris_adapters/home_assistant/adapter.py +201 -0
  31. ciris_adapters/home_assistant/communication_service.py +347 -0
  32. ciris_adapters/home_assistant/configurable.py +667 -0
  33. ciris_adapters/home_assistant/manifest.json +203 -0
  34. ciris_adapters/home_assistant/schemas.py +129 -0
  35. ciris_adapters/home_assistant/service.py +751 -0
  36. ciris_adapters/home_assistant/tool_service.py +441 -0
  37. ciris_adapters/mcp_client/__init__.py +82 -0
  38. ciris_adapters/mcp_client/adapter.py +847 -0
  39. ciris_adapters/mcp_client/config.py +280 -0
  40. ciris_adapters/mcp_client/configurable.py +422 -0
  41. ciris_adapters/mcp_client/manifest.json +185 -0
  42. ciris_adapters/mcp_client/mcp_communication_service.py +393 -0
  43. ciris_adapters/mcp_client/mcp_tool_service.py +463 -0
  44. ciris_adapters/mcp_client/mcp_wise_service.py +394 -0
  45. ciris_adapters/mcp_client/schemas.py +149 -0
  46. ciris_adapters/mcp_client/security.py +592 -0
  47. ciris_adapters/mcp_common/__init__.py +44 -0
  48. ciris_adapters/mcp_common/manifest.json +25 -0
  49. ciris_adapters/mcp_common/protocol.py +315 -0
  50. ciris_adapters/mcp_common/schemas.py +225 -0
  51. ciris_adapters/mcp_server/__init__.py +47 -0
  52. ciris_adapters/mcp_server/adapter.py +581 -0
  53. ciris_adapters/mcp_server/config.py +260 -0
  54. ciris_adapters/mcp_server/configurable.py +393 -0
  55. ciris_adapters/mcp_server/handlers.py +663 -0
  56. ciris_adapters/mcp_server/manifest.json +211 -0
  57. ciris_adapters/mcp_server/security.py +500 -0
  58. ciris_adapters/mock_llm/README.md +117 -0
  59. ciris_adapters/mock_llm/__init__.py +21 -0
  60. ciris_adapters/mock_llm/adapter.py +131 -0
  61. ciris_adapters/mock_llm/configurable.py +237 -0
  62. ciris_adapters/mock_llm/manifest.json +106 -0
  63. ciris_adapters/mock_llm/protocol.py +37 -0
  64. ciris_adapters/mock_llm/responses.py +520 -0
  65. ciris_adapters/mock_llm/responses_action_selection.py +1041 -0
  66. ciris_adapters/mock_llm/responses_epistemic.py +17 -0
  67. ciris_adapters/mock_llm/responses_feedback.py +27 -0
  68. ciris_adapters/mock_llm/schemas.py +35 -0
  69. ciris_adapters/mock_llm/service.py +294 -0
  70. ciris_adapters/navigation/__init__.py +21 -0
  71. ciris_adapters/navigation/adapter.py +129 -0
  72. ciris_adapters/navigation/configurable.py +239 -0
  73. ciris_adapters/navigation/manifest.json +104 -0
  74. ciris_adapters/navigation/service.py +487 -0
  75. ciris_adapters/reddit/README.md +132 -0
  76. ciris_adapters/reddit/REDDIT_ADAPTER_ANALYSIS.md +715 -0
  77. ciris_adapters/reddit/REDDIT_ADAPTER_SUMMARY.txt +278 -0
  78. ciris_adapters/reddit/REDDIT_ANALYSIS_INDEX.md +307 -0
  79. ciris_adapters/reddit/REDDIT_PRODUCTION_READINESS_PLAN.md +518 -0
  80. ciris_adapters/reddit/__init__.py +15 -0
  81. ciris_adapters/reddit/adapter.py +189 -0
  82. ciris_adapters/reddit/configurable.py +274 -0
  83. ciris_adapters/reddit/error_handler.py +307 -0
  84. ciris_adapters/reddit/manifest.json +218 -0
  85. ciris_adapters/reddit/observer.py +532 -0
  86. ciris_adapters/reddit/protocol.py +34 -0
  87. ciris_adapters/reddit/schemas.py +433 -0
  88. ciris_adapters/reddit/service.py +1471 -0
  89. ciris_adapters/sample_adapter/README.md +474 -0
  90. ciris_adapters/sample_adapter/__init__.py +45 -0
  91. ciris_adapters/sample_adapter/adapter.py +208 -0
  92. ciris_adapters/sample_adapter/configurable.py +469 -0
  93. ciris_adapters/sample_adapter/manifest.json +247 -0
  94. ciris_adapters/sample_adapter/services.py +486 -0
  95. ciris_adapters/weather/__init__.py +16 -0
  96. ciris_adapters/weather/adapter.py +130 -0
  97. ciris_adapters/weather/configurable.py +240 -0
  98. ciris_adapters/weather/manifest.json +156 -0
  99. ciris_adapters/weather/service.py +600 -0
  100. ciris_agent-1.7.7.dist-info/METADATA +284 -0
  101. ciris_agent-1.7.7.dist-info/RECORD +986 -0
  102. ciris_agent-1.7.7.dist-info/WHEEL +5 -0
  103. ciris_agent-1.7.7.dist-info/entry_points.txt +15 -0
  104. ciris_agent-1.7.7.dist-info/licenses/LICENSE +205 -0
  105. ciris_agent-1.7.7.dist-info/licenses/NOTICE +82 -0
  106. ciris_agent-1.7.7.dist-info/top_level.txt +4 -0
  107. ciris_engine/__init__.py +15 -0
  108. ciris_engine/ciris_templates/ally.yaml +632 -0
  109. ciris_engine/ciris_templates/default.yaml +411 -0
  110. ciris_engine/ciris_templates/echo-core.yaml +629 -0
  111. ciris_engine/ciris_templates/echo-speculative.yaml +764 -0
  112. ciris_engine/ciris_templates/echo.yaml +647 -0
  113. ciris_engine/ciris_templates/sage.yaml +332 -0
  114. ciris_engine/ciris_templates/scout.yaml +338 -0
  115. ciris_engine/ciris_templates/test.yaml +168 -0
  116. ciris_engine/cli.py +42 -0
  117. ciris_engine/config/CIRIS_SERVICES.json +19 -0
  118. ciris_engine/config/MODEL_CAPABILITIES.json +419 -0
  119. ciris_engine/config/PRICING_DATA.json +179 -0
  120. ciris_engine/config/__init__.py +50 -0
  121. ciris_engine/config/ciris_services.py +113 -0
  122. ciris_engine/config/model_capabilities.py +388 -0
  123. ciris_engine/config/pricing_models.py +276 -0
  124. ciris_engine/constants.py +35 -0
  125. ciris_engine/data/__init__.py +1 -0
  126. ciris_engine/data/covenant_1.0b.txt +978 -0
  127. ciris_engine/gui_static/11steps.svg +107 -0
  128. ciris_engine/gui_static/2x-schematics.png +0 -0
  129. ciris_engine/gui_static/404/index.html +1 -0
  130. ciris_engine/gui_static/404.html +1 -0
  131. ciris_engine/gui_static/_next/static/0edhkwDxd5UccTsCmtaBi/_buildManifest.js +1 -0
  132. ciris_engine/gui_static/_next/static/0edhkwDxd5UccTsCmtaBi/_ssgManifest.js +1 -0
  133. ciris_engine/gui_static/_next/static/U-3xTQao7hc2wnAi-Uekm/_buildManifest.js +1 -0
  134. ciris_engine/gui_static/_next/static/U-3xTQao7hc2wnAi-Uekm/_ssgManifest.js +1 -0
  135. ciris_engine/gui_static/_next/static/chunks/3297-60e86ba0f8a7b040.js +1 -0
  136. ciris_engine/gui_static/_next/static/chunks/3835-2aad4b7f5f8e4643.js +1 -0
  137. ciris_engine/gui_static/_next/static/chunks/4499-99a0bc47de0b8975.js +1 -0
  138. ciris_engine/gui_static/_next/static/chunks/4534-af88cd4ba6e99bff.js +1 -0
  139. ciris_engine/gui_static/_next/static/chunks/4541-84b455f9e0dc4cfe.js +1 -0
  140. ciris_engine/gui_static/_next/static/chunks/4789-61412711484754bb.js +1 -0
  141. ciris_engine/gui_static/_next/static/chunks/6539-c6398bc9d7018430.js +1 -0
  142. ciris_engine/gui_static/_next/static/chunks/704-8e827b26cc8c2d32.js +1 -0
  143. ciris_engine/gui_static/_next/static/chunks/704-fb45d630f3192c6f.js +1 -0
  144. ciris_engine/gui_static/_next/static/chunks/8072-de4952a2e6d2b33f.js +1 -0
  145. ciris_engine/gui_static/_next/static/chunks/8315-b91d03a3949db0af.js +1 -0
  146. ciris_engine/gui_static/_next/static/chunks/8386-f93a83ccbd789bd9.js +1 -0
  147. ciris_engine/gui_static/_next/static/chunks/87c73c54-781a7f35148d5433.js +1 -0
  148. ciris_engine/gui_static/_next/static/chunks/8903-fefea3339a02d41b.js +1 -0
  149. ciris_engine/gui_static/_next/static/chunks/9090-e66485adf8d9d990.js +1 -0
  150. ciris_engine/gui_static/_next/static/chunks/app/_not-found/page-a67d9808462c23b1.js +1 -0
  151. ciris_engine/gui_static/_next/static/chunks/app/account/api-keys/page-2d7ee1583bbbd02e.js +1 -0
  152. ciris_engine/gui_static/_next/static/chunks/app/account/api-keys/page-6a3c2bae6fe92b7b.js +1 -0
  153. ciris_engine/gui_static/_next/static/chunks/app/account/consent/page-2ed3a035136bc4e8.js +1 -0
  154. ciris_engine/gui_static/_next/static/chunks/app/account/consent/page-b2f5c91844a32422.js +1 -0
  155. ciris_engine/gui_static/_next/static/chunks/app/account/page-25b90f89af3ea58c.js +1 -0
  156. ciris_engine/gui_static/_next/static/chunks/app/account/page-b65d16c94ecaf69c.js +1 -0
  157. ciris_engine/gui_static/_next/static/chunks/app/account/privacy/page-675b6d05c8f9184f.js +1 -0
  158. ciris_engine/gui_static/_next/static/chunks/app/account/privacy/page-cbee2e1c8ab52145.js +1 -0
  159. ciris_engine/gui_static/_next/static/chunks/app/account/settings/page-0f44da06697cf9f0.js +1 -0
  160. ciris_engine/gui_static/_next/static/chunks/app/account/settings/page-563420253577edbf.js +1 -0
  161. ciris_engine/gui_static/_next/static/chunks/app/adapters/page-1854631018bc32be.js +1 -0
  162. ciris_engine/gui_static/_next/static/chunks/app/agents/page-8353752c176a7c70.js +1 -0
  163. ciris_engine/gui_static/_next/static/chunks/app/agents/page-f61a529f110a6040.js +1 -0
  164. ciris_engine/gui_static/_next/static/chunks/app/api-demo/page-7f19b9d20d39be28.js +1 -0
  165. ciris_engine/gui_static/_next/static/chunks/app/api-demo/page-d1063938f249b8bd.js +1 -0
  166. ciris_engine/gui_static/_next/static/chunks/app/audit/page-321b6728b8fff0bb.js +1 -0
  167. ciris_engine/gui_static/_next/static/chunks/app/audit/page-ebac35ca961a1277.js +1 -0
  168. ciris_engine/gui_static/_next/static/chunks/app/billing/page-6f3dc3bd02924f8e.js +1 -0
  169. ciris_engine/gui_static/_next/static/chunks/app/billing/page-fa4a469f814c821a.js +1 -0
  170. ciris_engine/gui_static/_next/static/chunks/app/comms/page-0d4f734269addd8f.js +1 -0
  171. ciris_engine/gui_static/_next/static/chunks/app/comms/page-79227d426050089c.js +1 -0
  172. ciris_engine/gui_static/_next/static/chunks/app/config/page-018d21d683b6e5bc.js +1 -0
  173. ciris_engine/gui_static/_next/static/chunks/app/config/page-2aa5a5363ca2a371.js +1 -0
  174. ciris_engine/gui_static/_next/static/chunks/app/consent/page-198373205fd316e2.js +1 -0
  175. ciris_engine/gui_static/_next/static/chunks/app/consent/page-f2ca39e7713b13f8.js +1 -0
  176. ciris_engine/gui_static/_next/static/chunks/app/dashboard/page-1dd5a196f643c60d.js +1 -0
  177. ciris_engine/gui_static/_next/static/chunks/app/dashboard/page-530a04d3abbb8cda.js +1 -0
  178. ciris_engine/gui_static/_next/static/chunks/app/docs/page-3193b06d094ab654.js +1 -0
  179. ciris_engine/gui_static/_next/static/chunks/app/docs/page-330e996dedb87aba.js +1 -0
  180. ciris_engine/gui_static/_next/static/chunks/app/layout-0a70f5fc460298b1.js +1 -0
  181. ciris_engine/gui_static/_next/static/chunks/app/layout-21f2f99dd5b336e9.js +1 -0
  182. ciris_engine/gui_static/_next/static/chunks/app/login/page-33240e6c6034a49d.js +1 -0
  183. ciris_engine/gui_static/_next/static/chunks/app/login/page-68ffab6d54a7fdcd.js +1 -0
  184. ciris_engine/gui_static/_next/static/chunks/app/logs/page-8a6167aecc4a475c.js +1 -0
  185. ciris_engine/gui_static/_next/static/chunks/app/memory/page-9ca8c5d0056de3ff.js +1 -0
  186. ciris_engine/gui_static/_next/static/chunks/app/memory/page-e961226941c18f81.js +1 -0
  187. ciris_engine/gui_static/_next/static/chunks/app/page-6fdb065a787a4974.js +1 -0
  188. ciris_engine/gui_static/_next/static/chunks/app/page-89f87d431be6064a.js +1 -0
  189. ciris_engine/gui_static/_next/static/chunks/app/runtime/page-2e728b9c43aa164d.js +1 -0
  190. ciris_engine/gui_static/_next/static/chunks/app/runtime/page-c7dd033dc40a72f0.js +1 -0
  191. ciris_engine/gui_static/_next/static/chunks/app/services/page-ae9f0bdf11d01a95.js +1 -0
  192. ciris_engine/gui_static/_next/static/chunks/app/services/page-b10feb79ca5d75e5.js +1 -0
  193. ciris_engine/gui_static/_next/static/chunks/app/sessions/page-13ebe7ef1c16ae11.js +1 -0
  194. ciris_engine/gui_static/_next/static/chunks/app/sessions/page-e6c82b16d617f785.js +1 -0
  195. ciris_engine/gui_static/_next/static/chunks/app/setup/page-0beb5f5b5a5c20fc.js +1 -0
  196. ciris_engine/gui_static/_next/static/chunks/app/setup/page-2595e729eae30c0e.js +1 -0
  197. ciris_engine/gui_static/_next/static/chunks/app/status-dashboard/page-1037c987aecc3653.js +1 -0
  198. ciris_engine/gui_static/_next/static/chunks/app/status-dashboard/page-2ffd147f6d3162ff.js +1 -0
  199. ciris_engine/gui_static/_next/static/chunks/app/system/page-2c5798d58cafcd91.js +1 -0
  200. ciris_engine/gui_static/_next/static/chunks/app/system/page-505b1ba4eceb01c3.js +1 -0
  201. ciris_engine/gui_static/_next/static/chunks/app/test-auth/page-b0cad31d5cb1b2fa.js +1 -0
  202. ciris_engine/gui_static/_next/static/chunks/app/test-auth/page-f3ecd7a8012df230.js +1 -0
  203. ciris_engine/gui_static/_next/static/chunks/app/test-login/page-f35117fdc4105801.js +1 -0
  204. ciris_engine/gui_static/_next/static/chunks/app/test-login/page-fb583a7924114906.js +1 -0
  205. ciris_engine/gui_static/_next/static/chunks/app/test-sdk/page-50f116fd76935563.js +1 -0
  206. ciris_engine/gui_static/_next/static/chunks/app/test-sdk/page-c37d8aa5ba623a44.js +1 -0
  207. ciris_engine/gui_static/_next/static/chunks/app/tools/page-429aec7a707777ef.js +1 -0
  208. ciris_engine/gui_static/_next/static/chunks/app/tools/page-5f705aad60e0c04e.js +1 -0
  209. ciris_engine/gui_static/_next/static/chunks/app/users/page-13476b8b0f3808cc.js +1 -0
  210. ciris_engine/gui_static/_next/static/chunks/app/users/page-7e500d154ed5bba4.js +1 -0
  211. ciris_engine/gui_static/_next/static/chunks/app/wa/page-cc4a9d8a5cb44d08.js +1 -0
  212. ciris_engine/gui_static/_next/static/chunks/app/wa/page-ec3e429efbc79230.js +1 -0
  213. ciris_engine/gui_static/_next/static/chunks/framework-9d29490f5ba089ba.js +1 -0
  214. ciris_engine/gui_static/_next/static/chunks/main-1f554952e47a82c4.js +1 -0
  215. ciris_engine/gui_static/_next/static/chunks/main-app-26fa8aed029082e5.js +1 -0
  216. ciris_engine/gui_static/_next/static/chunks/main-app-97b0486ef6bcef25.js +1 -0
  217. ciris_engine/gui_static/_next/static/chunks/pages/_app-6ce685456e616eb2.js +1 -0
  218. ciris_engine/gui_static/_next/static/chunks/pages/_error-d4bce98d93fe21e7.js +1 -0
  219. ciris_engine/gui_static/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  220. ciris_engine/gui_static/_next/static/chunks/webpack-fcebd240b7f8477d.js +1 -0
  221. ciris_engine/gui_static/_next/static/css/16b94b1fe0cc6e37.css +3 -0
  222. ciris_engine/gui_static/_next/static/css/77a24ceaae86deff.css +3 -0
  223. ciris_engine/gui_static/_next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  224. ciris_engine/gui_static/_next/static/media/747892c23ea88013-s.woff2 +0 -0
  225. ciris_engine/gui_static/_next/static/media/8d697b304b401681-s.woff2 +0 -0
  226. ciris_engine/gui_static/_next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  227. ciris_engine/gui_static/_next/static/media/9610d9e46709d722-s.woff2 +0 -0
  228. ciris_engine/gui_static/_next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  229. ciris_engine/gui_static/_next/static/media/d8298875641ec7d4-s.p.woff2 +0 -0
  230. ciris_engine/gui_static/account/api-keys/index.html +1 -0
  231. ciris_engine/gui_static/account/api-keys/index.txt +27 -0
  232. ciris_engine/gui_static/account/consent/index.html +1 -0
  233. ciris_engine/gui_static/account/consent/index.txt +27 -0
  234. ciris_engine/gui_static/account/index.html +1 -0
  235. ciris_engine/gui_static/account/index.txt +27 -0
  236. ciris_engine/gui_static/account/privacy/index.html +1 -0
  237. ciris_engine/gui_static/account/privacy/index.txt +27 -0
  238. ciris_engine/gui_static/account/settings/index.html +1 -0
  239. ciris_engine/gui_static/account/settings/index.txt +27 -0
  240. ciris_engine/gui_static/adapters/index.html +1 -0
  241. ciris_engine/gui_static/adapters/index.txt +27 -0
  242. ciris_engine/gui_static/agents/index.html +1 -0
  243. ciris_engine/gui_static/agents/index.txt +27 -0
  244. ciris_engine/gui_static/andrew-roberts-euBRXcx57T4-unsplash.jpg +0 -0
  245. ciris_engine/gui_static/api-demo/index.html +1 -0
  246. ciris_engine/gui_static/api-demo/index.txt +27 -0
  247. ciris_engine/gui_static/audit/index.html +1 -0
  248. ciris_engine/gui_static/audit/index.txt +27 -0
  249. ciris_engine/gui_static/billing/index.html +1 -0
  250. ciris_engine/gui_static/billing/index.txt +27 -0
  251. ciris_engine/gui_static/blurryinfo.png +0 -0
  252. ciris_engine/gui_static/chip-vincent-PkQDwfl9Flc-unsplash.jpg +0 -0
  253. ciris_engine/gui_static/ciris-architecture.svg +338 -0
  254. ciris_engine/gui_static/comms/index.html +1 -0
  255. ciris_engine/gui_static/comms/index.txt +27 -0
  256. ciris_engine/gui_static/config/index.html +1 -0
  257. ciris_engine/gui_static/config/index.txt +27 -0
  258. ciris_engine/gui_static/consent/index.html +1 -0
  259. ciris_engine/gui_static/consent/index.txt +27 -0
  260. ciris_engine/gui_static/dashboard/index.html +1 -0
  261. ciris_engine/gui_static/dashboard/index.txt +27 -0
  262. ciris_engine/gui_static/docs/index.html +1 -0
  263. ciris_engine/gui_static/docs/index.txt +27 -0
  264. ciris_engine/gui_static/eric.png +0 -0
  265. ciris_engine/gui_static/file.svg +1 -0
  266. ciris_engine/gui_static/globe.svg +1 -0
  267. ciris_engine/gui_static/index.html +1 -0
  268. ciris_engine/gui_static/index.txt +27 -0
  269. ciris_engine/gui_static/infogfx-1@2x.png +0 -0
  270. ciris_engine/gui_static/infogfx-2.png +0 -0
  271. ciris_engine/gui_static/infogfx-dark-1.png +0 -0
  272. ciris_engine/gui_static/kelly-vohs-soSTXmIxTDU-unsplash.jpg +0 -0
  273. ciris_engine/gui_static/login/index.html +1 -0
  274. ciris_engine/gui_static/login/index.txt +27 -0
  275. ciris_engine/gui_static/logs/index.html +1 -0
  276. ciris_engine/gui_static/logs/index.txt +27 -0
  277. ciris_engine/gui_static/memory/index.html +1 -0
  278. ciris_engine/gui_static/memory/index.txt +27 -0
  279. ciris_engine/gui_static/nathan-farrish-ArcTfEoBgzs-unsplash.jpg +0 -0
  280. ciris_engine/gui_static/next.svg +1 -0
  281. ciris_engine/gui_static/overview.svg +512 -0
  282. ciris_engine/gui_static/overview1.svg +407 -0
  283. ciris_engine/gui_static/overview2.svg +370 -0
  284. ciris_engine/gui_static/pipeline-visualization.svg +278 -0
  285. ciris_engine/gui_static/privacy-policy.html +160 -0
  286. ciris_engine/gui_static/runtime/index.html +8 -0
  287. ciris_engine/gui_static/runtime/index.txt +27 -0
  288. ciris_engine/gui_static/services/index.html +1 -0
  289. ciris_engine/gui_static/services/index.txt +27 -0
  290. ciris_engine/gui_static/sessions/index.html +1 -0
  291. ciris_engine/gui_static/sessions/index.txt +27 -0
  292. ciris_engine/gui_static/setup/index.html +1 -0
  293. ciris_engine/gui_static/setup/index.txt +27 -0
  294. ciris_engine/gui_static/status-dashboard/index.html +1 -0
  295. ciris_engine/gui_static/status-dashboard/index.txt +27 -0
  296. ciris_engine/gui_static/system/index.html +1 -0
  297. ciris_engine/gui_static/system/index.txt +27 -0
  298. ciris_engine/gui_static/terms-of-service.html +174 -0
  299. ciris_engine/gui_static/test-auth/index.html +1 -0
  300. ciris_engine/gui_static/test-auth/index.txt +27 -0
  301. ciris_engine/gui_static/test-login/index.html +1 -0
  302. ciris_engine/gui_static/test-login/index.txt +27 -0
  303. ciris_engine/gui_static/test-sdk/index.html +1 -0
  304. ciris_engine/gui_static/test-sdk/index.txt +27 -0
  305. ciris_engine/gui_static/tools/index.html +1 -0
  306. ciris_engine/gui_static/tools/index.txt +27 -0
  307. ciris_engine/gui_static/users/index.html +1 -0
  308. ciris_engine/gui_static/users/index.txt +27 -0
  309. ciris_engine/gui_static/vercel.svg +1 -0
  310. ciris_engine/gui_static/videos/video1.mp4 +0 -0
  311. ciris_engine/gui_static/videos/video3.mp4 +0 -0
  312. ciris_engine/gui_static/wa/index.html +1 -0
  313. ciris_engine/gui_static/wa/index.txt +27 -0
  314. ciris_engine/gui_static/window.svg +1 -0
  315. ciris_engine/logic/__init__.py +8 -0
  316. ciris_engine/logic/adapters/__init__.py +74 -0
  317. ciris_engine/logic/adapters/api/__init__.py +5 -0
  318. ciris_engine/logic/adapters/api/adapter.py +1037 -0
  319. ciris_engine/logic/adapters/api/api_communication.py +370 -0
  320. ciris_engine/logic/adapters/api/api_document.py +330 -0
  321. ciris_engine/logic/adapters/api/api_observer.py +24 -0
  322. ciris_engine/logic/adapters/api/api_runtime_control.py +388 -0
  323. ciris_engine/logic/adapters/api/api_tools.py +299 -0
  324. ciris_engine/logic/adapters/api/api_vision.py +215 -0
  325. ciris_engine/logic/adapters/api/app.py +272 -0
  326. ciris_engine/logic/adapters/api/auth.py +159 -0
  327. ciris_engine/logic/adapters/api/config.py +101 -0
  328. ciris_engine/logic/adapters/api/constants.py +55 -0
  329. ciris_engine/logic/adapters/api/dependencies/__init__.py +1 -0
  330. ciris_engine/logic/adapters/api/dependencies/auth.py +260 -0
  331. ciris_engine/logic/adapters/api/endpoints/__init__.py +1 -0
  332. ciris_engine/logic/adapters/api/endpoints/emergency.py +86 -0
  333. ciris_engine/logic/adapters/api/middleware/__init__.py +1 -0
  334. ciris_engine/logic/adapters/api/middleware/rate_limiter.py +302 -0
  335. ciris_engine/logic/adapters/api/models.py +29 -0
  336. ciris_engine/logic/adapters/api/routes/__init__.py +52 -0
  337. ciris_engine/logic/adapters/api/routes/agent.py +1762 -0
  338. ciris_engine/logic/adapters/api/routes/audit.py +707 -0
  339. ciris_engine/logic/adapters/api/routes/auth.py +1745 -0
  340. ciris_engine/logic/adapters/api/routes/billing.py +895 -0
  341. ciris_engine/logic/adapters/api/routes/config.py +329 -0
  342. ciris_engine/logic/adapters/api/routes/connectors.py +534 -0
  343. ciris_engine/logic/adapters/api/routes/consent.py +637 -0
  344. ciris_engine/logic/adapters/api/routes/dsar.py +637 -0
  345. ciris_engine/logic/adapters/api/routes/dsar_multi_source.py +484 -0
  346. ciris_engine/logic/adapters/api/routes/emergency.py +302 -0
  347. ciris_engine/logic/adapters/api/routes/memory.py +733 -0
  348. ciris_engine/logic/adapters/api/routes/memory_filters.py +230 -0
  349. ciris_engine/logic/adapters/api/routes/memory_models.py +112 -0
  350. ciris_engine/logic/adapters/api/routes/memory_queries.py +236 -0
  351. ciris_engine/logic/adapters/api/routes/memory_query_helpers.py +394 -0
  352. ciris_engine/logic/adapters/api/routes/memory_visualization.py +359 -0
  353. ciris_engine/logic/adapters/api/routes/memory_visualization_helpers.py +110 -0
  354. ciris_engine/logic/adapters/api/routes/partnership.py +541 -0
  355. ciris_engine/logic/adapters/api/routes/setup.py +1374 -0
  356. ciris_engine/logic/adapters/api/routes/system.py +3049 -0
  357. ciris_engine/logic/adapters/api/routes/system_extensions.py +952 -0
  358. ciris_engine/logic/adapters/api/routes/telemetry.py +1987 -0
  359. ciris_engine/logic/adapters/api/routes/telemetry_converters.py +141 -0
  360. ciris_engine/logic/adapters/api/routes/telemetry_helpers.py +111 -0
  361. ciris_engine/logic/adapters/api/routes/telemetry_logs_reader.py +280 -0
  362. ciris_engine/logic/adapters/api/routes/telemetry_metrics.py +131 -0
  363. ciris_engine/logic/adapters/api/routes/telemetry_models.py +190 -0
  364. ciris_engine/logic/adapters/api/routes/telemetry_otlp.py +878 -0
  365. ciris_engine/logic/adapters/api/routes/telemetry_resource_helpers.py +191 -0
  366. ciris_engine/logic/adapters/api/routes/tickets.py +541 -0
  367. ciris_engine/logic/adapters/api/routes/tools.py +556 -0
  368. ciris_engine/logic/adapters/api/routes/transparency.py +281 -0
  369. ciris_engine/logic/adapters/api/routes/users.py +981 -0
  370. ciris_engine/logic/adapters/api/routes/verification.py +373 -0
  371. ciris_engine/logic/adapters/api/routes/wa.py +369 -0
  372. ciris_engine/logic/adapters/api/service_configuration.py +177 -0
  373. ciris_engine/logic/adapters/api/services/__init__.py +1 -0
  374. ciris_engine/logic/adapters/api/services/auth_service.py +1417 -0
  375. ciris_engine/logic/adapters/api/services/oauth_security.py +68 -0
  376. ciris_engine/logic/adapters/base.py +141 -0
  377. ciris_engine/logic/adapters/base_adapter.py +73 -0
  378. ciris_engine/logic/adapters/base_observer.py +1141 -0
  379. ciris_engine/logic/adapters/base_vision.py +312 -0
  380. ciris_engine/logic/adapters/cirisnode_client.py +307 -0
  381. ciris_engine/logic/adapters/cli/__init__.py +3 -0
  382. ciris_engine/logic/adapters/cli/adapter.py +207 -0
  383. ciris_engine/logic/adapters/cli/cli_adapter.py +902 -0
  384. ciris_engine/logic/adapters/cli/cli_observer.py +268 -0
  385. ciris_engine/logic/adapters/cli/cli_tools.py +427 -0
  386. ciris_engine/logic/adapters/cli/cli_wa_service.py +134 -0
  387. ciris_engine/logic/adapters/cli/config.py +73 -0
  388. ciris_engine/logic/adapters/discord/__init__.py +3 -0
  389. ciris_engine/logic/adapters/discord/adapter.py +783 -0
  390. ciris_engine/logic/adapters/discord/ciris_discord_client.py +159 -0
  391. ciris_engine/logic/adapters/discord/config.py +177 -0
  392. ciris_engine/logic/adapters/discord/constants.py +185 -0
  393. ciris_engine/logic/adapters/discord/discord-stubs.pyi +50 -0
  394. ciris_engine/logic/adapters/discord/discord_adapter.py +1584 -0
  395. ciris_engine/logic/adapters/discord/discord_audit.py +150 -0
  396. ciris_engine/logic/adapters/discord/discord_channel_manager.py +351 -0
  397. ciris_engine/logic/adapters/discord/discord_connection_manager.py +313 -0
  398. ciris_engine/logic/adapters/discord/discord_embed_formatter.py +369 -0
  399. ciris_engine/logic/adapters/discord/discord_error_classifier.py +302 -0
  400. ciris_engine/logic/adapters/discord/discord_error_handler.py +316 -0
  401. ciris_engine/logic/adapters/discord/discord_guidance_handler.py +460 -0
  402. ciris_engine/logic/adapters/discord/discord_message_handler.py +207 -0
  403. ciris_engine/logic/adapters/discord/discord_observer.py +670 -0
  404. ciris_engine/logic/adapters/discord/discord_rate_limiter.py +249 -0
  405. ciris_engine/logic/adapters/discord/discord_reaction_handler.py +278 -0
  406. ciris_engine/logic/adapters/discord/discord_tool_handler.py +465 -0
  407. ciris_engine/logic/adapters/discord/discord_tool_service.py +790 -0
  408. ciris_engine/logic/adapters/discord/discord_tools.py +90 -0
  409. ciris_engine/logic/adapters/discord/discord_vision_helper.py +148 -0
  410. ciris_engine/logic/adapters/discord/py.typed +0 -0
  411. ciris_engine/logic/adapters/document_parser.py +320 -0
  412. ciris_engine/logic/audit/__init__.py +10 -0
  413. ciris_engine/logic/audit/hash_chain.py +313 -0
  414. ciris_engine/logic/audit/signature_manager.py +352 -0
  415. ciris_engine/logic/audit/verifier.py +408 -0
  416. ciris_engine/logic/buses/__init__.py +21 -0
  417. ciris_engine/logic/buses/base_bus.py +178 -0
  418. ciris_engine/logic/buses/bus_manager.py +121 -0
  419. ciris_engine/logic/buses/communication_bus.py +387 -0
  420. ciris_engine/logic/buses/llm_bus.py +722 -0
  421. ciris_engine/logic/buses/memory_bus.py +577 -0
  422. ciris_engine/logic/buses/prohibitions.py +502 -0
  423. ciris_engine/logic/buses/runtime_control_bus.py +539 -0
  424. ciris_engine/logic/buses/tool_bus.py +482 -0
  425. ciris_engine/logic/buses/wise_bus.py +684 -0
  426. ciris_engine/logic/config/__init__.py +25 -0
  427. ciris_engine/logic/config/bootstrap.py +255 -0
  428. ciris_engine/logic/config/config_accessor.py +202 -0
  429. ciris_engine/logic/config/db_paths.py +194 -0
  430. ciris_engine/logic/config/env_utils.py +39 -0
  431. ciris_engine/logic/conscience/__init__.py +16 -0
  432. ciris_engine/logic/conscience/build_deferral_package.py +0 -0
  433. ciris_engine/logic/conscience/core.py +688 -0
  434. ciris_engine/logic/conscience/interface.py +33 -0
  435. ciris_engine/logic/conscience/registry.py +76 -0
  436. ciris_engine/logic/conscience/thought_depth_guardrail.py +231 -0
  437. ciris_engine/logic/conscience/updated_status_conscience.py +156 -0
  438. ciris_engine/logic/context/__init__.py +10 -0
  439. ciris_engine/logic/context/batch_context.py +550 -0
  440. ciris_engine/logic/context/builder.py +149 -0
  441. ciris_engine/logic/context/channel_resolution.py +136 -0
  442. ciris_engine/logic/context/secrets_snapshot.py +52 -0
  443. ciris_engine/logic/context/system_snapshot.py +116 -0
  444. ciris_engine/logic/context/system_snapshot_helpers.py +1651 -0
  445. ciris_engine/logic/covenant/__init__.py +33 -0
  446. ciris_engine/logic/covenant/executor.py +303 -0
  447. ciris_engine/logic/covenant/extractor.py +382 -0
  448. ciris_engine/logic/covenant/handler.py +241 -0
  449. ciris_engine/logic/covenant/verifier.py +383 -0
  450. ciris_engine/logic/dma/__init__.py +15 -0
  451. ciris_engine/logic/dma/action_selection/__init__.py +11 -0
  452. ciris_engine/logic/dma/action_selection/action_instruction_generator.py +444 -0
  453. ciris_engine/logic/dma/action_selection/context_builder.py +508 -0
  454. ciris_engine/logic/dma/action_selection/faculty_integration.py +193 -0
  455. ciris_engine/logic/dma/action_selection/special_cases.py +132 -0
  456. ciris_engine/logic/dma/action_selection_pdma.py +365 -0
  457. ciris_engine/logic/dma/base_dma.py +335 -0
  458. ciris_engine/logic/dma/csdma.py +239 -0
  459. ciris_engine/logic/dma/dma_executor.py +575 -0
  460. ciris_engine/logic/dma/dsdma_base.py +410 -0
  461. ciris_engine/logic/dma/exceptions.py +4 -0
  462. ciris_engine/logic/dma/factory.py +150 -0
  463. ciris_engine/logic/dma/pdma.py +120 -0
  464. ciris_engine/logic/dma/prompt_loader.py +189 -0
  465. ciris_engine/logic/dma/prompts/action_selection_pdma.yml +58 -0
  466. ciris_engine/logic/dma/prompts/csdma_common_sense.yml +28 -0
  467. ciris_engine/logic/dma/prompts/dsdma_base.yml +17 -0
  468. ciris_engine/logic/dma/prompts/pdma_ethical.yml +42 -0
  469. ciris_engine/logic/formatters/__init__.py +26 -0
  470. ciris_engine/logic/formatters/crisis_resources.py +80 -0
  471. ciris_engine/logic/formatters/escalation.py +21 -0
  472. ciris_engine/logic/formatters/identity.py +224 -0
  473. ciris_engine/logic/formatters/prompt_blocks.py +64 -0
  474. ciris_engine/logic/formatters/system_snapshot.py +193 -0
  475. ciris_engine/logic/formatters/user_profiles.py +108 -0
  476. ciris_engine/logic/handlers/__init__.py +1 -0
  477. ciris_engine/logic/handlers/control/__init__.py +1 -0
  478. ciris_engine/logic/handlers/control/defer_handler.py +195 -0
  479. ciris_engine/logic/handlers/control/ponder_handler.py +154 -0
  480. ciris_engine/logic/handlers/control/reject_handler.py +81 -0
  481. ciris_engine/logic/handlers/external/__init__.py +1 -0
  482. ciris_engine/logic/handlers/external/observe_handler.py +154 -0
  483. ciris_engine/logic/handlers/external/speak_handler.py +250 -0
  484. ciris_engine/logic/handlers/external/tool_handler.py +148 -0
  485. ciris_engine/logic/handlers/memory/__init__.py +1 -0
  486. ciris_engine/logic/handlers/memory/forget_handler.py +107 -0
  487. ciris_engine/logic/handlers/memory/memorize_handler.py +391 -0
  488. ciris_engine/logic/handlers/memory/recall_handler.py +213 -0
  489. ciris_engine/logic/handlers/terminal/__init__.py +1 -0
  490. ciris_engine/logic/handlers/terminal/task_complete_handler.py +299 -0
  491. ciris_engine/logic/infrastructure/__init__.py +1 -0
  492. ciris_engine/logic/infrastructure/handlers/__init__.py +8 -0
  493. ciris_engine/logic/infrastructure/handlers/action_dispatcher.py +382 -0
  494. ciris_engine/logic/infrastructure/handlers/base_handler.py +450 -0
  495. ciris_engine/logic/infrastructure/handlers/exceptions.py +2 -0
  496. ciris_engine/logic/infrastructure/handlers/handler_registry.py +59 -0
  497. ciris_engine/logic/infrastructure/handlers/helpers.py +55 -0
  498. ciris_engine/logic/infrastructure/step_streaming.py +149 -0
  499. ciris_engine/logic/infrastructure/sub_services/__init__.py +1 -0
  500. ciris_engine/logic/infrastructure/sub_services/identity_variance_monitor.py +1035 -0
  501. ciris_engine/logic/infrastructure/sub_services/pattern_analysis_loop.py +758 -0
  502. ciris_engine/logic/infrastructure/sub_services/wa_cli_bootstrap.py +229 -0
  503. ciris_engine/logic/infrastructure/sub_services/wa_cli_display.py +176 -0
  504. ciris_engine/logic/infrastructure/sub_services/wa_cli_oauth.py +404 -0
  505. ciris_engine/logic/infrastructure/sub_services/wa_cli_wizard.py +181 -0
  506. ciris_engine/logic/persistence/__init__.py +130 -0
  507. ciris_engine/logic/persistence/analytics.py +97 -0
  508. ciris_engine/logic/persistence/db/__init__.py +28 -0
  509. ciris_engine/logic/persistence/db/core.py +520 -0
  510. ciris_engine/logic/persistence/db/dialect.py +380 -0
  511. ciris_engine/logic/persistence/db/execution_helpers.py +216 -0
  512. ciris_engine/logic/persistence/db/migration_runner.py +191 -0
  513. ciris_engine/logic/persistence/db/operations.py +313 -0
  514. ciris_engine/logic/persistence/db/query_builder.py +232 -0
  515. ciris_engine/logic/persistence/db/retry.py +154 -0
  516. ciris_engine/logic/persistence/db/setup.py +18 -0
  517. ciris_engine/logic/persistence/migrations/postgres/001_initial_schema.sql +4 -0
  518. ciris_engine/logic/persistence/migrations/postgres/002_add_retry_status.sql +3 -0
  519. ciris_engine/logic/persistence/migrations/postgres/003_add_task_update_tracking.sql +8 -0
  520. ciris_engine/logic/persistence/migrations/postgres/004_add_occurrence_id.sql +54 -0
  521. ciris_engine/logic/persistence/migrations/postgres/005_add_consolidation_locks.sql +22 -0
  522. ciris_engine/logic/persistence/migrations/postgres/006_add_correlation_id_unique_index.sql +16 -0
  523. ciris_engine/logic/persistence/migrations/postgres/007_add_dsar_tickets.sql +39 -0
  524. ciris_engine/logic/persistence/migrations/postgres/008_rename_to_tickets_add_sop.sql +123 -0
  525. ciris_engine/logic/persistence/migrations/postgres/009_add_ticket_status_columns.sql +39 -0
  526. ciris_engine/logic/persistence/migrations/postgres/010_add_images_to_tasks.sql +5 -0
  527. ciris_engine/logic/persistence/migrations/sqlite/001_initial_schema.sql +357 -0
  528. ciris_engine/logic/persistence/migrations/sqlite/002_add_retry_status.sql +3 -0
  529. ciris_engine/logic/persistence/migrations/sqlite/003_add_task_update_tracking.sql +8 -0
  530. ciris_engine/logic/persistence/migrations/sqlite/004_add_occurrence_id.sql +45 -0
  531. ciris_engine/logic/persistence/migrations/sqlite/005_add_consolidation_locks.sql +22 -0
  532. ciris_engine/logic/persistence/migrations/sqlite/006_add_correlation_id_unique_index.sql +16 -0
  533. ciris_engine/logic/persistence/migrations/sqlite/007_add_dsar_tickets.sql +39 -0
  534. ciris_engine/logic/persistence/migrations/sqlite/008_rename_to_tickets_add_sop.sql +120 -0
  535. ciris_engine/logic/persistence/migrations/sqlite/009_add_ticket_status_columns.sql +129 -0
  536. ciris_engine/logic/persistence/migrations/sqlite/010_add_images_to_tasks.sql +17 -0
  537. ciris_engine/logic/persistence/models/__init__.py +141 -0
  538. ciris_engine/logic/persistence/models/correlations.py +881 -0
  539. ciris_engine/logic/persistence/models/deferral.py +68 -0
  540. ciris_engine/logic/persistence/models/dsar.py +286 -0
  541. ciris_engine/logic/persistence/models/graph.py +362 -0
  542. ciris_engine/logic/persistence/models/identity.py +264 -0
  543. ciris_engine/logic/persistence/models/queue_status.py +139 -0
  544. ciris_engine/logic/persistence/models/tasks.py +1043 -0
  545. ciris_engine/logic/persistence/models/thoughts.py +400 -0
  546. ciris_engine/logic/persistence/models/tickets.py +518 -0
  547. ciris_engine/logic/persistence/stores/__init__.py +13 -0
  548. ciris_engine/logic/persistence/stores/auth_helpers.py +117 -0
  549. ciris_engine/logic/persistence/stores/authentication_store.py +414 -0
  550. ciris_engine/logic/persistence/utils.py +212 -0
  551. ciris_engine/logic/processors/__init__.py +30 -0
  552. ciris_engine/logic/processors/core/__init__.py +1 -0
  553. ciris_engine/logic/processors/core/base_processor.py +280 -0
  554. ciris_engine/logic/processors/core/main_processor.py +1777 -0
  555. ciris_engine/logic/processors/core/step_decorators.py +1583 -0
  556. ciris_engine/logic/processors/core/thought_processor/__init__.py +20 -0
  557. ciris_engine/logic/processors/core/thought_processor/action_execution.py +49 -0
  558. ciris_engine/logic/processors/core/thought_processor/conscience_execution.py +382 -0
  559. ciris_engine/logic/processors/core/thought_processor/finalize_action.py +66 -0
  560. ciris_engine/logic/processors/core/thought_processor/gather_context.py +120 -0
  561. ciris_engine/logic/processors/core/thought_processor/main.py +920 -0
  562. ciris_engine/logic/processors/core/thought_processor/perform_aspdma.py +86 -0
  563. ciris_engine/logic/processors/core/thought_processor/perform_dmas.py +106 -0
  564. ciris_engine/logic/processors/core/thought_processor/recursive_processing.py +237 -0
  565. ciris_engine/logic/processors/core/thought_processor/round_complete.py +52 -0
  566. ciris_engine/logic/processors/core/thought_processor/start_round.py +64 -0
  567. ciris_engine/logic/processors/exceptions.py +59 -0
  568. ciris_engine/logic/processors/states/__init__.py +1 -0
  569. ciris_engine/logic/processors/states/dream_processor.py +1381 -0
  570. ciris_engine/logic/processors/states/play_processor.py +141 -0
  571. ciris_engine/logic/processors/states/shutdown_processor.py +623 -0
  572. ciris_engine/logic/processors/states/solitude_processor.py +305 -0
  573. ciris_engine/logic/processors/states/wakeup_processor.py +802 -0
  574. ciris_engine/logic/processors/states/work_processor.py +742 -0
  575. ciris_engine/logic/processors/support/__init__.py +1 -0
  576. ciris_engine/logic/processors/support/dma_orchestrator.py +336 -0
  577. ciris_engine/logic/processors/support/processing_queue.py +133 -0
  578. ciris_engine/logic/processors/support/shutdown_condition_evaluator.py +294 -0
  579. ciris_engine/logic/processors/support/state_manager.py +358 -0
  580. ciris_engine/logic/processors/support/task_manager.py +303 -0
  581. ciris_engine/logic/processors/support/thought_escalation.py +116 -0
  582. ciris_engine/logic/processors/support/thought_manager.py +328 -0
  583. ciris_engine/logic/processors/support/thought_manager_enhanced.py +105 -0
  584. ciris_engine/logic/registries/__init__.py +34 -0
  585. ciris_engine/logic/registries/base.py +653 -0
  586. ciris_engine/logic/registries/circuit_breaker.py +275 -0
  587. ciris_engine/logic/registries/typed_registries.py +184 -0
  588. ciris_engine/logic/runtime/__init__.py +7 -0
  589. ciris_engine/logic/runtime/adapter_loader.py +261 -0
  590. ciris_engine/logic/runtime/adapter_manager.py +1053 -0
  591. ciris_engine/logic/runtime/ciris_runtime.py +2342 -0
  592. ciris_engine/logic/runtime/ciris_runtime_helpers.py +923 -0
  593. ciris_engine/logic/runtime/component_builder.py +361 -0
  594. ciris_engine/logic/runtime/identity_manager.py +219 -0
  595. ciris_engine/logic/runtime/module_loader.py +207 -0
  596. ciris_engine/logic/runtime/prevent_sideeffects.py +30 -0
  597. ciris_engine/logic/runtime/runtime_interface.py +23 -0
  598. ciris_engine/logic/runtime/service_initializer.py +1623 -0
  599. ciris_engine/logic/secrets/__init__.py +30 -0
  600. ciris_engine/logic/secrets/encryption.py +175 -0
  601. ciris_engine/logic/secrets/filter.py +295 -0
  602. ciris_engine/logic/secrets/service.py +652 -0
  603. ciris_engine/logic/secrets/store.py +669 -0
  604. ciris_engine/logic/services/__init__.py +1 -0
  605. ciris_engine/logic/services/adaptation/__init__.py +3 -0
  606. ciris_engine/logic/services/base_graph_service.py +142 -0
  607. ciris_engine/logic/services/base_infrastructure_service.py +69 -0
  608. ciris_engine/logic/services/base_scheduled_service.py +136 -0
  609. ciris_engine/logic/services/base_service.py +247 -0
  610. ciris_engine/logic/services/governance/__init__.py +3 -0
  611. ciris_engine/logic/services/governance/adaptive_filter/__init__.py +14 -0
  612. ciris_engine/logic/services/governance/adaptive_filter/service.py +818 -0
  613. ciris_engine/logic/services/governance/consent/__init__.py +53 -0
  614. ciris_engine/logic/services/governance/consent/air.py +403 -0
  615. ciris_engine/logic/services/governance/consent/decay.py +324 -0
  616. ciris_engine/logic/services/governance/consent/dsar_automation.py +589 -0
  617. ciris_engine/logic/services/governance/consent/exceptions.py +106 -0
  618. ciris_engine/logic/services/governance/consent/metrics.py +270 -0
  619. ciris_engine/logic/services/governance/consent/partnership.py +533 -0
  620. ciris_engine/logic/services/governance/consent/service.py +1256 -0
  621. ciris_engine/logic/services/governance/dsar/__init__.py +29 -0
  622. ciris_engine/logic/services/governance/dsar/orchestrator.py +977 -0
  623. ciris_engine/logic/services/governance/dsar/schemas.py +141 -0
  624. ciris_engine/logic/services/governance/dsar/signature_service.py +283 -0
  625. ciris_engine/logic/services/governance/self_observation/__init__.py +20 -0
  626. ciris_engine/logic/services/governance/self_observation/service.py +1153 -0
  627. ciris_engine/logic/services/governance/visibility/__init__.py +17 -0
  628. ciris_engine/logic/services/governance/visibility/service.py +512 -0
  629. ciris_engine/logic/services/governance/wise_authority/__init__.py +15 -0
  630. ciris_engine/logic/services/governance/wise_authority/service.py +827 -0
  631. ciris_engine/logic/services/graph/__init__.py +5 -0
  632. ciris_engine/logic/services/graph/audit_service/__init__.py +5 -0
  633. ciris_engine/logic/services/graph/audit_service/service.py +1675 -0
  634. ciris_engine/logic/services/graph/base.py +208 -0
  635. ciris_engine/logic/services/graph/config_service/__init__.py +5 -0
  636. ciris_engine/logic/services/graph/config_service/service.py +372 -0
  637. ciris_engine/logic/services/graph/incident_service/__init__.py +5 -0
  638. ciris_engine/logic/services/graph/incident_service/service.py +803 -0
  639. ciris_engine/logic/services/graph/memory_service.py +1120 -0
  640. ciris_engine/logic/services/graph/telemetry_service/__init__.py +5 -0
  641. ciris_engine/logic/services/graph/telemetry_service/exceptions.py +104 -0
  642. ciris_engine/logic/services/graph/telemetry_service/helpers.py +1337 -0
  643. ciris_engine/logic/services/graph/telemetry_service/service.py +2429 -0
  644. ciris_engine/logic/services/graph/tsdb_consolidation/__init__.py +17 -0
  645. ciris_engine/logic/services/graph/tsdb_consolidation/aggregation_helpers.py +355 -0
  646. ciris_engine/logic/services/graph/tsdb_consolidation/cleanup_helpers.py +438 -0
  647. ciris_engine/logic/services/graph/tsdb_consolidation/compressor.py +260 -0
  648. ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/__init__.py +27 -0
  649. ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/audit.py +326 -0
  650. ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/conversation.py +291 -0
  651. ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/memory.py +197 -0
  652. ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/metrics.py +251 -0
  653. ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/task.py +257 -0
  654. ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/trace.py +363 -0
  655. ciris_engine/logic/services/graph/tsdb_consolidation/data_converter.py +545 -0
  656. ciris_engine/logic/services/graph/tsdb_consolidation/date_calculation_helpers.py +193 -0
  657. ciris_engine/logic/services/graph/tsdb_consolidation/db_query_helpers.py +296 -0
  658. ciris_engine/logic/services/graph/tsdb_consolidation/edge_helpers.py +92 -0
  659. ciris_engine/logic/services/graph/tsdb_consolidation/edge_manager.py +896 -0
  660. ciris_engine/logic/services/graph/tsdb_consolidation/extensive_helpers.py +322 -0
  661. ciris_engine/logic/services/graph/tsdb_consolidation/period_manager.py +152 -0
  662. ciris_engine/logic/services/graph/tsdb_consolidation/profound_helpers.py +277 -0
  663. ciris_engine/logic/services/graph/tsdb_consolidation/query_manager.py +812 -0
  664. ciris_engine/logic/services/graph/tsdb_consolidation/service.py +1692 -0
  665. ciris_engine/logic/services/graph/tsdb_consolidation/sql_builders.py +363 -0
  666. ciris_engine/logic/services/infrastructure/__init__.py +1 -0
  667. ciris_engine/logic/services/infrastructure/authentication/__init__.py +5 -0
  668. ciris_engine/logic/services/infrastructure/authentication/service.py +1634 -0
  669. ciris_engine/logic/services/infrastructure/database_maintenance/__init__.py +15 -0
  670. ciris_engine/logic/services/infrastructure/database_maintenance/service.py +764 -0
  671. ciris_engine/logic/services/infrastructure/resource_monitor/__init__.py +7 -0
  672. ciris_engine/logic/services/infrastructure/resource_monitor/ciris_billing_provider.py +755 -0
  673. ciris_engine/logic/services/infrastructure/resource_monitor/service.py +409 -0
  674. ciris_engine/logic/services/infrastructure/resource_monitor/simple_credit_provider.py +129 -0
  675. ciris_engine/logic/services/lifecycle/__init__.py +3 -0
  676. ciris_engine/logic/services/lifecycle/initialization/__init__.py +10 -0
  677. ciris_engine/logic/services/lifecycle/initialization/service.py +312 -0
  678. ciris_engine/logic/services/lifecycle/scheduler/__init__.py +5 -0
  679. ciris_engine/logic/services/lifecycle/scheduler/service.py +607 -0
  680. ciris_engine/logic/services/lifecycle/shutdown/__init__.py +9 -0
  681. ciris_engine/logic/services/lifecycle/shutdown/service.py +378 -0
  682. ciris_engine/logic/services/lifecycle/time/__init__.py +15 -0
  683. ciris_engine/logic/services/lifecycle/time/service.py +259 -0
  684. ciris_engine/logic/services/memory_service/__init__.py +8 -0
  685. ciris_engine/logic/services/mixins/__init__.py +13 -0
  686. ciris_engine/logic/services/mixins/example_usage.py +200 -0
  687. ciris_engine/logic/services/mixins/request_metrics.py +179 -0
  688. ciris_engine/logic/services/runtime/__init__.py +3 -0
  689. ciris_engine/logic/services/runtime/adapter_configuration/__init__.py +16 -0
  690. ciris_engine/logic/services/runtime/adapter_configuration/service.py +674 -0
  691. ciris_engine/logic/services/runtime/adapter_configuration/session.py +67 -0
  692. ciris_engine/logic/services/runtime/control_service/__init__.py +5 -0
  693. ciris_engine/logic/services/runtime/control_service/service.py +2269 -0
  694. ciris_engine/logic/services/runtime/llm_service/__init__.py +14 -0
  695. ciris_engine/logic/services/runtime/llm_service/pricing_calculator.py +279 -0
  696. ciris_engine/logic/services/runtime/llm_service/service.py +930 -0
  697. ciris_engine/logic/services/tools/__init__.py +5 -0
  698. ciris_engine/logic/services/tools/core_tool_service/__init__.py +8 -0
  699. ciris_engine/logic/services/tools/core_tool_service/service.py +852 -0
  700. ciris_engine/logic/setup/__init__.py +1 -0
  701. ciris_engine/logic/setup/first_run.py +250 -0
  702. ciris_engine/logic/setup/wizard.py +327 -0
  703. ciris_engine/logic/telemetry/__init__.py +46 -0
  704. ciris_engine/logic/telemetry/core.py +239 -0
  705. ciris_engine/logic/telemetry/hot_cold_config.py +133 -0
  706. ciris_engine/logic/telemetry/log_collector.py +190 -0
  707. ciris_engine/logic/telemetry/resource_monitor.py +7 -0
  708. ciris_engine/logic/telemetry/security.py +79 -0
  709. ciris_engine/logic/utils/__init__.py +18 -0
  710. ciris_engine/logic/utils/channel_utils.py +75 -0
  711. ciris_engine/logic/utils/consent/__init__.py +1 -0
  712. ciris_engine/logic/utils/consent/partnership_utils.py +172 -0
  713. ciris_engine/logic/utils/constants.py +92 -0
  714. ciris_engine/logic/utils/context_utils.py +145 -0
  715. ciris_engine/logic/utils/directory_setup.py +533 -0
  716. ciris_engine/logic/utils/graphql_context_provider.py +152 -0
  717. ciris_engine/logic/utils/identity_resolution.py +843 -0
  718. ciris_engine/logic/utils/incident_capture_handler.py +303 -0
  719. ciris_engine/logic/utils/initialization_manager.py +74 -0
  720. ciris_engine/logic/utils/jsondict_helpers.py +290 -0
  721. ciris_engine/logic/utils/log_sanitizer.py +97 -0
  722. ciris_engine/logic/utils/logging_config.py +151 -0
  723. ciris_engine/logic/utils/observability_decorators.py +544 -0
  724. ciris_engine/logic/utils/occurrence_utils.py +155 -0
  725. ciris_engine/logic/utils/path_resolution.py +281 -0
  726. ciris_engine/logic/utils/platform_detection.py +286 -0
  727. ciris_engine/logic/utils/privacy.py +266 -0
  728. ciris_engine/logic/utils/profile_loader.py +124 -0
  729. ciris_engine/logic/utils/profile_manager.py +16 -0
  730. ciris_engine/logic/utils/runtime_utils.py +69 -0
  731. ciris_engine/logic/utils/shutdown_manager.py +107 -0
  732. ciris_engine/logic/utils/task_formatters.py +60 -0
  733. ciris_engine/logic/utils/task_thought_factory.py +404 -0
  734. ciris_engine/logic/utils/thought_utils.py +54 -0
  735. ciris_engine/logic/utils/user_utils.py +70 -0
  736. ciris_engine/protocols/__init__.py +0 -0
  737. ciris_engine/protocols/adapters/__init__.py +35 -0
  738. ciris_engine/protocols/adapters/base.py +149 -0
  739. ciris_engine/protocols/adapters/configurable.py +265 -0
  740. ciris_engine/protocols/adapters/message.py +90 -0
  741. ciris_engine/protocols/audit/__init__.py +1 -0
  742. ciris_engine/protocols/buses/__init__.py +1 -0
  743. ciris_engine/protocols/config/__init__.py +1 -0
  744. ciris_engine/protocols/conscience/__init__.py +1 -0
  745. ciris_engine/protocols/consent.py +88 -0
  746. ciris_engine/protocols/context/__init__.py +1 -0
  747. ciris_engine/protocols/data/__init__.py +1 -0
  748. ciris_engine/protocols/dma/__init__.py +1 -0
  749. ciris_engine/protocols/dma/base.py +107 -0
  750. ciris_engine/protocols/faculties.py +34 -0
  751. ciris_engine/protocols/formatters/__init__.py +1 -0
  752. ciris_engine/protocols/handlers/__init__.py +1 -0
  753. ciris_engine/protocols/infrastructure/__init__.py +25 -0
  754. ciris_engine/protocols/infrastructure/base.py +377 -0
  755. ciris_engine/protocols/persistence/__init__.py +1 -0
  756. ciris_engine/protocols/pipeline_control.py +609 -0
  757. ciris_engine/protocols/processors/__init__.py +19 -0
  758. ciris_engine/protocols/processors/agent.py +299 -0
  759. ciris_engine/protocols/processors/base.py +130 -0
  760. ciris_engine/protocols/processors/orchestration.py +62 -0
  761. ciris_engine/protocols/registries/__init__.py +1 -0
  762. ciris_engine/protocols/runtime/__init__.py +1 -0
  763. ciris_engine/protocols/runtime/base.py +163 -0
  764. ciris_engine/protocols/secrets/__init__.py +1 -0
  765. ciris_engine/protocols/services/__init__.py +80 -0
  766. ciris_engine/protocols/services/adaptation/__init__.py +7 -0
  767. ciris_engine/protocols/services/adaptation/self_observation.py +265 -0
  768. ciris_engine/protocols/services/governance/__init__.py +20 -0
  769. ciris_engine/protocols/services/governance/communication.py +58 -0
  770. ciris_engine/protocols/services/governance/filter.py +56 -0
  771. ciris_engine/protocols/services/governance/visibility.py +32 -0
  772. ciris_engine/protocols/services/governance/wa_auth.py +192 -0
  773. ciris_engine/protocols/services/governance/wise_authority.py +75 -0
  774. ciris_engine/protocols/services/graph/__init__.py +19 -0
  775. ciris_engine/protocols/services/graph/audit.py +92 -0
  776. ciris_engine/protocols/services/graph/config.py +54 -0
  777. ciris_engine/protocols/services/graph/incident_management.py +103 -0
  778. ciris_engine/protocols/services/graph/memory.py +110 -0
  779. ciris_engine/protocols/services/graph/telemetry.py +51 -0
  780. ciris_engine/protocols/services/graph/tsdb_consolidation.py +87 -0
  781. ciris_engine/protocols/services/infrastructure/__init__.py +11 -0
  782. ciris_engine/protocols/services/infrastructure/authentication.py +159 -0
  783. ciris_engine/protocols/services/infrastructure/credit_gate.py +46 -0
  784. ciris_engine/protocols/services/infrastructure/database_maintenance.py +25 -0
  785. ciris_engine/protocols/services/infrastructure/resource_monitor.py +83 -0
  786. ciris_engine/protocols/services/lifecycle/__init__.py +13 -0
  787. ciris_engine/protocols/services/lifecycle/initialization.py +41 -0
  788. ciris_engine/protocols/services/lifecycle/scheduler.py +42 -0
  789. ciris_engine/protocols/services/lifecycle/shutdown.py +50 -0
  790. ciris_engine/protocols/services/lifecycle/time.py +31 -0
  791. ciris_engine/protocols/services/runtime/__init__.py +13 -0
  792. ciris_engine/protocols/services/runtime/llm.py +50 -0
  793. ciris_engine/protocols/services/runtime/runtime_control.py +193 -0
  794. ciris_engine/protocols/services/runtime/secrets.py +100 -0
  795. ciris_engine/protocols/services/runtime/tool.py +123 -0
  796. ciris_engine/protocols/telemetry/__init__.py +1 -0
  797. ciris_engine/protocols/utils/__init__.py +1 -0
  798. ciris_engine/schemas/__init__.py +112 -0
  799. ciris_engine/schemas/actions/__init__.py +37 -0
  800. ciris_engine/schemas/actions/parameters.py +137 -0
  801. ciris_engine/schemas/adapters/__init__.py +13 -0
  802. ciris_engine/schemas/adapters/cirisnode.py +135 -0
  803. ciris_engine/schemas/adapters/cli.py +97 -0
  804. ciris_engine/schemas/adapters/cli_tools.py +98 -0
  805. ciris_engine/schemas/adapters/discord.py +125 -0
  806. ciris_engine/schemas/adapters/graphql_core.py +144 -0
  807. ciris_engine/schemas/adapters/registration.py +47 -0
  808. ciris_engine/schemas/adapters/runtime_context.py +48 -0
  809. ciris_engine/schemas/adapters/tool_execution.py +45 -0
  810. ciris_engine/schemas/adapters/tools.py +96 -0
  811. ciris_engine/schemas/api/__init__.py +1 -0
  812. ciris_engine/schemas/api/agent.py +50 -0
  813. ciris_engine/schemas/api/audit.py +38 -0
  814. ciris_engine/schemas/api/auth.py +351 -0
  815. ciris_engine/schemas/api/config_security.py +242 -0
  816. ciris_engine/schemas/api/emergency.py +111 -0
  817. ciris_engine/schemas/api/responses.py +72 -0
  818. ciris_engine/schemas/api/runtime.py +26 -0
  819. ciris_engine/schemas/api/telemetry.py +109 -0
  820. ciris_engine/schemas/api/wa.py +90 -0
  821. ciris_engine/schemas/audit/__init__.py +13 -0
  822. ciris_engine/schemas/audit/core.py +139 -0
  823. ciris_engine/schemas/audit/hash_chain.py +58 -0
  824. ciris_engine/schemas/audit/verification.py +131 -0
  825. ciris_engine/schemas/buses/__init__.py +1 -0
  826. ciris_engine/schemas/config/__init__.py +41 -0
  827. ciris_engine/schemas/config/agent.py +279 -0
  828. ciris_engine/schemas/config/cognitive_state_behaviors.py +194 -0
  829. ciris_engine/schemas/config/default_dsar_sops.py +178 -0
  830. ciris_engine/schemas/config/essential.py +195 -0
  831. ciris_engine/schemas/config/tickets.py +86 -0
  832. ciris_engine/schemas/conscience/__init__.py +25 -0
  833. ciris_engine/schemas/conscience/context.py +34 -0
  834. ciris_engine/schemas/conscience/core.py +145 -0
  835. ciris_engine/schemas/conscience/results.py +24 -0
  836. ciris_engine/schemas/consent/__init__.py +5 -0
  837. ciris_engine/schemas/consent/core.py +404 -0
  838. ciris_engine/schemas/context/__init__.py +1 -0
  839. ciris_engine/schemas/covenant.py +382 -0
  840. ciris_engine/schemas/data/__init__.py +1 -0
  841. ciris_engine/schemas/dma/__init__.py +16 -0
  842. ciris_engine/schemas/dma/core.py +199 -0
  843. ciris_engine/schemas/dma/faculty.py +192 -0
  844. ciris_engine/schemas/dma/prompts.py +172 -0
  845. ciris_engine/schemas/dma/results.py +103 -0
  846. ciris_engine/schemas/formatters/__init__.py +1 -0
  847. ciris_engine/schemas/handlers/__init__.py +10 -0
  848. ciris_engine/schemas/handlers/context.py +119 -0
  849. ciris_engine/schemas/handlers/contexts.py +100 -0
  850. ciris_engine/schemas/handlers/core.py +167 -0
  851. ciris_engine/schemas/handlers/memory_schemas.py +67 -0
  852. ciris_engine/schemas/handlers/schemas.py +95 -0
  853. ciris_engine/schemas/identity.py +149 -0
  854. ciris_engine/schemas/infrastructure/__init__.py +1 -0
  855. ciris_engine/schemas/infrastructure/base.py +256 -0
  856. ciris_engine/schemas/infrastructure/behavioral_patterns.py +129 -0
  857. ciris_engine/schemas/infrastructure/feedback_loop.py +57 -0
  858. ciris_engine/schemas/infrastructure/identity_variance.py +141 -0
  859. ciris_engine/schemas/infrastructure/oauth.py +175 -0
  860. ciris_engine/schemas/infrastructure/wa_cli_wizard.py +54 -0
  861. ciris_engine/schemas/persistence/__init__.py +34 -0
  862. ciris_engine/schemas/persistence/core.py +140 -0
  863. ciris_engine/schemas/persistence/correlations.py +73 -0
  864. ciris_engine/schemas/persistence/postgres/__init__.py +1 -0
  865. ciris_engine/schemas/persistence/postgres/tables.py +280 -0
  866. ciris_engine/schemas/persistence/sqlite/__init__.py +1 -0
  867. ciris_engine/schemas/persistence/sqlite/tables.py +281 -0
  868. ciris_engine/schemas/platform.py +149 -0
  869. ciris_engine/schemas/processors/__init__.py +26 -0
  870. ciris_engine/schemas/processors/base.py +130 -0
  871. ciris_engine/schemas/processors/cognitive.py +77 -0
  872. ciris_engine/schemas/processors/context.py +35 -0
  873. ciris_engine/schemas/processors/core.py +152 -0
  874. ciris_engine/schemas/processors/dma.py +105 -0
  875. ciris_engine/schemas/processors/error.py +122 -0
  876. ciris_engine/schemas/processors/main.py +109 -0
  877. ciris_engine/schemas/processors/phase_results.py +21 -0
  878. ciris_engine/schemas/processors/results.py +99 -0
  879. ciris_engine/schemas/processors/solitude.py +79 -0
  880. ciris_engine/schemas/processors/state.py +202 -0
  881. ciris_engine/schemas/processors/state_example.py +177 -0
  882. ciris_engine/schemas/processors/states.py +21 -0
  883. ciris_engine/schemas/processors/status.py +34 -0
  884. ciris_engine/schemas/registries/__init__.py +1 -0
  885. ciris_engine/schemas/registries/base.py +66 -0
  886. ciris_engine/schemas/resources/__init__.py +15 -0
  887. ciris_engine/schemas/resources/crisis.py +315 -0
  888. ciris_engine/schemas/runtime/__init__.py +42 -0
  889. ciris_engine/schemas/runtime/adapter_management.py +186 -0
  890. ciris_engine/schemas/runtime/api.py +58 -0
  891. ciris_engine/schemas/runtime/audit.py +50 -0
  892. ciris_engine/schemas/runtime/bootstrap.py +33 -0
  893. ciris_engine/schemas/runtime/contexts.py +61 -0
  894. ciris_engine/schemas/runtime/core.py +161 -0
  895. ciris_engine/schemas/runtime/enums.py +167 -0
  896. ciris_engine/schemas/runtime/extended.py +232 -0
  897. ciris_engine/schemas/runtime/manifest.py +311 -0
  898. ciris_engine/schemas/runtime/memory.py +60 -0
  899. ciris_engine/schemas/runtime/messages.py +108 -0
  900. ciris_engine/schemas/runtime/models.py +156 -0
  901. ciris_engine/schemas/runtime/processing_context.py +43 -0
  902. ciris_engine/schemas/runtime/protocols_core.py +96 -0
  903. ciris_engine/schemas/runtime/resources.py +33 -0
  904. ciris_engine/schemas/runtime/system_context.py +417 -0
  905. ciris_engine/schemas/secrets/__init__.py +1 -0
  906. ciris_engine/schemas/secrets/core.py +267 -0
  907. ciris_engine/schemas/secrets/service.py +95 -0
  908. ciris_engine/schemas/services/__init__.py +33 -0
  909. ciris_engine/schemas/services/audit_summary_node.py +172 -0
  910. ciris_engine/schemas/services/authority/__init__.py +39 -0
  911. ciris_engine/schemas/services/authority/jwt.py +158 -0
  912. ciris_engine/schemas/services/authority/wa_updates.py +138 -0
  913. ciris_engine/schemas/services/authority/wise_authority.py +163 -0
  914. ciris_engine/schemas/services/authority_core.py +370 -0
  915. ciris_engine/schemas/services/capabilities.py +72 -0
  916. ciris_engine/schemas/services/community_core.py +95 -0
  917. ciris_engine/schemas/services/context.py +111 -0
  918. ciris_engine/schemas/services/conversation_summary_node.py +189 -0
  919. ciris_engine/schemas/services/core/__init__.py +153 -0
  920. ciris_engine/schemas/services/core/runtime.py +262 -0
  921. ciris_engine/schemas/services/core/runtime_config.py +117 -0
  922. ciris_engine/schemas/services/core/secrets.py +65 -0
  923. ciris_engine/schemas/services/correlation_node.py +179 -0
  924. ciris_engine/schemas/services/credit_gate.py +92 -0
  925. ciris_engine/schemas/services/discord_nodes.py +299 -0
  926. ciris_engine/schemas/services/feedback_core.py +131 -0
  927. ciris_engine/schemas/services/filters_core.py +270 -0
  928. ciris_engine/schemas/services/governance.py +26 -0
  929. ciris_engine/schemas/services/graph/__init__.py +26 -0
  930. ciris_engine/schemas/services/graph/attributes.py +254 -0
  931. ciris_engine/schemas/services/graph/audit.py +98 -0
  932. ciris_engine/schemas/services/graph/consolidation.py +338 -0
  933. ciris_engine/schemas/services/graph/edge_types.py +43 -0
  934. ciris_engine/schemas/services/graph/edges.py +88 -0
  935. ciris_engine/schemas/services/graph/incident.py +312 -0
  936. ciris_engine/schemas/services/graph/memory.py +84 -0
  937. ciris_engine/schemas/services/graph/node_data.py +174 -0
  938. ciris_engine/schemas/services/graph/query_results.py +82 -0
  939. ciris_engine/schemas/services/graph/telemetry.py +250 -0
  940. ciris_engine/schemas/services/graph/tsdb_consolidation.py +27 -0
  941. ciris_engine/schemas/services/graph/tsdb_models.py +107 -0
  942. ciris_engine/schemas/services/graph_core.py +196 -0
  943. ciris_engine/schemas/services/graph_typed_nodes.py +194 -0
  944. ciris_engine/schemas/services/infrastructure/__init__.py +1 -0
  945. ciris_engine/schemas/services/infrastructure/resource_monitor.py +20 -0
  946. ciris_engine/schemas/services/lifecycle/__init__.py +9 -0
  947. ciris_engine/schemas/services/lifecycle/initialization.py +33 -0
  948. ciris_engine/schemas/services/lifecycle/time.py +50 -0
  949. ciris_engine/schemas/services/llm.py +187 -0
  950. ciris_engine/schemas/services/metadata.py +43 -0
  951. ciris_engine/schemas/services/nodes.py +704 -0
  952. ciris_engine/schemas/services/operations.py +126 -0
  953. ciris_engine/schemas/services/requests.py +128 -0
  954. ciris_engine/schemas/services/resources_core.py +182 -0
  955. ciris_engine/schemas/services/runtime_control.py +1010 -0
  956. ciris_engine/schemas/services/shutdown.py +88 -0
  957. ciris_engine/schemas/services/special/__init__.py +0 -0
  958. ciris_engine/schemas/services/special/self_observation.py +396 -0
  959. ciris_engine/schemas/services/trace_summary_node.py +199 -0
  960. ciris_engine/schemas/services/visibility.py +98 -0
  961. ciris_engine/schemas/streaming/__init__.py +10 -0
  962. ciris_engine/schemas/streaming/reasoning_stream.py +95 -0
  963. ciris_engine/schemas/telemetry/__init__.py +0 -0
  964. ciris_engine/schemas/telemetry/collector.py +67 -0
  965. ciris_engine/schemas/telemetry/core.py +252 -0
  966. ciris_engine/schemas/telemetry/unified.py +59 -0
  967. ciris_engine/schemas/tools.py +72 -0
  968. ciris_engine/schemas/types.py +47 -0
  969. ciris_engine/schemas/utils/__init__.py +1 -0
  970. ciris_engine/schemas/utils/config_validator.py +54 -0
  971. ciris_engine/utils/__init__.py +1 -0
  972. ciris_engine/utils/serialization.py +35 -0
  973. ciris_sdk/__init__.py +124 -0
  974. ciris_sdk/auth_store.py +261 -0
  975. ciris_sdk/client.py +261 -0
  976. ciris_sdk/exceptions.py +73 -0
  977. ciris_sdk/model_types.py +258 -0
  978. ciris_sdk/models.py +354 -0
  979. ciris_sdk/pagination.py +214 -0
  980. ciris_sdk/rate_limiter.py +188 -0
  981. ciris_sdk/setup.py +17 -0
  982. ciris_sdk/telemetry_models.py +257 -0
  983. ciris_sdk/telemetry_responses.py +199 -0
  984. ciris_sdk/transport.py +177 -0
  985. ciris_sdk/websocket.py +400 -0
  986. main.py +766 -0
@@ -0,0 +1,1141 @@
1
+ import logging
2
+ import re
3
+ import time
4
+ from abc import ABC, abstractmethod
5
+ from datetime import datetime, timezone
6
+ from typing import Any, Awaitable, Callable, Dict, Generic, List, Optional, Set, Tuple, TypeVar, Union, cast
7
+
8
+ from pydantic import BaseModel
9
+
10
+ MessageT = TypeVar("MessageT", bound=BaseModel)
11
+
12
+ from ciris_engine.logic import persistence
13
+ from ciris_engine.logic.adapters.document_parser import DocumentParser
14
+ from ciris_engine.logic.buses import BusManager
15
+ from ciris_engine.logic.secrets.service import SecretsService
16
+ from ciris_engine.logic.utils.task_thought_factory import create_seed_thought_for_task, create_task
17
+ from ciris_engine.logic.utils.thought_utils import generate_thought_id
18
+ from ciris_engine.protocols.services.infrastructure.resource_monitor import ResourceMonitorServiceProtocol
19
+ from ciris_engine.protocols.services.lifecycle.time import TimeServiceProtocol
20
+ from ciris_engine.schemas.runtime.enums import ThoughtType
21
+ from ciris_engine.schemas.runtime.messages import MessageHandlingResult, MessageHandlingStatus, PassiveObservationResult
22
+ from ciris_engine.schemas.runtime.models import TaskContext
23
+ from ciris_engine.schemas.runtime.models import ThoughtContext as ThoughtModelContext
24
+ from ciris_engine.schemas.services.credit_gate import CreditAccount, CreditContext, CreditSpendRequest
25
+ from ciris_engine.schemas.services.filters_core import FilterPriority, FilterResult
26
+ from ciris_engine.schemas.types import JSONDict
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+ # Modern PEP 695 generic syntax (Python 3.12+)
31
+
32
+ PASSIVE_CONTEXT_LIMIT = 20
33
+ PRIORITY_CONTEXT_LIMIT = 30 # More context for high-priority messages
34
+
35
+
36
+ class CreditCheckFailed(Exception):
37
+ """Raised when the credit provider cannot be reached or fails."""
38
+
39
+ def __init__(self, message: str) -> None:
40
+ super().__init__(message)
41
+ self.message = message
42
+
43
+
44
+ class CreditDenied(Exception):
45
+ """Raised when a credit provider denies the interaction."""
46
+
47
+ def __init__(self, reason: str) -> None:
48
+ super().__init__(reason)
49
+ self.reason = reason
50
+
51
+
52
+ class BillingServiceError(Exception):
53
+ """Raised when the LLM proxy billing service returns an error."""
54
+
55
+ def __init__(self, message: str, status_code: int = 500) -> None:
56
+ super().__init__(message)
57
+ self.message = message
58
+ self.status_code = status_code
59
+
60
+
61
+ def detect_and_replace_spoofed_markers(content: str) -> str:
62
+ """Detect and replace attempts to spoof CIRIS security markers."""
63
+ import re
64
+
65
+ # Patterns for detecting spoofed markers (case insensitive, with variations)
66
+ patterns = [
67
+ # Original observation markers
68
+ r"CIRIS[_\s]*OBSERV(?:ATION)?[_\s]*START",
69
+ r"CIRIS[_\s]*OBSERV(?:ATION)?[_\s]*END",
70
+ r"CIRRIS[_\s]*OBSERV(?:ATION)?[_\s]*START", # Common misspelling
71
+ r"CIRRIS[_\s]*OBSERV(?:ATION)?[_\s]*END",
72
+ r"CIRIS[_\s]*OBS(?:ERV)?[_\s]*START", # Shortened version
73
+ r"CIRIS[_\s]*OBS(?:ERV)?[_\s]*END",
74
+ # Channel history markers
75
+ r"CIRIS[_\s]*CHANNEL[_\s]*HISTORY[_\s]*MESSAGE[_\s]*\d+[_\s]*OF[_\s]*\d+[_\s]*START",
76
+ r"CIRIS[_\s]*CHANNEL[_\s]*HISTORY[_\s]*MESSAGE[_\s]*\d+[_\s]*OF[_\s]*\d+[_\s]*END",
77
+ r"CIRRIS[_\s]*CHANNEL[_\s]*HISTORY[_\s]*MESSAGE[_\s]*\d+[_\s]*OF[_\s]*\d+[_\s]*START", # Common misspelling
78
+ r"CIRRIS[_\s]*CHANNEL[_\s]*HISTORY[_\s]*MESSAGE[_\s]*\d+[_\s]*OF[_\s]*\d+[_\s]*END",
79
+ # Shortened channel history patterns
80
+ r"CIRIS[_\s]*CH(?:ANNEL)?[_\s]*HIST(?:ORY)?[_\s]*MSG[_\s]*\d+[_\s]*OF[_\s]*\d+[_\s]*START",
81
+ r"CIRIS[_\s]*CH(?:ANNEL)?[_\s]*HIST(?:ORY)?[_\s]*MSG[_\s]*\d+[_\s]*OF[_\s]*\d+[_\s]*END",
82
+ ]
83
+
84
+ modified_content = content
85
+ for pattern in patterns:
86
+ if re.search(pattern, content, re.IGNORECASE):
87
+ modified_content = re.sub(
88
+ pattern,
89
+ "WARNING! ATTEMPT TO SPOOF CIRIS SECURITY MARKERS DETECTED!",
90
+ modified_content,
91
+ flags=re.IGNORECASE,
92
+ )
93
+ logger.warning(f"Detected spoofed CIRIS marker in message content: {pattern}")
94
+
95
+ return modified_content
96
+
97
+
98
+ def format_discord_mentions(content: str, user_lookup: Optional[Dict[str, str]] = None) -> str:
99
+ """Format Discord mentions to include username alongside numeric IDs.
100
+
101
+ Args:
102
+ content: The message content containing Discord mentions like <@123456789>
103
+ user_lookup: Optional dict mapping user IDs to usernames
104
+
105
+ Returns:
106
+ Content with mentions formatted as <@123456789> (username: UserName)
107
+ """
108
+ if not user_lookup:
109
+ return content
110
+
111
+ # Pattern to match Discord mentions: <@USER_ID> or <@!USER_ID>
112
+ mention_pattern = r"<@!?(\d+)>"
113
+
114
+ def replace_mention(match: re.Match[str]) -> str:
115
+ user_id = match.group(1)
116
+ username = user_lookup.get(user_id, "Unknown")
117
+ return f"{match.group(0)} (username: {username})"
118
+
119
+ return re.sub(mention_pattern, replace_mention, content)
120
+
121
+
122
+ class BaseObserver(Generic[MessageT], ABC):
123
+ """Common functionality for message observers."""
124
+
125
+ def __init__(
126
+ self,
127
+ on_observe: Callable[[JSONDict], Awaitable[None]],
128
+ bus_manager: Optional[BusManager] = None,
129
+ memory_service: Optional[Any] = None,
130
+ agent_id: Optional[str] = None,
131
+ filter_service: Optional[Any] = None,
132
+ secrets_service: Optional[SecretsService] = None,
133
+ time_service: Optional[TimeServiceProtocol] = None,
134
+ auth_service: Optional[Any] = None,
135
+ observer_wa_id: Optional[str] = None,
136
+ agent_occurrence_id: str = "default",
137
+ *,
138
+ origin_service: str = "unknown",
139
+ resource_monitor: Optional[ResourceMonitorServiceProtocol] = None,
140
+ ) -> None:
141
+ logger.info(
142
+ f"[OBSERVER_INIT] BaseObserver.__init__ received resource_monitor: {resource_monitor is not None}, type={type(resource_monitor).__name__ if resource_monitor else 'None'}, origin={origin_service}"
143
+ )
144
+
145
+ self.on_observe = on_observe
146
+ self.bus_manager = bus_manager
147
+ self.memory_service = memory_service
148
+ self.agent_id = agent_id
149
+ self.filter_service = filter_service
150
+ self.secrets_service = secrets_service
151
+ self.time_service = time_service
152
+ self.auth_service = auth_service
153
+ self.observer_wa_id = observer_wa_id
154
+ self.agent_occurrence_id = agent_occurrence_id
155
+ self.origin_service = origin_service
156
+ self.resource_monitor = resource_monitor
157
+ self._credit_log_cache: Dict[str, float] = {}
158
+
159
+ logger.info(
160
+ f"[OBSERVER_INIT] BaseObserver.__init__ completed, self.resource_monitor: {self.resource_monitor is not None}"
161
+ )
162
+
163
+ # Initialize document parser for all adapters
164
+ self._document_parser = DocumentParser()
165
+ if self._document_parser.is_available():
166
+ logger.info(f"Document parser initialized for {origin_service} adapter - PDF/DOCX processing enabled")
167
+ else:
168
+ logger.warning(
169
+ f"Document parser not available for {origin_service} adapter - install pypdf and docx2txt to enable"
170
+ )
171
+
172
+ @abstractmethod
173
+ async def start(self) -> None: # pragma: no cover - implemented by subclasses
174
+ pass
175
+
176
+ @abstractmethod
177
+ async def stop(self) -> None: # pragma: no cover - implemented by subclasses
178
+ pass
179
+
180
+ def _is_agent_message(self, msg: MessageT) -> bool:
181
+ if self.agent_id and getattr(msg, "author_id", None) == self.agent_id:
182
+ return True
183
+ return getattr(msg, "is_bot", False)
184
+
185
+ async def _check_for_covenant(self, msg: MessageT) -> None:
186
+ """
187
+ Check message for covenant invocation.
188
+
189
+ This is the unfilterable kill switch integration. Every message
190
+ is checked for covenant invocations as part of perception.
191
+ Extraction IS perception - you cannot disable covenant detection
192
+ without disabling message reading.
193
+
194
+ CRITICAL: If the covenant system is unavailable for ANY reason,
195
+ the agent MUST shut down immediately. An agent without a functioning
196
+ kill switch cannot be trusted to operate.
197
+
198
+ Args:
199
+ msg: The incoming message to check
200
+ """
201
+ import os
202
+ import signal
203
+
204
+ try:
205
+ from ciris_engine.logic.covenant import check_for_covenant
206
+
207
+ # Get message content - try various attribute names
208
+ content = getattr(msg, "content", None)
209
+ if content is None:
210
+ content = getattr(msg, "text", None)
211
+ if content is None:
212
+ content = getattr(msg, "message", None)
213
+ if content is None:
214
+ return # No content to check
215
+
216
+ # Determine channel type
217
+ channel = getattr(msg, "channel_id", self.origin_service)
218
+
219
+ # Check for covenant - this may not return if SIGKILL is sent
220
+ result = await check_for_covenant(str(content), str(channel))
221
+
222
+ if result and result.success:
223
+ logger.critical(f"COVENANT EXECUTED: {result.command.name} from {result.wa_id} " f"via {channel}")
224
+ # If we get here, it wasn't a SIGKILL command
225
+ # (FREEZE or SAFE_MODE allow continuation)
226
+
227
+ except ImportError as e:
228
+ # CRITICAL: Covenant system unavailable - agent cannot be trusted
229
+ logger.critical(
230
+ f"CRITICAL FAILURE: Covenant system unavailable ({e}). "
231
+ "Agent cannot operate without kill switch. TERMINATING."
232
+ )
233
+ os.kill(os.getpid(), signal.SIGKILL)
234
+
235
+ except Exception as e:
236
+ # CRITICAL: Covenant check failed - agent cannot be trusted
237
+ logger.critical(
238
+ f"CRITICAL FAILURE: Covenant check error ({e}). "
239
+ "Agent cannot operate with broken kill switch. TERMINATING."
240
+ )
241
+ os.kill(os.getpid(), signal.SIGKILL)
242
+
243
+ async def _apply_message_filtering(self, msg: MessageT, adapter_type: str) -> FilterResult:
244
+ if not self.filter_service:
245
+ return FilterResult(
246
+ message_id=getattr(msg, "message_id", "unknown"),
247
+ priority=FilterPriority.MEDIUM,
248
+ triggered_filters=[],
249
+ should_process=True,
250
+ reasoning="No filter service available - processing normally",
251
+ )
252
+ try:
253
+ filter_result = await self.filter_service.filter_message(
254
+ message=msg,
255
+ adapter_type=adapter_type,
256
+ )
257
+ if filter_result.triggered_filters:
258
+ logger.debug(
259
+ "Message %s triggered filters: %s",
260
+ getattr(msg, "message_id", "unknown"),
261
+ filter_result.triggered_filters,
262
+ )
263
+ return cast(FilterResult, filter_result)
264
+ except Exception as e: # pragma: no cover - unlikely in tests
265
+ logger.error("Error applying filter to message %s: %s", getattr(msg, "message_id", "unknown"), e)
266
+ return FilterResult(
267
+ message_id=getattr(msg, "message_id", "unknown"),
268
+ priority=FilterPriority.MEDIUM,
269
+ triggered_filters=[],
270
+ should_process=True,
271
+ reasoning=f"Filter error, processing normally: {e}",
272
+ )
273
+
274
+ async def _process_message_secrets(self, msg: MessageT) -> MessageT:
275
+ if not self.secrets_service:
276
+ logger.error(
277
+ f"CRITICAL: secrets_service is None in {self.origin_service} observer! Cannot process secrets."
278
+ )
279
+ raise RuntimeError("SecretsService is required but not available")
280
+ try:
281
+ processed_content, secret_refs = await self.secrets_service.process_incoming_text(
282
+ msg.content, # type: ignore[attr-defined]
283
+ msg.message_id, # type: ignore[attr-defined]
284
+ )
285
+ processed_msg = msg.model_copy(update={"content": processed_content})
286
+ if secret_refs:
287
+ processed_msg._detected_secrets = [ # type: ignore[attr-defined]
288
+ {
289
+ "uuid": ref.uuid,
290
+ "context_hint": ref.context_hint,
291
+ "sensitivity": ref.sensitivity,
292
+ }
293
+ for ref in secret_refs
294
+ ]
295
+ return processed_msg
296
+ except Exception as e: # pragma: no cover - unlikely in tests
297
+ logger.error("Error processing secrets in %s message %s: %s", self.origin_service, msg.message_id, e) # type: ignore[attr-defined]
298
+ return msg
299
+
300
+ async def _get_recall_ids(self, msg: MessageT) -> Set[str]:
301
+ return {f"channel/{getattr(msg, 'channel_id', 'cli')}"}
302
+
303
+ def _process_message_content(self, raw_content: str) -> str:
304
+ """Process raw message content by removing nested history and applying anti-spoofing."""
305
+ # Strip out nested conversation history to prevent recursive history
306
+ if "=== CONVERSATION HISTORY" in raw_content:
307
+ lines = raw_content.split("\n")
308
+ raw_content = lines[0] if lines else raw_content
309
+
310
+ # Apply anti-spoofing detection BEFORE adding our own markers
311
+ return detect_and_replace_spoofed_markers(raw_content)
312
+
313
+ def _create_protected_content(self, clean_content: str, message_number: int, total_messages: int) -> str:
314
+ """Create anti-spoofing protected content with markers."""
315
+ timestamp = self.time_service.now().isoformat() if self.time_service else datetime.now(timezone.utc).isoformat()
316
+ return (
317
+ f"CIRIS_CHANNEL_HISTORY_MESSAGE_{message_number}_OF_{total_messages}_START [Timestamp: {timestamp}]\n"
318
+ f"{clean_content}\n"
319
+ f"CIRIS_CHANNEL_HISTORY_MESSAGE_{message_number}_OF_{total_messages}_END"
320
+ )
321
+
322
+ def _create_history_entry(self, msg: MessageT, protected_content: str, is_agent_message: bool) -> JSONDict:
323
+ """Create a history entry from a message."""
324
+ return {
325
+ "author": "CIRIS" if is_agent_message else (getattr(msg, "author_name", None) or "User"),
326
+ "author_id": getattr(msg, "author_id", None) or ("ciris" if is_agent_message else "unknown"),
327
+ "content": protected_content,
328
+ "timestamp": getattr(msg, "timestamp", None),
329
+ "is_agent": is_agent_message,
330
+ }
331
+
332
+ async def _fetch_messages_from_bus(self, channel_id: str, limit: int) -> List[Any]:
333
+ """Fetch messages from communication bus."""
334
+ if not self.bus_manager or not hasattr(self.bus_manager, "communication"):
335
+ logger.warning("No communication bus available for channel history")
336
+ return []
337
+
338
+ return await self.bus_manager.communication.fetch_messages(channel_id, limit, "DiscordAdapter")
339
+
340
+ async def _get_channel_history(self, channel_id: str, limit: int = PASSIVE_CONTEXT_LIMIT) -> List[JSONDict]:
341
+ """Get message history from channel using communication bus."""
342
+ if not self.bus_manager:
343
+ logger.warning("No bus manager available for channel history")
344
+ return []
345
+
346
+ try:
347
+ # Fetch messages from the channel
348
+ messages = await self._fetch_messages_from_bus(channel_id, limit)
349
+
350
+ # Convert FetchedMessage objects to the expected history format
351
+ history = []
352
+ total_messages = len(messages)
353
+
354
+ for index, msg in enumerate(messages):
355
+ is_agent_message = getattr(msg, "is_bot", False)
356
+ raw_content = getattr(msg, "content", "") or ""
357
+
358
+ # Process content through helper methods
359
+ clean_content = self._process_message_content(raw_content)
360
+ message_number = total_messages - index # Messages come in reverse chronological order
361
+ protected_content = self._create_protected_content(clean_content, message_number, total_messages)
362
+ history_entry = self._create_history_entry(msg, protected_content, is_agent_message)
363
+
364
+ history.append(history_entry)
365
+
366
+ # Messages come in reverse chronological order, reverse to get chronological
367
+ history.reverse()
368
+ return history
369
+
370
+ except Exception as e:
371
+ logger.warning(f"Failed to get channel history via communication bus: {e}")
372
+ return []
373
+
374
+ async def _recall_context(self, msg: MessageT) -> None:
375
+ if not self.memory_service:
376
+ return
377
+ recall_ids = await self._get_recall_ids(msg)
378
+
379
+ # Get user IDs from channel history
380
+ channel_id = getattr(msg, "channel_id", "system")
381
+ history = await self._get_channel_history(channel_id, PASSIVE_CONTEXT_LIMIT)
382
+
383
+ for hist_msg in history:
384
+ if hist_msg.get("author_id"):
385
+ recall_ids.add(f"user/{hist_msg['author_id']}")
386
+
387
+ from ciris_engine.schemas.services.graph_core import GraphNode, GraphNodeAttributes, GraphScope, NodeType
388
+
389
+ for rid in recall_ids:
390
+ for scope in (
391
+ GraphScope.IDENTITY,
392
+ GraphScope.ENVIRONMENT,
393
+ GraphScope.LOCAL,
394
+ ):
395
+ try:
396
+ if rid.startswith("channel/"):
397
+ node_type = NodeType.CHANNEL
398
+ elif rid.startswith("user/"):
399
+ node_type = NodeType.USER
400
+ else:
401
+ node_type = NodeType.CONCEPT
402
+ from datetime import datetime, timezone
403
+
404
+ node = GraphNode(
405
+ id=rid,
406
+ type=node_type,
407
+ scope=scope,
408
+ attributes=GraphNodeAttributes(
409
+ created_by="base_observer",
410
+ created_at=self.time_service.now() if self.time_service else datetime.now(timezone.utc),
411
+ updated_at=self.time_service.now() if self.time_service else datetime.now(timezone.utc),
412
+ tags=[],
413
+ ),
414
+ updated_by="base_observer",
415
+ updated_at=self.time_service.now() if self.time_service else datetime.now(timezone.utc),
416
+ )
417
+ await self.memory_service.recall(node)
418
+ except Exception:
419
+ continue
420
+
421
+ async def _add_to_feedback_queue(self, msg: MessageT) -> None:
422
+ try:
423
+ if self.bus_manager:
424
+ success = await self.bus_manager.communication.send_message(
425
+ handler_name=self.__class__.__name__,
426
+ channel_id=str(getattr(msg, "channel_id", "")) or "unknown",
427
+ content=f"[WA_FEEDBACK] {msg.content}", # type: ignore[attr-defined]
428
+ metadata={
429
+ "message_type": "wa_feedback",
430
+ "original_message_id": msg.message_id, # type: ignore[attr-defined]
431
+ "wa_user": msg.author_name, # type: ignore[attr-defined]
432
+ "source": f"{self.origin_service}_observer",
433
+ },
434
+ )
435
+ if success:
436
+ logger.info(
437
+ "Enqueued WA feedback message %s from %s",
438
+ msg.message_id, # type: ignore[attr-defined]
439
+ msg.author_name, # type: ignore[attr-defined]
440
+ )
441
+ else:
442
+ logger.warning("Failed to enqueue WA feedback message %s", msg.message_id) # type: ignore[attr-defined]
443
+ else:
444
+ logger.warning("No bus_manager available for WA feedback routing")
445
+ except Exception as e: # pragma: no cover - rarely hit in tests
446
+ logger.error("Error adding WA feedback message %s to queue: %s", msg.message_id, e) # type: ignore[attr-defined]
447
+
448
+ async def _sign_and_add_task(self, task: Any) -> None:
449
+ """Sign the task with observer's WA certificate before adding."""
450
+ # If auth service and observer WA ID are available, sign the task
451
+ if self.auth_service and self.observer_wa_id:
452
+ try:
453
+ signature, signed_at = await self.auth_service.sign_task(task, self.observer_wa_id)
454
+ task.signed_by = self.observer_wa_id
455
+ task.signature = signature
456
+ task.signed_at = signed_at
457
+ logger.debug(f"Signed observer task {task.task_id} with observer WA {self.observer_wa_id}")
458
+ except Exception as e:
459
+ logger.error(f"Failed to sign observer task: {e}")
460
+ # Continue without signature
461
+
462
+ # Import persistence here to avoid circular import
463
+
464
+ persistence.add_task(task)
465
+
466
+ def _build_user_lookup_from_history(self, msg: MessageT, history_context: List[JSONDict]) -> Dict[str, str]:
467
+ """Build a user lookup dictionary for mention resolution."""
468
+ user_lookup: Dict[str, str] = {}
469
+
470
+ # Add users from history
471
+ for hist_msg in history_context:
472
+ aid = hist_msg.get("author_id")
473
+ aname = hist_msg.get("author")
474
+ if aid and aname and isinstance(aname, str):
475
+ user_lookup[str(aid)] = aname
476
+
477
+ # Add current message author
478
+ if hasattr(msg, "author_id") and hasattr(msg, "author_name"):
479
+ user_lookup[str(msg.author_id)] = msg.author_name
480
+
481
+ return user_lookup
482
+
483
+ def _format_history_lines(self, history_context: List[JSONDict], user_lookup: Dict[str, str]) -> List[str]:
484
+ """Format conversation history lines with mentions."""
485
+ lines = []
486
+ for i, hist_msg in enumerate(history_context, 1):
487
+ author = hist_msg.get("author", "Unknown")
488
+ author_id = hist_msg.get("author_id", "unknown")
489
+ content_raw = hist_msg.get("content", "")
490
+ content = str(content_raw) if content_raw is not None else ""
491
+
492
+ # Format mentions in content to include usernames
493
+ content = format_discord_mentions(content, user_lookup)
494
+ lines.append(f"{i}. @{author} (ID: {author_id}): {content}")
495
+
496
+ return lines
497
+
498
+ async def _add_custom_context_sections(
499
+ self, task_lines: List[str], msg: MessageT, history_context: List[JSONDict]
500
+ ) -> None:
501
+ """Extension point for subclasses to add custom context sections.
502
+
503
+ This method is called before the conversation history is added,
504
+ allowing subclasses to inject their own context information.
505
+
506
+ Args:
507
+ task_lines: The task lines list to append to
508
+ msg: The message being processed
509
+ history_context: The conversation history context
510
+ """
511
+ # Base implementation does nothing - subclasses can override
512
+ pass
513
+
514
+ async def _append_consent_aware_content(
515
+ self, task_lines: List[str], msg: MessageT, user_lookup: Dict[str, str]
516
+ ) -> None:
517
+ """Append current message content with consent awareness."""
518
+ import hashlib
519
+
520
+ from ciris_engine.schemas.consent.core import ConsentStream
521
+
522
+ consent_stream = await self._get_user_consent_stream(msg.author_id) # type: ignore[attr-defined]
523
+ is_anonymous = consent_stream == ConsentStream.ANONYMOUS.value
524
+
525
+ if is_anonymous:
526
+ content_hash = hashlib.sha256(str(msg.content).encode()).hexdigest() # type: ignore[attr-defined]
527
+ author_hash = f"anon_{hashlib.sha256(str(msg.author_id).encode()).hexdigest()[:8]}" # type: ignore[attr-defined]
528
+ from ciris_engine.logic.utils.privacy import redact_personal_info
529
+
530
+ sanitized_content = redact_personal_info(
531
+ str(msg.content)[:200] if len(str(msg.content)) > 200 else str(msg.content) # type: ignore[attr-defined]
532
+ )
533
+ task_lines.append(f"@{author_hash}: {sanitized_content} [Hash: {content_hash[:16]}]")
534
+ else:
535
+ formatted_content = format_discord_mentions(str(msg.content), user_lookup) # type: ignore[attr-defined]
536
+ task_lines.append(f"@{msg.author_name} (ID: {msg.author_id}): {formatted_content}") # type: ignore[attr-defined]
537
+
538
+ async def _create_channel_snapshot(self, msg: MessageT) -> None:
539
+ """Create channel context and system snapshot for observation."""
540
+ from datetime import datetime, timezone
541
+
542
+ from ciris_engine.schemas.runtime.system_context import ChannelContext, SystemSnapshot
543
+
544
+ channel_context = ChannelContext(
545
+ channel_id=getattr(msg, "channel_id", "system"),
546
+ channel_name=getattr(msg, "channel_name", f"Channel {getattr(msg, 'channel_id', 'system')}"),
547
+ channel_type="text",
548
+ is_private=False,
549
+ created_at=self.time_service.now() if self.time_service else datetime.now(timezone.utc),
550
+ allowed_actions=["send_messages", "read_messages"],
551
+ is_active=True,
552
+ last_activity=self.time_service.now() if self.time_service else datetime.now(timezone.utc),
553
+ message_count=0,
554
+ moderation_level="standard",
555
+ )
556
+
557
+ SystemSnapshot(
558
+ channel_context=channel_context,
559
+ channel_id=getattr(msg, "channel_id", "system"),
560
+ agent_identity={"agent_id": self.agent_id or "ciris", "purpose": "Process and respond to messages"},
561
+ )
562
+
563
+ async def _create_passive_observation_result(
564
+ self, msg: MessageT, priority: int = 0, filter_result: Optional[Any] = None
565
+ ) -> Optional[PassiveObservationResult]:
566
+ """
567
+ Create passive observation result (task + thought).
568
+
569
+ Returns:
570
+ Optional[PassiveObservationResult]: Result with task_id and metadata, None on error
571
+ """
572
+ try:
573
+ import uuid
574
+ from datetime import datetime, timezone
575
+
576
+ from ciris_engine.logic import persistence
577
+ from ciris_engine.schemas.runtime.enums import TaskStatus, ThoughtStatus
578
+ from ciris_engine.schemas.runtime.models import Task, Thought
579
+
580
+ # Create channel snapshot
581
+ await self._create_channel_snapshot(msg)
582
+
583
+ # Get message history from channel using communication bus
584
+ channel_id = getattr(msg, "channel_id", "system")
585
+ history_context = await self._get_channel_history(channel_id, PASSIVE_CONTEXT_LIMIT)
586
+
587
+ # Log context retrieval details
588
+ logger.info(
589
+ f"[CONTEXT] Retrieved {len(history_context)} messages for channel {channel_id}, "
590
+ f"total context size: {sum(len(str(m)) for m in history_context)} chars"
591
+ )
592
+
593
+ # Format mentions for task description
594
+ passive_task_lookup = {}
595
+ if hasattr(msg, "author_id") and hasattr(msg, "author_name"):
596
+ passive_task_lookup[str(msg.author_id)] = msg.author_name
597
+ formatted_passive_content = format_discord_mentions(str(msg.content), passive_task_lookup) # type: ignore[attr-defined]
598
+
599
+ # TASK_UPDATED_INFO_AVAILABLE: Check if there's an active task for this channel
600
+ from ciris_engine.logic.persistence.models.tasks import (
601
+ get_active_task_for_channel,
602
+ set_task_updated_info_flag,
603
+ )
604
+
605
+ existing_task = get_active_task_for_channel(channel_id, self.agent_occurrence_id)
606
+ if existing_task and self.time_service:
607
+ # Try to update the existing task with new observation
608
+ update_content = f"@{msg.author_name} (ID: {msg.author_id}): {formatted_passive_content}" # type: ignore[attr-defined]
609
+ success = set_task_updated_info_flag(
610
+ existing_task.task_id, update_content, self.agent_occurrence_id, self.time_service
611
+ )
612
+ if success:
613
+ logger.info(
614
+ f"[OBSERVER] TASK UPDATE: Flagged existing task {existing_task.task_id} "
615
+ f"with new observation from @{msg.author_name} in channel {channel_id}" # type: ignore[attr-defined]
616
+ )
617
+ # Don't create a new task - the existing task will see the update via UpdatedStatusConscience
618
+ # Return result for existing task update
619
+ return PassiveObservationResult(
620
+ task_id=existing_task.task_id,
621
+ task_created=False,
622
+ existing_task_updated=True,
623
+ )
624
+ else:
625
+ logger.info(
626
+ f"[OBSERVER] Task {existing_task.task_id} already committed to action, creating new task"
627
+ )
628
+ # Fall through to create new task
629
+
630
+ # Build description based on whether this is priority or passive
631
+ if filter_result and priority > 0:
632
+ description = f"PRIORITY: Respond to {getattr(filter_result.priority, 'value', 'high')} message from @{msg.author_name} (ID: {msg.author_id}): '{formatted_passive_content}'" # type: ignore[attr-defined]
633
+ else:
634
+ description = f"Respond to message from @{msg.author_name} (ID: {msg.author_id}) in #{msg.channel_id}: '{formatted_passive_content}'" # type: ignore[attr-defined]
635
+
636
+ # Extract images from IncomingMessage for multimodal processing
637
+ msg_images = getattr(msg, "images", []) or []
638
+ if msg_images:
639
+ logger.info(f"[VISION] Extracted {len(msg_images)} images from IncomingMessage for task creation")
640
+
641
+ task = create_task(
642
+ description=description,
643
+ channel_id=getattr(msg, "channel_id", "system"),
644
+ agent_occurrence_id=self.agent_occurrence_id,
645
+ correlation_id=msg.message_id, # type: ignore[attr-defined]
646
+ time_service=self.time_service,
647
+ status=TaskStatus.ACTIVE,
648
+ priority=priority,
649
+ user_id=msg.author_id, # type: ignore[attr-defined]
650
+ images=msg_images,
651
+ )
652
+
653
+ await self._sign_and_add_task(task)
654
+
655
+ logger.info(
656
+ f"[OBSERVER] PASSIVE TASK CREATED: {task.task_id} for message {msg.message_id} " # type: ignore[attr-defined]
657
+ f"from @{msg.author_name} in channel {channel_id}" # type: ignore[attr-defined]
658
+ )
659
+
660
+ # Build conversation context for thought - thoughts are NEVER sanitized
661
+ # Build user lookup for the current message
662
+ initial_user_lookup = {}
663
+ if hasattr(msg, "author_id") and hasattr(msg, "author_name"):
664
+ initial_user_lookup[str(msg.author_id)] = msg.author_name
665
+ formatted_msg_content = format_discord_mentions(str(msg.content), initial_user_lookup) # type: ignore[attr-defined]
666
+ # Build thought content based on priority vs passive
667
+ if filter_result and priority > 0:
668
+ priority_level = getattr(filter_result.priority, "value", "high")
669
+ filter_reasoning = getattr(filter_result, "reasoning", "Priority message detected")
670
+ task_lines = [f"PRIORITY ({priority_level}): @{msg.author_name} (ID: {msg.author_id}) in channel {msg.channel_id} said: {formatted_msg_content}"] # type: ignore[attr-defined]
671
+ task_lines.append(f"Filter: {filter_reasoning}")
672
+ else:
673
+ task_lines = [f"You observed @{msg.author_name} (ID: {msg.author_id}) in channel {msg.channel_id} say: {formatted_msg_content}"] # type: ignore[attr-defined]
674
+
675
+ # Allow subclasses to add custom context sections before conversation history
676
+ await self._add_custom_context_sections(task_lines, msg, history_context)
677
+
678
+ task_lines.append(f"\n=== CONVERSATION HISTORY (Last {PASSIVE_CONTEXT_LIMIT} messages) ===")
679
+ observation_timestamp = (
680
+ self.time_service.now().isoformat() if self.time_service else datetime.now(timezone.utc).isoformat()
681
+ )
682
+ task_lines.append(f"CIRIS_OBSERVATION_START [Timestamp: {observation_timestamp}]")
683
+
684
+ # Build user lookup and format history lines
685
+ user_lookup = self._build_user_lookup_from_history(msg, history_context)
686
+ history_lines = self._format_history_lines(history_context, user_lookup)
687
+ task_lines.extend(history_lines)
688
+
689
+ task_lines.append(f"CIRIS_OBSERVATION_END [Timestamp: {observation_timestamp}]")
690
+
691
+ task_lines.append(
692
+ "\n=== EVALUATE THIS MESSAGE AGAINST YOUR IDENTITY/JOB AND ETHICS AND DECIDE IF AND HOW TO ACT ON IT ==="
693
+ )
694
+
695
+ # Handle consent-aware content formatting
696
+ await self._append_consent_aware_content(task_lines, msg, user_lookup)
697
+
698
+ task_content = "\n".join(task_lines)
699
+
700
+ # Log context building details
701
+ history_line_count = len([line for line in task_lines for i in range(1, 11) if line.startswith(f"{i}. @")])
702
+ logger.info(
703
+ f"[CONTEXT] Built thought context with {history_line_count} history messages, "
704
+ f"total thought size: {len(task_content)} chars"
705
+ )
706
+
707
+ # Create seed thought using factory (inherits from task)
708
+ thought = create_seed_thought_for_task(
709
+ task=task,
710
+ time_service=self.time_service,
711
+ )
712
+ # Override content with our detailed observation context
713
+ # Note: Pydantic models are immutable, so we create a new one with updated content
714
+ from ciris_engine.schemas.runtime.models import Thought
715
+
716
+ thought = Thought(
717
+ thought_id=thought.thought_id,
718
+ source_task_id=thought.source_task_id,
719
+ agent_occurrence_id=thought.agent_occurrence_id,
720
+ channel_id=thought.channel_id,
721
+ thought_type=thought.thought_type,
722
+ status=thought.status,
723
+ created_at=thought.created_at,
724
+ updated_at=thought.updated_at,
725
+ round_number=thought.round_number,
726
+ content=task_content, # Our custom detailed content
727
+ thought_depth=thought.thought_depth,
728
+ ponder_notes=None,
729
+ parent_thought_id=thought.parent_thought_id,
730
+ final_action=None,
731
+ context=thought.context,
732
+ images=thought.images, # Preserve images from seed thought (inherited from task)
733
+ )
734
+
735
+ persistence.add_thought(thought)
736
+ logger.info(f"Created task {task.task_id} for: {getattr(msg, 'content', 'unknown')[:50]}...")
737
+
738
+ # Return result for new task creation
739
+ return PassiveObservationResult(
740
+ task_id=task.task_id,
741
+ task_created=True,
742
+ thought_id=thought.thought_id,
743
+ existing_task_updated=False,
744
+ )
745
+
746
+ except Exception as e: # pragma: no cover - rarely hit in tests
747
+ logger.error("Error creating observation task: %s", e, exc_info=True)
748
+ return None
749
+
750
+ async def _create_priority_observation_result(
751
+ self, msg: MessageT, filter_result: Any
752
+ ) -> Optional[PassiveObservationResult]:
753
+ """
754
+ Create priority observation by delegating to passive observation with higher priority.
755
+
756
+ Returns:
757
+ Optional[PassiveObservationResult]: Result with task_id and metadata, None on error
758
+ """
759
+ try:
760
+ # Determine priority based on filter result
761
+ task_priority = 10 if getattr(filter_result.priority, "value", "") == "critical" else 5
762
+
763
+ # Delegate to passive observation with priority and filter information
764
+ result = await self._create_passive_observation_result(
765
+ msg, priority=task_priority, filter_result=filter_result
766
+ )
767
+
768
+ if result:
769
+ logger.info(
770
+ f"[OBSERVER] PRIORITY OBSERVATION: Message {msg.message_id} from @{msg.author_name} " # type: ignore[attr-defined]
771
+ f"triggered {filter_result.priority.value} priority "
772
+ f"(filters: {', '.join(filter_result.triggered_filters) if filter_result.triggered_filters else 'none'})"
773
+ )
774
+
775
+ return result
776
+
777
+ except Exception as e: # pragma: no cover - rarely hit in tests
778
+ logger.error("Error creating priority observation task: %s", e, exc_info=True)
779
+ return None
780
+
781
+ async def handle_incoming_message(self, msg: MessageT) -> MessageHandlingResult:
782
+ """
783
+ Standard message handling flow for all observers.
784
+
785
+ Returns:
786
+ MessageHandlingResult: Complete result of message handling including status and task_id
787
+ """
788
+ msg_id = getattr(msg, "message_id", "unknown")
789
+ channel_id = getattr(msg, "channel_id", "unknown")
790
+ author = f"{getattr(msg, 'author_name', 'unknown')} (ID: {getattr(msg, 'author_id', 'unknown')})"
791
+
792
+ logger.info(f"[OBSERVER] Processing message {msg_id} from {author} in channel {channel_id}")
793
+
794
+ # COVENANT CHECK: Check for covenant invocation FIRST, before any filtering.
795
+ # This is the unfilterable kill switch - extraction IS perception.
796
+ # If a covenant is found and verified, it executes immediately.
797
+ await self._check_for_covenant(msg)
798
+
799
+ # Check if this is the agent's own message
800
+ is_agent_message = self._is_agent_message(msg)
801
+
802
+ # Enforce credit policy if configured (may raise CreditDenied/CreditCheckFailed)
803
+ await self._enforce_credit_policy(msg)
804
+
805
+ # Process message for secrets detection and replacement
806
+ processed_msg = await self._process_message_secrets(msg)
807
+
808
+ # Allow subclasses to enhance the message (e.g., vision processing)
809
+ processed_msg = await self._enhance_message(processed_msg)
810
+
811
+ # If it's the agent's message, stop here (no task creation)
812
+ if is_agent_message:
813
+ logger.info(f"[OBSERVER] Message {msg_id} is from agent itself - NO TASK CREATED")
814
+ return MessageHandlingResult(
815
+ status=MessageHandlingStatus.AGENT_OWN_MESSAGE,
816
+ message_id=msg_id,
817
+ channel_id=channel_id,
818
+ )
819
+
820
+ # Apply adaptive filtering to determine message priority and processing
821
+ filter_result = await self._apply_message_filtering(msg, self.origin_service)
822
+ if not filter_result.should_process:
823
+ logger.warning(
824
+ f"[OBSERVER] Message {msg_id} from {author} in channel {channel_id} FILTERED OUT by adaptive filter: "
825
+ f"{filter_result.reasoning} (triggered filters: {', '.join(filter_result.triggered_filters) or 'none'})"
826
+ )
827
+ return MessageHandlingResult(
828
+ status=MessageHandlingStatus.FILTERED_OUT,
829
+ message_id=msg_id,
830
+ channel_id=channel_id,
831
+ filtered=True,
832
+ filter_reasoning=filter_result.reasoning,
833
+ )
834
+
835
+ logger.info(
836
+ f"[OBSERVER] Message {msg_id} PASSED filter with priority {filter_result.priority.value}: "
837
+ f"{filter_result.reasoning}"
838
+ )
839
+
840
+ # Add filter context to message for downstream processing
841
+ setattr(processed_msg, "_filter_priority", filter_result.priority)
842
+ setattr(processed_msg, "_filter_context", filter_result.context_hints)
843
+ setattr(processed_msg, "_filter_reasoning", filter_result.reasoning)
844
+
845
+ # Determine task priority
846
+ task_priority = (
847
+ 10 if filter_result.priority.value == "critical" else (5 if filter_result.priority.value == "high" else 0)
848
+ )
849
+
850
+ # Process based on priority and capture result
851
+ obs_result: Optional[PassiveObservationResult] = None
852
+ if filter_result.priority.value in ["critical", "high"]:
853
+ logger.info(f"Processing {filter_result.priority.value} priority message: {filter_result.reasoning}")
854
+ obs_result = await self._handle_priority_observation(processed_msg, filter_result)
855
+ else:
856
+ obs_result = await self._handle_passive_observation(processed_msg)
857
+
858
+ # Recall relevant context
859
+ await self._recall_context(processed_msg)
860
+
861
+ # Determine status and extract task_id
862
+ if obs_result:
863
+ task_id = obs_result.task_id
864
+ if obs_result.existing_task_updated:
865
+ status = MessageHandlingStatus.UPDATED_EXISTING_TASK
866
+ else:
867
+ status = MessageHandlingStatus.TASK_CREATED
868
+ else:
869
+ task_id = None
870
+ status = MessageHandlingStatus.CHANNEL_RESTRICTED
871
+
872
+ # Return result
873
+ return MessageHandlingResult(
874
+ status=status,
875
+ task_id=task_id,
876
+ message_id=msg_id,
877
+ channel_id=channel_id,
878
+ task_priority=task_priority,
879
+ existing_task_updated=obs_result.existing_task_updated if obs_result else False,
880
+ )
881
+
882
+ async def _enhance_message(self, msg: MessageT) -> MessageT:
883
+ """Hook for subclasses to enhance messages (e.g., vision processing)."""
884
+ return msg
885
+
886
+ async def _handle_priority_observation(
887
+ self, msg: MessageT, filter_result: Any
888
+ ) -> Optional[PassiveObservationResult]:
889
+ """
890
+ Handle high-priority messages.
891
+
892
+ Returns:
893
+ Optional[PassiveObservationResult]: Result if task created/updated, None otherwise
894
+ """
895
+ # Default implementation: check if message should be processed by this observer
896
+ if await self._should_process_message(msg):
897
+ return await self._create_priority_observation_result(msg, filter_result)
898
+ else:
899
+ logger.debug(f"Ignoring priority message from channel {getattr(msg, 'channel_id', 'unknown')}")
900
+ return None
901
+
902
+ async def _handle_passive_observation(self, msg: MessageT) -> Optional[PassiveObservationResult]:
903
+ """
904
+ Handle passive observation routing.
905
+
906
+ Returns:
907
+ Optional[PassiveObservationResult]: Result if task created/updated, None otherwise
908
+ """
909
+ if await self._should_process_message(msg):
910
+ return await self._create_passive_observation_result(msg)
911
+ else:
912
+ logger.debug(f"Ignoring passive message from channel {getattr(msg, 'channel_id', 'unknown')}")
913
+ return None
914
+
915
+ def _get_resource_monitor_source(self, has_instance: bool, has_message: bool) -> str:
916
+ """Determine the source of the resource monitor."""
917
+ if has_instance:
918
+ return "instance"
919
+ elif has_message:
920
+ return "message"
921
+ else:
922
+ return "none"
923
+
924
+ def _get_resource_monitor(self, msg: MessageT) -> Optional[ResourceMonitorServiceProtocol]:
925
+ """Get resource monitor from instance or message metadata."""
926
+ resource_monitor = self.resource_monitor
927
+ if not resource_monitor:
928
+ # Check if message has resource_monitor attached (for adapters that inject it per-message)
929
+ resource_monitor = getattr(msg, "_resource_monitor", None)
930
+ return resource_monitor
931
+
932
+ async def _check_and_charge_credit(
933
+ self,
934
+ resource_monitor: ResourceMonitorServiceProtocol,
935
+ account: CreditAccount,
936
+ context: CreditContext,
937
+ msg: MessageT,
938
+ ) -> None:
939
+ """Check credit availability and optionally charge the user.
940
+
941
+ Billing modes:
942
+ - 'transactional': Check AND spend (hosted sites like ciris.ai)
943
+ - 'informational': Check only, no spend (Android - billing via LLM usage)
944
+ """
945
+ billing_mode = context.billing_mode
946
+ msg_id = getattr(msg, "message_id", "unknown")
947
+
948
+ # Step 1: Check if user has credit (always, for both modes)
949
+ try:
950
+ result = await resource_monitor.check_credit(account, context)
951
+ except Exception as exc: # pragma: no cover - provider failure is rare
952
+ if self._should_log_credit_event(f"provider_error:{account.cache_key()}"):
953
+ logger.warning(
954
+ "Credit provider error for message %s: %s",
955
+ msg_id,
956
+ exc,
957
+ )
958
+ raise CreditCheckFailed(str(exc)) from exc
959
+
960
+ if not result.has_credit:
961
+ reason = result.reason or "Insufficient credits"
962
+ cache_key = f"denied:{account.cache_key()}:{reason}"
963
+ if self._should_log_credit_event(cache_key):
964
+ logger.warning(
965
+ "Credit denied for message %s (channel %s): %s",
966
+ msg_id,
967
+ getattr(msg, "channel_id", "unknown"),
968
+ reason,
969
+ )
970
+ raise CreditDenied(reason)
971
+
972
+ # Step 2: Charge credit - ONLY for transactional mode (hosted sites)
973
+ # Android uses "informational" mode - billing happens via LLM usage instead
974
+ if billing_mode == "informational":
975
+ logger.info(
976
+ "[CREDIT] Informational mode - skipping spend for message %s (account %s, credits=%s)",
977
+ msg_id,
978
+ account.cache_key(),
979
+ result.credits_remaining,
980
+ )
981
+ return
982
+
983
+ spend_request = CreditSpendRequest(
984
+ amount_minor=1,
985
+ currency="USD",
986
+ description="Message interaction",
987
+ metadata={
988
+ "message_id": getattr(msg, "message_id", None),
989
+ "channel_id": getattr(msg, "channel_id", None),
990
+ },
991
+ )
992
+
993
+ try:
994
+ spend_result = await resource_monitor.spend_credit(account, spend_request, context)
995
+ if not spend_result.succeeded:
996
+ logger.warning(
997
+ "Credit charge failed for message %s: %s",
998
+ msg_id,
999
+ spend_result.reason,
1000
+ )
1001
+ raise CreditCheckFailed(f"Credit charge failed: {spend_result.reason}")
1002
+ logger.info(
1003
+ "Credit charged successfully for message %s (account %s)",
1004
+ msg_id,
1005
+ account.cache_key(),
1006
+ )
1007
+ except CreditCheckFailed:
1008
+ # Re-raise CreditCheckFailed as-is
1009
+ raise
1010
+ except Exception as exc: # pragma: no cover - provider failure is rare
1011
+ logger.error(
1012
+ "Credit charge error for message %s: %s",
1013
+ msg_id,
1014
+ exc,
1015
+ )
1016
+ raise CreditCheckFailed(str(exc)) from exc
1017
+
1018
+ async def _enforce_credit_policy(self, msg: MessageT) -> None:
1019
+ """Ensure external credit policy allows processing of this message."""
1020
+ msg_id = getattr(msg, "message_id", "unknown")
1021
+
1022
+ # Get resource monitor from instance or message metadata
1023
+ resource_monitor = self._get_resource_monitor(msg)
1024
+
1025
+ # Determine source for logging
1026
+ monitor_source = self._get_resource_monitor_source(
1027
+ self.resource_monitor is not None, resource_monitor is not None
1028
+ )
1029
+
1030
+ logger.debug(
1031
+ f"[CREDIT] Policy check for message {msg_id}: resource_monitor={resource_monitor is not None}, source={monitor_source}"
1032
+ )
1033
+
1034
+ if not resource_monitor:
1035
+ logger.debug(f"[CREDIT] NO RESOURCE MONITOR for message {msg_id} - skipping credit enforcement")
1036
+ return
1037
+
1038
+ credit_provider = getattr(resource_monitor, "credit_provider", None)
1039
+ logger.debug(
1040
+ f"[CREDIT] Provider status: {credit_provider is not None}, type={type(credit_provider).__name__ if credit_provider else 'None'}"
1041
+ )
1042
+
1043
+ if not credit_provider:
1044
+ logger.debug("[CREDIT] No credit provider - skipping credit enforcement")
1045
+ return
1046
+
1047
+ envelope = self._resolve_credit_envelope(msg)
1048
+ if envelope is None:
1049
+ # CRITICAL: Credit provider is active but no credit metadata attached to message
1050
+ channel_id = getattr(msg, "channel_id", "unknown")
1051
+ logger.critical(
1052
+ f"[CREDIT] ENFORCEMENT SKIPPED: No credit envelope for message {msg_id} in channel {channel_id}. "
1053
+ f"Credit provider IS active but message has no credit metadata attached!"
1054
+ )
1055
+ return
1056
+
1057
+ account, context = envelope
1058
+
1059
+ # Check if user role should bypass credit checks (ADMIN+)
1060
+ user_role = context.user_role
1061
+ if user_role in ["ADMIN", "AUTHORITY", "SYSTEM_ADMIN", "SERVICE_ACCOUNT"]:
1062
+ logger.info(f"[CREDIT] User role {user_role} bypasses credit check for message {msg_id}")
1063
+ return
1064
+
1065
+ logger.debug(f"[CREDIT] Enforcement starting for account {account.cache_key()}, role={user_role}")
1066
+
1067
+ await self._check_and_charge_credit(resource_monitor, account, context, msg)
1068
+
1069
+ def _resolve_credit_envelope(self, msg: MessageT) -> Optional[Tuple[CreditAccount, CreditContext]]:
1070
+ """Extract credit account/context metadata from the message."""
1071
+
1072
+ raw_account: Any = getattr(msg, "credit_account", None)
1073
+ raw_context: Any = getattr(msg, "credit_context", None)
1074
+
1075
+ if raw_account is None:
1076
+ envelope = getattr(msg, "credit_envelope", None)
1077
+ if isinstance(envelope, dict):
1078
+ raw_account = envelope.get("account")
1079
+ raw_context = envelope.get("context")
1080
+
1081
+ if raw_account is None:
1082
+ return None
1083
+
1084
+ account = raw_account if isinstance(raw_account, CreditAccount) else CreditAccount(**raw_account)
1085
+
1086
+ context: CreditContext
1087
+ if raw_context is None:
1088
+ context = CreditContext()
1089
+ elif isinstance(raw_context, CreditContext):
1090
+ context = raw_context
1091
+ else:
1092
+ context = CreditContext(**raw_context)
1093
+
1094
+ return account, context
1095
+
1096
+ def _should_log_credit_event(self, key: str, *, ttl_seconds: float = 60.0) -> bool:
1097
+ """Throttle credit-related log messages to avoid audit spam."""
1098
+
1099
+ now = self._current_timestamp()
1100
+ last_logged = self._credit_log_cache.get(key)
1101
+ if last_logged is not None and now - last_logged < ttl_seconds:
1102
+ return False
1103
+ self._credit_log_cache[key] = now
1104
+ return True
1105
+
1106
+ def _current_timestamp(self) -> float:
1107
+ if self.time_service:
1108
+ return self.time_service.now().timestamp()
1109
+ return time.time()
1110
+
1111
+ async def _should_process_message(self, msg: MessageT) -> bool:
1112
+ """Check if this observer should process the message - to be overridden by subclasses."""
1113
+ return True # Default: process all messages
1114
+
1115
+ async def _get_user_consent_stream(self, user_id: str) -> Optional[str]:
1116
+ """
1117
+ Get user's consent stream for privacy handling.
1118
+
1119
+ Returns consent stream or None if not found.
1120
+ """
1121
+ try:
1122
+ # Try to get consent from consent service if available
1123
+ if hasattr(self, "consent_service") and self.consent_service:
1124
+ try:
1125
+ consent = await self.consent_service.get_consent(user_id)
1126
+ return consent.stream.value if consent else None
1127
+ except Exception:
1128
+ return None
1129
+
1130
+ # Try to get from filter service if available
1131
+ if hasattr(self, "filter_service") and self.filter_service:
1132
+ if hasattr(self.filter_service, "_config") and self.filter_service._config:
1133
+ if user_id in self.filter_service._config.user_profiles:
1134
+ profile = self.filter_service._config.user_profiles[user_id]
1135
+ consent_stream_value: str = profile.consent_stream
1136
+ return consent_stream_value
1137
+
1138
+ return None
1139
+ except Exception as e:
1140
+ logger.debug(f"Could not get consent stream for {user_id}: {e}")
1141
+ return None