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,1584 @@
1
+ import asyncio
2
+ import logging
3
+ import uuid
4
+ from datetime import datetime, timedelta, timezone
5
+ from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Union
6
+
7
+ import discord
8
+ from discord.errors import ConnectionClosed, HTTPException
9
+
10
+ from ciris_engine.logic import persistence
11
+ from ciris_engine.logic.adapters.base import Service
12
+ from ciris_engine.protocols.services import CommunicationService, WiseAuthorityService
13
+ from ciris_engine.protocols.services.lifecycle.time import TimeServiceProtocol
14
+ from ciris_engine.schemas.adapters.discord import DiscordApprovalData, DiscordChannelInfo, DiscordGuidanceData
15
+ from ciris_engine.schemas.adapters.tools import ToolExecutionResult
16
+ from ciris_engine.schemas.runtime.enums import ServiceType
17
+ from ciris_engine.schemas.runtime.messages import FetchedMessage, IncomingMessage
18
+ from ciris_engine.schemas.runtime.system_context import ChannelContext
19
+ from ciris_engine.schemas.services.authority.wise_authority import PendingDeferral
20
+ from ciris_engine.schemas.services.authority_core import (
21
+ DeferralApprovalContext,
22
+ DeferralRequest,
23
+ DeferralResponse,
24
+ GuidanceRequest,
25
+ GuidanceResponse,
26
+ WAPermission,
27
+ )
28
+ from ciris_engine.schemas.services.context import DeferralContext, GuidanceContext
29
+ from ciris_engine.schemas.services.core import ServiceCapabilities, ServiceStatus
30
+ from ciris_engine.schemas.services.discord_nodes import DiscordApprovalNode, DiscordDeferralNode
31
+ from ciris_engine.schemas.services.graph_core import GraphNodeAttributes, GraphScope
32
+ from ciris_engine.schemas.telemetry.core import (
33
+ ServiceCorrelation,
34
+ ServiceCorrelationStatus,
35
+ ServiceRequestData,
36
+ ServiceResponseData,
37
+ )
38
+ from ciris_engine.schemas.types import JSONDict
39
+
40
+ from .config import DiscordAdapterConfig
41
+ from .constants import ACTION_OBSERVE
42
+ from .discord_audit import DiscordAuditLogger
43
+ from .discord_channel_manager import DiscordChannelManager
44
+ from .discord_connection_manager import DiscordConnectionManager
45
+ from .discord_embed_formatter import DiscordEmbedFormatter
46
+ from .discord_error_handler import DiscordErrorHandler
47
+ from .discord_guidance_handler import DiscordGuidanceHandler
48
+ from .discord_message_handler import DiscordMessageHandler
49
+ from .discord_rate_limiter import DiscordRateLimiter
50
+ from .discord_reaction_handler import ApprovalRequest, ApprovalStatus, DiscordReactionHandler
51
+ from .discord_tool_handler import DiscordToolHandler
52
+
53
+ if TYPE_CHECKING:
54
+ from ciris_engine.protocols.services.lifecycle.time import TimeServiceProtocol
55
+ from ciris_engine.schemas.adapters.registration import AdapterServiceRegistration
56
+
57
+ logger = logging.getLogger(__name__)
58
+
59
+
60
+ class DiscordAdapter(Service, CommunicationService, WiseAuthorityService):
61
+ """
62
+ Discord adapter implementing CommunicationService and WiseAuthorityService protocols.
63
+ Coordinates specialized handlers for different aspects of Discord functionality.
64
+ """
65
+
66
+ def __init__(
67
+ self,
68
+ token: str,
69
+ bot: Optional[discord.Client] = None,
70
+ on_message: Optional[Callable[[IncomingMessage], Awaitable[None]]] = None,
71
+ time_service: Optional["TimeServiceProtocol"] = None,
72
+ bus_manager: Optional[Any] = None,
73
+ config: Optional[DiscordAdapterConfig] = None,
74
+ ) -> None:
75
+ retry_config = {
76
+ "retry": {
77
+ "global": {
78
+ "max_retries": 3,
79
+ "base_delay": 2.0,
80
+ "max_delay": 30.0,
81
+ },
82
+ "discord_api": {
83
+ "retryable_exceptions": (HTTPException, ConnectionClosed, asyncio.TimeoutError),
84
+ },
85
+ }
86
+ }
87
+ super().__init__(config=retry_config)
88
+
89
+ self.token = token
90
+ self._time_service = time_service
91
+ self.bus_manager = bus_manager
92
+ self.discord_config = config or DiscordAdapterConfig()
93
+
94
+ # Ensure we have a time service
95
+ if self._time_service is None:
96
+ from ciris_engine.logic.services.lifecycle.time import TimeService
97
+
98
+ self._time_service = TimeService()
99
+
100
+ # Pass monitored channel IDs from config
101
+ monitored_channels = self.discord_config.monitored_channel_ids if self.discord_config else []
102
+
103
+ # Get filter and consent services from bus manager if available
104
+ filter_service = None
105
+ consent_service = None
106
+ if bus_manager:
107
+ filter_service = getattr(bus_manager, "adaptive_filter_service", None)
108
+ consent_service = getattr(bus_manager, "consent_service", None)
109
+
110
+ self._channel_manager = DiscordChannelManager(
111
+ token=token,
112
+ client=bot,
113
+ on_message_callback=on_message,
114
+ monitored_channel_ids=monitored_channels,
115
+ filter_service=filter_service,
116
+ consent_service=consent_service,
117
+ )
118
+ self._message_handler = DiscordMessageHandler(bot)
119
+ self._guidance_handler = DiscordGuidanceHandler(
120
+ bot, self._time_service, self.bus_manager.memory if self.bus_manager else None
121
+ )
122
+ self._reaction_handler = DiscordReactionHandler(bot, self._time_service)
123
+ self._audit_logger = DiscordAuditLogger(self._time_service)
124
+ self._connection_manager = DiscordConnectionManager(token, bot, self._time_service)
125
+ self._error_handler = DiscordErrorHandler()
126
+ self._rate_limiter = DiscordRateLimiter()
127
+ self._embed_formatter = DiscordEmbedFormatter()
128
+ self._tool_handler = DiscordToolHandler(None, bot, self._time_service)
129
+ self._start_time: Optional[datetime] = None
130
+ self._approval_timeout_task: Optional[asyncio.Task[None]] = None
131
+
132
+ # Metrics tracking for v1.4.3 - Discord adapter metrics
133
+ self._messages_processed = 0
134
+ self._commands_handled = 0
135
+ self._errors_total = 0
136
+
137
+ # Set up connection callbacks
138
+ self._setup_connection_callbacks()
139
+
140
+ async def _retry_discord_operation(
141
+ self,
142
+ operation: Callable[..., Awaitable[Any]],
143
+ *args: Any,
144
+ operation_name: str,
145
+ config_key: str = "discord_api",
146
+ **kwargs: Any,
147
+ ) -> Any:
148
+ """Wrapper for retry_with_backoff that handles Discord-specific configuration."""
149
+ # Apply rate limiting before the operation
150
+ endpoint = kwargs.get("endpoint", operation_name)
151
+ await self._rate_limiter.acquire(endpoint)
152
+
153
+ try:
154
+ # Get retry config from base class config (which is a dict)
155
+ retry_cfg = (
156
+ self.config.get("retry", {}).get(config_key, {})
157
+ if hasattr(self, "config") and isinstance(self.config, dict)
158
+ else {}
159
+ )
160
+ result = await self.retry_with_backoff(
161
+ operation,
162
+ *args,
163
+ max_retries=retry_cfg.get("max_retries", 3),
164
+ base_delay=retry_cfg.get("base_delay", 2.0),
165
+ max_delay=retry_cfg.get("max_delay", 30.0),
166
+ # Include all connection-related errors as retryable
167
+ retryable_exceptions=retry_cfg.get(
168
+ "retryable_exceptions",
169
+ (
170
+ HTTPException, # Discord API errors
171
+ ConnectionClosed, # WebSocket closed
172
+ asyncio.TimeoutError, # Timeout errors
173
+ RuntimeError, # Session closed errors
174
+ OSError, # SSL and network errors
175
+ ConnectionError, # Base connection errors
176
+ ConnectionResetError, # Connection reset by peer
177
+ ConnectionAbortedError, # Connection aborted
178
+ ),
179
+ ),
180
+ **kwargs,
181
+ )
182
+ return result
183
+ except Exception as e:
184
+ # Handle errors with the error handler
185
+ if isinstance(e, (HTTPException, ConnectionClosed)):
186
+ error_info = self._error_handler.handle_channel_error(
187
+ kwargs.get("channel_id", "unknown"), e, operation_name
188
+ )
189
+ # Re-raise if not retryable
190
+ if not error_info.can_retry:
191
+ raise
192
+ raise
193
+
194
+ async def _emit_telemetry(
195
+ self, metric_name: str, value: float = 1.0, tags: Optional[dict[str, Union[str, float, int, bool]]] = None
196
+ ) -> None:
197
+ """Emit telemetry as TSDBGraphNode through memory bus."""
198
+ if not self.bus_manager or not self.bus_manager.memory:
199
+ return # No bus manager, can't emit telemetry
200
+
201
+ try:
202
+ # If value is in tags, extract it
203
+ if tags and "value" in tags:
204
+ value = float(tags.pop("value"))
205
+ elif tags and "execution_time" in tags:
206
+ value = float(tags["execution_time"])
207
+ elif tags and "success" in tags:
208
+ # For boolean success, use 1.0 for true, 0.0 for false
209
+ value = 1.0 if tags["success"] else 0.0
210
+
211
+ # Convert all tag values to strings as required by memorize_metric
212
+ string_tags = {k: str(v) for k, v in (tags or {}).items()}
213
+
214
+ # Use memorize_metric instead of creating GraphNode directly
215
+ await self.bus_manager.memory.memorize_metric(
216
+ metric_name=metric_name, value=value, tags=string_tags, scope="local", handler_name="adapter.discord"
217
+ )
218
+ except Exception as e:
219
+ logger.debug(f"Failed to emit telemetry {metric_name}: {e}")
220
+
221
+ async def send_message(self, channel_id: str, content: str) -> bool:
222
+ """Implementation of CommunicationService.send_message"""
223
+ # Check if client exists, but let retry logic handle connection state
224
+ if not self._client:
225
+ logger.warning(f"Discord client not initialized, cannot send message to channel {channel_id}")
226
+ return False
227
+
228
+ correlation_id = str(uuid.uuid4())
229
+ time_service = self._time_service
230
+ if time_service is None:
231
+ logger.error("Time service not initialized")
232
+ raise RuntimeError("Time service not initialized")
233
+ start_time = time_service.now()
234
+
235
+ try:
236
+ # The retry logic will handle connection issues and wait for reconnection
237
+ await self._retry_discord_operation(
238
+ self._message_handler.send_message_to_channel,
239
+ channel_id,
240
+ content,
241
+ operation_name="send_message",
242
+ config_key="discord_api",
243
+ )
244
+
245
+ end_time = time_service.now()
246
+ execution_time_ms = (end_time - start_time).total_seconds() * 1000
247
+
248
+ # result contains the return value from send_message_to_channel
249
+ persistence.add_correlation(
250
+ ServiceCorrelation(
251
+ correlation_id=correlation_id,
252
+ service_type="discord",
253
+ handler_name="DiscordAdapter",
254
+ action_type="speak",
255
+ request_data=ServiceRequestData(
256
+ service_type="discord",
257
+ method_name="send_message",
258
+ channel_id=channel_id,
259
+ parameters={"content": content},
260
+ request_timestamp=start_time,
261
+ ),
262
+ response_data=ServiceResponseData(
263
+ success=True,
264
+ result_summary="Message sent successfully",
265
+ execution_time_ms=execution_time_ms,
266
+ response_timestamp=end_time,
267
+ ),
268
+ status=ServiceCorrelationStatus.COMPLETED,
269
+ created_at=start_time,
270
+ updated_at=end_time,
271
+ timestamp=start_time,
272
+ ),
273
+ time_service,
274
+ )
275
+
276
+ # Increment message processed counter for sent messages too
277
+ self._messages_processed += 1
278
+
279
+ # Emit telemetry for message sent
280
+ await self._emit_telemetry(
281
+ "discord.message.sent",
282
+ 1.0,
283
+ {"adapter_type": "discord", "channel_id": channel_id, "execution_time": str(execution_time_ms)},
284
+ )
285
+
286
+ # Audit log the operation
287
+ await self._audit_logger.log_message_sent(
288
+ channel_id=channel_id,
289
+ author_id="discord_adapter",
290
+ message_content=content,
291
+ correlation_id=correlation_id,
292
+ )
293
+
294
+ return True
295
+ except (HTTPException, ConnectionClosed, asyncio.TimeoutError, RuntimeError, OSError, ConnectionError) as e:
296
+ # These are retryable exceptions - let them propagate so retry logic can handle them
297
+ # But first log the error for debugging
298
+ self._errors_total += 1
299
+ error_info = self._error_handler.handle_message_error(e, content, channel_id)
300
+ logger.error(f"Failed to send message via Discord: {error_info}")
301
+ # Re-raise the exception so retry logic can handle it
302
+ raise
303
+ except Exception as e:
304
+ # Handle non-retryable errors
305
+ self._errors_total += 1
306
+ error_info = self._error_handler.handle_message_error(e, content, channel_id)
307
+ logger.error(f"Failed to send message via Discord (non-retryable): {error_info}")
308
+ return False
309
+
310
+ async def fetch_messages(
311
+ self, channel_id: str, *, limit: int = 50, before: Optional[datetime] = None
312
+ ) -> List[FetchedMessage]:
313
+ """Implementation of CommunicationService.fetch_messages - fetches from Discord API to include all messages"""
314
+ # Primary: Fetch directly from Discord API to include messages from all users and bots
315
+ if self._channel_manager.client:
316
+ try:
317
+ messages_result = await self._retry_discord_operation(
318
+ self._message_handler.fetch_messages_from_channel,
319
+ channel_id,
320
+ limit,
321
+ operation_name="fetch_messages",
322
+ config_key="discord_api",
323
+ )
324
+ # Messages from handler are already FetchedMessage objects
325
+ if messages_result:
326
+ # Type narrow: we know this should be List[FetchedMessage]
327
+ return list(messages_result) if isinstance(messages_result, list) else []
328
+ except Exception as e:
329
+ logger.warning(f"Failed to fetch messages from Discord API for channel {channel_id}: {e}")
330
+
331
+ # Fallback: Try correlation database (only includes messages this agent observed/spoke)
332
+ from ciris_engine.logic.persistence import get_correlations_by_channel
333
+
334
+ try:
335
+ # Get correlations for this channel
336
+ correlations = get_correlations_by_channel(channel_id=channel_id, limit=limit)
337
+
338
+ messages = []
339
+ for corr in correlations:
340
+ # Extract message data from correlation
341
+ if corr.action_type == "speak" and corr.request_data:
342
+ # This is an outgoing message from the agent
343
+ content = ""
344
+ if hasattr(corr.request_data, "parameters") and corr.request_data.parameters:
345
+ content = corr.request_data.parameters.get("content", "")
346
+
347
+ messages.append(
348
+ FetchedMessage(
349
+ message_id=corr.correlation_id,
350
+ author_id="ciris",
351
+ author_name="CIRIS",
352
+ content=content,
353
+ timestamp=(
354
+ (corr.timestamp or corr.created_at).isoformat()
355
+ if corr.timestamp or corr.created_at
356
+ else None
357
+ ),
358
+ is_bot=True,
359
+ )
360
+ )
361
+ elif corr.action_type == ACTION_OBSERVE and corr.request_data:
362
+ # This is an incoming message from a user
363
+ content = ""
364
+ author_id = "unknown"
365
+ author_name = "User"
366
+
367
+ if hasattr(corr.request_data, "parameters") and corr.request_data.parameters:
368
+ params = corr.request_data.parameters
369
+ content = params.get("content", "")
370
+ author_id = params.get("author_id", "unknown")
371
+ author_name = params.get("author_name", "User")
372
+
373
+ messages.append(
374
+ FetchedMessage(
375
+ message_id=corr.correlation_id,
376
+ author_id=author_id,
377
+ author_name=author_name,
378
+ content=content,
379
+ timestamp=(
380
+ (corr.timestamp or corr.created_at).isoformat()
381
+ if corr.timestamp or corr.created_at
382
+ else None
383
+ ),
384
+ is_bot=False,
385
+ )
386
+ )
387
+
388
+ # Sort by timestamp
389
+ messages.sort(key=lambda m: m.timestamp or "")
390
+
391
+ return messages
392
+
393
+ except Exception as e:
394
+ logger.error(f"Failed to fetch messages from correlations for Discord channel {channel_id}: {e}")
395
+ return []
396
+
397
+ # --- WiseAuthorityService ---
398
+ async def fetch_guidance(self, context: GuidanceContext) -> Optional[str]:
399
+ """Send a guidance request to the configured guidance channel and wait for a response."""
400
+ deferral_channel_id = self.discord_config.deferral_channel_id
401
+ if not deferral_channel_id:
402
+ logger.error("DiscordAdapter: Guidance channel not configured.")
403
+ raise RuntimeError("Guidance channel not configured.")
404
+
405
+ time_service = self._time_service
406
+ if time_service is None:
407
+ logger.error("Time service not initialized")
408
+ return None
409
+ start_time = time_service.now()
410
+
411
+ try:
412
+ correlation_id = str(uuid.uuid4())
413
+ guidance_result = await self._retry_discord_operation(
414
+ self._guidance_handler.fetch_guidance_from_channel,
415
+ deferral_channel_id,
416
+ context.model_dump(),
417
+ operation_name="fetch_guidance",
418
+ config_key="discord_api",
419
+ )
420
+ # guidance_result should be a dict from fetch_guidance_from_channel
421
+ guidance = guidance_result if isinstance(guidance_result, dict) else {}
422
+
423
+ end_time = time_service.now()
424
+ execution_time_ms = (end_time - start_time).total_seconds() * 1000
425
+
426
+ persistence.add_correlation(
427
+ ServiceCorrelation(
428
+ correlation_id=correlation_id,
429
+ service_type="discord",
430
+ handler_name="DiscordAdapter",
431
+ action_type="fetch_guidance",
432
+ request_data=ServiceRequestData(
433
+ service_type="discord",
434
+ method_name="fetch_guidance",
435
+ channel_id=deferral_channel_id,
436
+ parameters={"context": str(context.model_dump())},
437
+ request_timestamp=start_time,
438
+ ),
439
+ response_data=ServiceResponseData(
440
+ success=True,
441
+ result_summary=f"Guidance received: {guidance.get('guidance', 'None')}",
442
+ execution_time_ms=execution_time_ms,
443
+ response_timestamp=end_time,
444
+ ),
445
+ status=ServiceCorrelationStatus.COMPLETED,
446
+ created_at=start_time,
447
+ updated_at=end_time,
448
+ timestamp=start_time,
449
+ ),
450
+ time_service,
451
+ )
452
+ # Note: Guidance requests are already audited via defer handler action
453
+
454
+ guidance_text = guidance.get("guidance")
455
+ return guidance_text
456
+ except Exception as e:
457
+ self._errors_total += 1
458
+ logger.exception(f"Failed to fetch guidance from Discord: {e}")
459
+ raise
460
+
461
+ async def check_authorization(self, wa_id: str, action: str, resource: Optional[str] = None) -> bool:
462
+ """Check if a Discord user is authorized for an action."""
463
+ # In Discord, authorization is based on roles:
464
+ # - AUTHORITY role can do anything
465
+ # - OBSERVER role can only observe
466
+ # - No role = no permissions
467
+ try:
468
+ if not self._channel_manager.client:
469
+ return False
470
+
471
+ # Get user from all guilds the bot is in
472
+ user = None
473
+ for guild in self._channel_manager.client.guilds:
474
+ member = guild.get_member(int(wa_id))
475
+ if member:
476
+ user = member
477
+ break
478
+
479
+ if not user:
480
+ return False
481
+
482
+ # Check roles
483
+ role_names = [role.name.upper() for role in user.roles]
484
+
485
+ # AUTHORITY can do anything
486
+ if "AUTHORITY" in role_names:
487
+ return True
488
+
489
+ # OBSERVER can only observe/read
490
+ if "OBSERVER" in role_names and action in ["read", "observe", "fetch"]:
491
+ return True
492
+
493
+ return False
494
+ except Exception as e:
495
+ logger.error(f"Error checking authorization for {wa_id}: {e}")
496
+ return False
497
+
498
+ async def request_approval(self, action: str, context: DeferralApprovalContext) -> bool:
499
+ """Request approval for an action through the deferral channel."""
500
+ deferral_channel_id = self.discord_config.deferral_channel_id
501
+ if not deferral_channel_id:
502
+ logger.error("DiscordAdapter: Deferral channel not configured.")
503
+ return False
504
+
505
+ try:
506
+ # Create approval request embed
507
+ approval_data = DiscordApprovalData(
508
+ action=action,
509
+ task_id=context.task_id,
510
+ thought_id=context.thought_id,
511
+ requester_id=context.requester_id,
512
+ action_name=context.action_name,
513
+ action_params=context.action_params or {},
514
+ channel_id=context.channel_id,
515
+ )
516
+ embed = self._embed_formatter.format_approval_request(action, approval_data)
517
+
518
+ # Get channel for sending embed
519
+ channel = await self._channel_manager.resolve_channel(deferral_channel_id)
520
+ if not channel:
521
+ logger.error(f"Could not resolve deferral channel {deferral_channel_id}")
522
+ return False
523
+
524
+ # Send embed message
525
+ sent_message = await channel.send(embed=embed)
526
+
527
+ # Create approval result container
528
+ approval_result = None
529
+
530
+ async def handle_approval(approval: ApprovalRequest) -> None:
531
+ nonlocal approval_result
532
+ approval_result = approval
533
+
534
+ # Create approval request using the sent message
535
+ approval_request = ApprovalRequest(
536
+ message_id=sent_message.id,
537
+ channel_id=int(deferral_channel_id),
538
+ request_type="action_approval",
539
+ context={
540
+ "action": action,
541
+ "task_id": context.task_id,
542
+ "thought_id": context.thought_id,
543
+ "requester_id": context.requester_id,
544
+ },
545
+ timeout_seconds=300, # 5 minute timeout
546
+ )
547
+
548
+ # Add reactions
549
+ await sent_message.add_reaction("✅")
550
+ await sent_message.add_reaction("❌")
551
+
552
+ # Register with reaction handler
553
+ self._reaction_handler._pending_approvals[sent_message.id] = approval_request
554
+ self._reaction_handler._approval_callbacks[sent_message.id] = handle_approval
555
+
556
+ # Schedule timeout
557
+ self._approval_timeout_task = asyncio.create_task(self._reaction_handler._handle_timeout(approval_request))
558
+
559
+ if not approval_request:
560
+ return False
561
+
562
+ # Wait for approval resolution (up to timeout)
563
+ max_wait = approval_request.timeout_seconds + 5
564
+ time_service = self._time_service
565
+ if time_service is None:
566
+ logger.error("Time service not initialized")
567
+ return False
568
+ start_time = time_service.now()
569
+
570
+ while approval_result is None:
571
+ await asyncio.sleep(0.5)
572
+ elapsed = (time_service.now() - start_time).total_seconds()
573
+ if elapsed > max_wait:
574
+ logger.error("Approval request timed out")
575
+ return False
576
+
577
+ # Store approval request in memory
578
+ if approval_result and self.bus_manager and self.bus_manager.memory:
579
+ try:
580
+ approval_node = DiscordApprovalNode(
581
+ id=f"discord_approval/{approval_request.message_id}",
582
+ scope=GraphScope.LOCAL,
583
+ attributes=GraphNodeAttributes(created_by="discord_adapter", tags=["discord", "approval"]),
584
+ approval_id=str(approval_request.message_id),
585
+ action=action,
586
+ request_type="action_approval",
587
+ channel_id=deferral_channel_id,
588
+ message_id=str(approval_request.message_id),
589
+ task_id=context.task_id,
590
+ thought_id=context.thought_id,
591
+ requester_id=context.requester_id,
592
+ status=approval_result.status.value,
593
+ resolved_at=approval_result.resolved_at,
594
+ resolver_id=approval_result.resolver_id,
595
+ resolver_name=approval_result.resolver_name,
596
+ context={"channel_id": context.channel_id} if context.channel_id else {},
597
+ action_params=context.action_params,
598
+ updated_at=time_service.now(),
599
+ updated_by="discord_adapter",
600
+ )
601
+
602
+ await self.bus_manager.memory.store(
603
+ node_id=str(approval_request.message_id),
604
+ node_type="DISCORD_APPROVAL",
605
+ attributes=approval_node.to_graph_node().attributes,
606
+ scope="local",
607
+ handler_name="discord_adapter",
608
+ )
609
+ except Exception as e:
610
+ logger.error(f"Failed to store approval in memory: {e}")
611
+
612
+ # Note: Approval requests are already audited via handler actions
613
+
614
+ # Return true only if approved
615
+ return approval_result.status == ApprovalStatus.APPROVED
616
+
617
+ except Exception as e:
618
+ self._errors_total += 1
619
+ logger.exception(f"Failed to request approval: {e}")
620
+ return False
621
+
622
+ async def get_guidance(self, request: GuidanceRequest) -> GuidanceResponse:
623
+ """Get guidance using the structured request/response format."""
624
+ # Convert GuidanceRequest to GuidanceContext for fetch_guidance
625
+ # Generate IDs if not available
626
+ context = GuidanceContext(
627
+ thought_id=f"guidance_{uuid.uuid4().hex[:8]}",
628
+ task_id=f"task_{uuid.uuid4().hex[:8]}",
629
+ question=request.context, # GuidanceRequest.context is the question
630
+ ethical_considerations=request.options if request.options else [],
631
+ domain_context={"urgency": request.urgency} if request.urgency else {},
632
+ )
633
+
634
+ guidance = await self.fetch_guidance(context)
635
+
636
+ # Increment commands handled counter for guidance requests
637
+ self._commands_handled += 1
638
+
639
+ return GuidanceResponse(
640
+ selected_option=guidance if guidance in request.options else None,
641
+ custom_guidance=guidance if guidance not in request.options else None,
642
+ reasoning="Guidance provided by Discord WA channel",
643
+ wa_id="discord_wa",
644
+ signature=f"discord_{uuid.uuid4().hex[:8]}",
645
+ )
646
+
647
+ async def get_pending_deferrals(self, wa_id: Optional[str] = None) -> List[PendingDeferral]:
648
+ """Get pending deferrals from the deferral channel."""
649
+ if not self.bus_manager or not self.bus_manager.memory:
650
+ logger.warning("No memory bus available for deferral tracking")
651
+ return []
652
+
653
+ try:
654
+ # Query memory for pending deferrals
655
+ query = {"node_type": "DISCORD_DEFERRAL", "status": "pending"}
656
+
657
+ # Add WA filter if specified
658
+ if wa_id:
659
+ query["created_by"] = wa_id
660
+
661
+ # Search memory
662
+ nodes = await self.bus_manager.memory.search(query)
663
+
664
+ # Convert to PendingDeferral objects
665
+ pending = []
666
+ for node in nodes:
667
+ if isinstance(node.attributes, dict):
668
+ attrs = node.attributes
669
+ else:
670
+ attrs = node.attributes.model_dump() if hasattr(node.attributes, "model_dump") else {}
671
+
672
+ pending.append(
673
+ PendingDeferral(
674
+ deferral_id=attrs.get("deferral_id", node.id),
675
+ task_id=attrs.get("task_id", ""),
676
+ thought_id=attrs.get("thought_id", ""),
677
+ reason=attrs.get("reason", ""),
678
+ created_at=attrs.get(
679
+ "created_at", self._time_service.now() if self._time_service else datetime.now()
680
+ ),
681
+ deferred_by=attrs.get("created_by", "discord_agent"),
682
+ channel_id=attrs.get("channel_id"),
683
+ priority=attrs.get("priority", "normal"),
684
+ )
685
+ )
686
+
687
+ return pending
688
+
689
+ except Exception as e:
690
+ logger.error(f"Failed to get pending deferrals: {e}")
691
+ return []
692
+
693
+ async def resolve_deferral(self, deferral_id: str, response: DeferralResponse) -> bool:
694
+ """Resolve a deferred decision."""
695
+ deferral_channel_id = self.discord_config.deferral_channel_id
696
+ if not deferral_channel_id:
697
+ return False
698
+
699
+ try:
700
+ # Send resolution message
701
+ message = "**DEFERRAL RESOLVED**\n"
702
+ message += f"ID: {deferral_id}\n"
703
+ message += f"Approved: {'Yes' if response.approved else 'No'}\n"
704
+ if response.reason:
705
+ message += f"Reason: {response.reason}\n"
706
+ if response.modified_time:
707
+ message += f"Modified Time: {response.modified_time.isoformat()}\n"
708
+ message += f"WA ID: {response.wa_id}\n"
709
+
710
+ return await self.send_message(deferral_channel_id, message)
711
+ except Exception as e:
712
+ logger.error(f"Failed to resolve deferral: {e}")
713
+ return False
714
+
715
+ async def grant_permission(self, wa_id: str, permission: str, resource: Optional[str] = None) -> bool:
716
+ """Grant AUTHORITY or OBSERVER role to a Discord user."""
717
+ if permission.upper() not in ["AUTHORITY", "OBSERVER"]:
718
+ logger.error(f"Invalid permission: {permission}. Must be AUTHORITY or OBSERVER.")
719
+ return False
720
+
721
+ try:
722
+ if not self._channel_manager.client:
723
+ return False
724
+
725
+ # Find user in guilds and grant role
726
+ for guild in self._channel_manager.client.guilds:
727
+ member = guild.get_member(int(wa_id))
728
+ if member:
729
+ # Find or create role
730
+ role = discord.utils.get(guild.roles, name=permission.upper())
731
+ if not role:
732
+ # Create role if it doesn't exist
733
+ role = await guild.create_role(name=permission.upper())
734
+
735
+ # Grant role
736
+ await member.add_roles(role)
737
+ logger.info(f"Granted {permission} to user {wa_id} in guild {guild.name}")
738
+
739
+ # Note: Permission changes are already audited via grant/revoke handler actions
740
+
741
+ return True
742
+
743
+ logger.error(f"User {wa_id} not found in any guild")
744
+ return False
745
+ except Exception as e:
746
+ logger.exception(f"Failed to grant permission: {e}")
747
+ return False
748
+
749
+ async def revoke_permission(self, wa_id: str, permission: str, resource: Optional[str] = None) -> bool:
750
+ """Revoke AUTHORITY or OBSERVER role from a Discord user."""
751
+ if permission.upper() not in ["AUTHORITY", "OBSERVER"]:
752
+ logger.error(f"Invalid permission: {permission}. Must be AUTHORITY or OBSERVER.")
753
+ return False
754
+
755
+ try:
756
+ if not self._channel_manager.client:
757
+ return False
758
+
759
+ # Find user in guilds and remove role
760
+ for guild in self._channel_manager.client.guilds:
761
+ member = guild.get_member(int(wa_id))
762
+ if member:
763
+ role = discord.utils.get(guild.roles, name=permission.upper())
764
+ if role and role in member.roles:
765
+ await member.remove_roles(role)
766
+ logger.info(f"Revoked {permission} from user {wa_id} in guild {guild.name}")
767
+
768
+ # Note: Permission changes are already audited via grant/revoke handler actions
769
+
770
+ return True
771
+
772
+ return False
773
+ except Exception as e:
774
+ logger.exception(f"Failed to revoke permission: {e}")
775
+ return False
776
+
777
+ def get_active_channels(self) -> List[DiscordChannelInfo]:
778
+ """Get list of active Discord channels."""
779
+ channels: List[DiscordChannelInfo] = []
780
+
781
+ logger.info(
782
+ f"[DISCORD] get_active_channels called, client ready: {self._channel_manager.client.is_ready() if self._channel_manager.client else False}"
783
+ )
784
+
785
+ if not self._channel_manager.client or not self._channel_manager.client.is_ready():
786
+ logger.warning("[DISCORD] Client not ready, returning empty channels")
787
+ return channels
788
+
789
+ try:
790
+ # Get all monitored channels
791
+ logger.info(f"[DISCORD] Checking {len(self.discord_config.monitored_channel_ids)} monitored channels")
792
+ for channel_id in self.discord_config.monitored_channel_ids:
793
+ channel = self._channel_manager.client.get_channel(int(channel_id))
794
+ logger.info(f"[DISCORD] Channel {channel_id}: {'found' if channel else 'not found'}")
795
+ if channel:
796
+ channels.append(
797
+ DiscordChannelInfo(
798
+ channel_id=f"discord_{channel_id}",
799
+ channel_type="discord",
800
+ display_name=(
801
+ f"#{channel.name}" if hasattr(channel, "name") else f"Discord Channel {channel_id}"
802
+ ),
803
+ is_active=True,
804
+ created_at=(
805
+ channel.created_at.isoformat()
806
+ if hasattr(channel, "created_at") and channel.created_at
807
+ else None
808
+ ),
809
+ last_activity=None, # Could track this if needed
810
+ message_count=0, # Could track this if needed
811
+ )
812
+ )
813
+
814
+ # Add deferral channel if configured
815
+ if self.discord_config.deferral_channel_id:
816
+ channel = self._channel_manager.client.get_channel(int(self.discord_config.deferral_channel_id))
817
+ if channel and f"discord_{self.discord_config.deferral_channel_id}" not in [
818
+ ch.channel_id for ch in channels
819
+ ]:
820
+ channels.append(
821
+ DiscordChannelInfo(
822
+ channel_id=f"discord_{self.discord_config.deferral_channel_id}",
823
+ channel_type="discord",
824
+ display_name=(
825
+ f"#{channel.name} (Deferrals)"
826
+ if hasattr(channel, "name")
827
+ else "Discord Deferral Channel"
828
+ ),
829
+ is_active=True,
830
+ created_at=(
831
+ channel.created_at.isoformat()
832
+ if hasattr(channel, "created_at") and channel.created_at
833
+ else None
834
+ ),
835
+ last_activity=None,
836
+ message_count=0,
837
+ )
838
+ )
839
+
840
+ except Exception as e:
841
+ logger.error(f"Error getting active channels: {e}", exc_info=True)
842
+
843
+ logger.info(f"[DISCORD] Returning {len(channels)} channels")
844
+ return channels
845
+
846
+ async def execute_tool(
847
+ self,
848
+ tool_name: str,
849
+ tool_args: Optional[Dict[str, Union[str, int, float, bool, List[Any], JSONDict]]] = None,
850
+ *,
851
+ parameters: Optional[Dict[str, Union[str, int, float, bool, List[Any], JSONDict]]] = None,
852
+ ) -> ToolExecutionResult:
853
+ """Execute a tool through the tool handler."""
854
+
855
+ # Support both tool_args and parameters for compatibility
856
+ from typing import cast
857
+
858
+ args = tool_args or parameters or {}
859
+ result = await self._tool_handler.execute_tool(tool_name, cast(JSONDict, args))
860
+
861
+ # Increment commands handled counter
862
+ self._commands_handled += 1
863
+
864
+ # Emit telemetry for tool execution
865
+ await self._emit_telemetry(
866
+ "discord.tool.executed",
867
+ 1.0,
868
+ {
869
+ "adapter_type": "discord",
870
+ "tool_name": tool_name,
871
+ "success": str(result.success),
872
+ "status": result.status.value if hasattr(result.status, "value") else str(result.status),
873
+ },
874
+ )
875
+
876
+ return result
877
+
878
+ async def list_tools(self) -> List[str]:
879
+ """List available tools through the tool handler."""
880
+ return self._tool_handler.get_available_tools()
881
+
882
+ async def list_permissions(self, wa_id: str) -> List[WAPermission]:
883
+ """List all permissions for a Discord user."""
884
+ permissions: List[WAPermission] = []
885
+
886
+ try:
887
+ if not self._channel_manager.client:
888
+ return permissions
889
+
890
+ # Check all guilds
891
+ for guild in self._channel_manager.client.guilds:
892
+ member = guild.get_member(int(wa_id))
893
+ if member:
894
+ for role in member.roles:
895
+ if role.name.upper() in ["AUTHORITY", "OBSERVER"]:
896
+ permissions.append(
897
+ WAPermission(
898
+ permission_id=f"discord_{guild.id}_{role.name.upper()}_{wa_id}",
899
+ wa_id=wa_id,
900
+ permission_type="role",
901
+ permission_name=role.name.upper(),
902
+ resource=f"guild:{guild.id}",
903
+ granted_at=self._time_service.now() if self._time_service else datetime.now(),
904
+ granted_by="discord_adapter",
905
+ )
906
+ )
907
+
908
+ return permissions
909
+ except Exception as e:
910
+ logger.error(f"Failed to list permissions: {e}")
911
+ return []
912
+
913
+ async def send_deferral(self, deferral: DeferralRequest) -> str:
914
+ """Send a decision deferral to human WAs - returns deferral ID."""
915
+ deferral_channel_id = self.discord_config.deferral_channel_id
916
+ if not deferral_channel_id:
917
+ logger.error("DiscordAdapter: Deferral channel not configured.")
918
+ logger.error(f" - Current config: {self.discord_config}")
919
+ logger.error(f" - Monitored channels: {self.discord_config.monitored_channel_ids}")
920
+ logger.error(f" - Admin user IDs: {self.discord_config.admin_user_ids}")
921
+ raise RuntimeError("Deferral channel not configured.")
922
+
923
+ logger.info(f"Sending deferral to channel {deferral_channel_id}")
924
+ logger.info(f" - Task ID: {deferral.task_id}")
925
+ logger.info(f" - Thought ID: {deferral.thought_id}")
926
+ logger.info(f" - Reason: {deferral.reason}")
927
+
928
+ time_service = self._time_service
929
+ if time_service is None:
930
+ logger.error("Time service not initialized")
931
+ raise RuntimeError("Time service not initialized")
932
+ start_time = time_service.now()
933
+
934
+ try:
935
+ correlation_id = str(uuid.uuid4())
936
+
937
+ # Create deferral data for embed formatter
938
+ deferral_data = DiscordGuidanceData(
939
+ deferral_id=correlation_id,
940
+ task_id=deferral.task_id,
941
+ thought_id=deferral.thought_id,
942
+ reason=deferral.reason,
943
+ defer_until=deferral.defer_until,
944
+ context=deferral.context or {},
945
+ )
946
+
947
+ # Create rich embed
948
+ embed = self._embed_formatter.format_deferral_request(deferral_data)
949
+
950
+ # Send the embed with a plain text notification
951
+ message_text = (
952
+ f"**DEFERRAL REQUEST (ID: {correlation_id})**\n"
953
+ f"Task ID: {deferral.task_id}\n"
954
+ f"Thought ID: {deferral.thought_id}\n"
955
+ f"Reason: {deferral.reason}"
956
+ )
957
+ if deferral.defer_until:
958
+ message_text += f"\nDefer Until: {deferral.defer_until}"
959
+ if deferral.context:
960
+ context_str = ", ".join(f"{k}: {v}" for k, v in deferral.context.items())
961
+ message_text += f"\nContext: {context_str}"
962
+
963
+ # Get the Discord client from channel manager
964
+ client = self._channel_manager.client
965
+ if not client:
966
+ raise RuntimeError("Discord client not available")
967
+
968
+ # Get the channel
969
+ channel = client.get_channel(int(deferral_channel_id))
970
+ if not channel:
971
+ raise RuntimeError(f"Deferral channel {deferral_channel_id} not found")
972
+
973
+ # Check if channel supports sending messages
974
+ if not isinstance(
975
+ channel,
976
+ (discord.TextChannel, discord.DMChannel, discord.Thread, discord.VoiceChannel, discord.StageChannel),
977
+ ):
978
+ raise RuntimeError(f"Channel {deferral_channel_id} does not support sending messages")
979
+
980
+ # Split the message if needed using the message handler's method
981
+ chunks = self._message_handler._split_message(message_text, max_length=1900)
982
+
983
+ # Send the first chunk with the embed
984
+ if chunks:
985
+ sent_message = await channel.send(content=chunks[0], embed=embed)
986
+
987
+ # Send additional chunks if any (without embed)
988
+ for i in range(1, len(chunks)):
989
+ continuation = f"*(Continued from deferral {correlation_id})*\n\n{chunks[i]}"
990
+ await channel.send(content=continuation)
991
+ await asyncio.sleep(0.5) # Small delay between messages
992
+ else:
993
+ # Fallback if no chunks
994
+ sent_message = await channel.send(content="**DEFERRAL REQUEST** (content too long)", embed=embed)
995
+
996
+ # Add reaction UI for WAs to respond
997
+ await sent_message.add_reaction("✅") # Approve
998
+ await sent_message.add_reaction("❌") # Deny
999
+ await sent_message.add_reaction("🔄") # Request more info
1000
+
1001
+ # Store message ID for tracking responses
1002
+ if hasattr(self._reaction_handler, "track_deferral"):
1003
+ await self._reaction_handler.track_deferral(
1004
+ message_id=str(sent_message.id),
1005
+ deferral_id=correlation_id,
1006
+ task_id=deferral.task_id,
1007
+ thought_id=deferral.thought_id,
1008
+ )
1009
+
1010
+ # Store deferral in memory graph
1011
+ if self.bus_manager and self.bus_manager.memory:
1012
+ try:
1013
+ deferral_node = DiscordDeferralNode(
1014
+ id=f"discord_deferral/{correlation_id}",
1015
+ scope=GraphScope.LOCAL,
1016
+ attributes=GraphNodeAttributes(created_by="discord_adapter", tags=["discord", "deferral"]),
1017
+ deferral_id=correlation_id,
1018
+ task_id=deferral.task_id,
1019
+ thought_id=deferral.thought_id,
1020
+ reason=deferral.reason,
1021
+ defer_until=deferral.defer_until,
1022
+ channel_id=deferral_channel_id,
1023
+ status="pending",
1024
+ context=deferral.context,
1025
+ updated_at=start_time,
1026
+ updated_by="discord_adapter",
1027
+ )
1028
+
1029
+ await self.bus_manager.memory.store(
1030
+ node_id=correlation_id,
1031
+ node_type="DISCORD_DEFERRAL",
1032
+ attributes=deferral_node.to_graph_node().attributes,
1033
+ scope="local",
1034
+ handler_name="discord_adapter",
1035
+ )
1036
+ except Exception as e:
1037
+ logger.error(f"Failed to store deferral in memory: {e}")
1038
+
1039
+ end_time = time_service.now()
1040
+ execution_time_ms = (end_time - start_time).total_seconds() * 1000
1041
+
1042
+ persistence.add_correlation(
1043
+ ServiceCorrelation(
1044
+ correlation_id=correlation_id,
1045
+ service_type="discord",
1046
+ handler_name="DiscordAdapter",
1047
+ action_type="send_deferral",
1048
+ request_data=ServiceRequestData(
1049
+ service_type="discord",
1050
+ method_name="send_deferral",
1051
+ channel_id=deferral_channel_id,
1052
+ parameters={
1053
+ "reason": deferral.reason,
1054
+ "task_id": deferral.task_id,
1055
+ "thought_id": deferral.thought_id,
1056
+ },
1057
+ request_timestamp=start_time,
1058
+ ),
1059
+ response_data=ServiceResponseData(
1060
+ success=True,
1061
+ result_summary=f"Deferral sent to channel {deferral_channel_id}",
1062
+ execution_time_ms=execution_time_ms,
1063
+ response_timestamp=end_time,
1064
+ ),
1065
+ status=ServiceCorrelationStatus.COMPLETED,
1066
+ created_at=start_time,
1067
+ updated_at=end_time,
1068
+ timestamp=start_time,
1069
+ ),
1070
+ time_service,
1071
+ )
1072
+
1073
+ # Increment commands handled counter for deferral requests
1074
+ self._commands_handled += 1
1075
+
1076
+ return correlation_id
1077
+ except Exception as e:
1078
+ self._errors_total += 1
1079
+ logger.exception(f"Failed to send deferral to Discord: {e}")
1080
+ raise
1081
+
1082
+ # Legacy method for backward compatibility
1083
+ async def send_deferral_legacy(self, context: DeferralContext) -> bool:
1084
+ """Send a deferral report to the configured deferral channel (legacy)."""
1085
+ try:
1086
+ # Convert DeferralContext to DeferralRequest
1087
+ request = DeferralRequest(
1088
+ task_id=context.task_id,
1089
+ thought_id=context.thought_id,
1090
+ reason=context.reason,
1091
+ defer_until=context.defer_until
1092
+ or (self._time_service.now() if self._time_service else datetime.now()) + timedelta(hours=1),
1093
+ context=context.metadata,
1094
+ )
1095
+ await self.send_deferral(request)
1096
+ return True
1097
+ except Exception:
1098
+ return False
1099
+
1100
+ def get_capabilities(self) -> ServiceCapabilities:
1101
+ """Return service capabilities in the proper format."""
1102
+ return ServiceCapabilities(
1103
+ service_name="DiscordAdapter",
1104
+ actions=[
1105
+ # Communication capabilities
1106
+ "send_message",
1107
+ "fetch_messages",
1108
+ # Tool capabilities
1109
+ "execute_tool",
1110
+ "list_tools",
1111
+ # WiseAuthority capabilities
1112
+ "fetch_guidance",
1113
+ "send_deferral",
1114
+ "check_authorization",
1115
+ "request_approval",
1116
+ "get_guidance",
1117
+ "get_pending_deferrals",
1118
+ "resolve_deferral",
1119
+ "grant_permission",
1120
+ "revoke_permission",
1121
+ "list_permissions",
1122
+ ],
1123
+ version="1.0.0",
1124
+ dependencies=["discord.py"],
1125
+ )
1126
+
1127
+ def get_status(self) -> ServiceStatus:
1128
+ """Return current service status."""
1129
+ try:
1130
+ # Check if client is ready without blocking
1131
+ is_healthy = self._channel_manager.client is not None and not self._channel_manager.client.is_closed()
1132
+ except Exception as e:
1133
+ logger.warning(
1134
+ f"Discord health check failed: {type(e).__name__}: {str(e)} - Client state unknown, latency check failed"
1135
+ )
1136
+ is_healthy = False
1137
+
1138
+ # Get actual latency from Discord client
1139
+ latency_ms = 0.0
1140
+ if self._message_handler and self._message_handler.client:
1141
+ # Discord.py provides latency in seconds, convert to milliseconds
1142
+ latency_seconds = self._message_handler.client.latency
1143
+ if latency_seconds is not None and latency_seconds >= 0:
1144
+ latency_ms = latency_seconds * 1000.0
1145
+
1146
+ return ServiceStatus(
1147
+ service_name="DiscordAdapter",
1148
+ service_type="adapter",
1149
+ is_healthy=is_healthy,
1150
+ uptime_seconds=(
1151
+ float((self._time_service.now() - self._start_time).total_seconds())
1152
+ if self._start_time and self._time_service
1153
+ else 0.0
1154
+ ),
1155
+ metrics={"latency": latency_ms},
1156
+ )
1157
+
1158
+ def _get_time_service(self) -> TimeServiceProtocol:
1159
+ """Get time service instance."""
1160
+ # Discord adapter already has _time_service set in __init__
1161
+ if self._time_service is None:
1162
+ raise RuntimeError("TimeService not available")
1163
+ return self._time_service
1164
+
1165
+ def _collect_metrics(self) -> Dict[str, float]:
1166
+ """Collect base metrics for the Discord adapter."""
1167
+ uptime = 0.0
1168
+ if self._start_time:
1169
+ uptime = (self._get_time_service().now() - self._start_time).total_seconds()
1170
+
1171
+ is_running = self._channel_manager and self._channel_manager.client and self._channel_manager.client.is_ready()
1172
+
1173
+ return {
1174
+ "healthy": True if is_running else False,
1175
+ "uptime_seconds": uptime,
1176
+ "request_count": float(self._messages_processed),
1177
+ "error_count": float(self._errors_total),
1178
+ "error_rate": float(self._errors_total) / max(1, self._messages_processed),
1179
+ }
1180
+
1181
+ def get_metrics(self) -> Dict[str, float]:
1182
+ """Get all metrics including base, custom, and v1.4.3 specific."""
1183
+ # Get all base + custom metrics
1184
+ metrics = self._collect_metrics()
1185
+
1186
+ # Add v1.4.3 specific metrics
1187
+ # Get active guild count from client if available
1188
+ guilds_active = 0.0
1189
+ if self._channel_manager and self._channel_manager.client and self._channel_manager.client.is_ready():
1190
+ try:
1191
+ guilds_active = float(len(self._channel_manager.client.guilds))
1192
+ except Exception:
1193
+ guilds_active = 0.0
1194
+
1195
+ metrics.update(
1196
+ {
1197
+ "discord_messages_processed": float(self._messages_processed),
1198
+ "discord_commands_handled": float(self._commands_handled),
1199
+ "discord_errors_total": float(self._errors_total),
1200
+ "discord_guilds_active": guilds_active,
1201
+ }
1202
+ )
1203
+
1204
+ return metrics
1205
+
1206
+ async def _send_output(self, channel_id: str, content: str) -> None:
1207
+ """Send output to a Discord channel with retry logic"""
1208
+ await self._retry_discord_operation(
1209
+ self._message_handler.send_message_to_channel,
1210
+ channel_id,
1211
+ content,
1212
+ operation_name="send_output",
1213
+ config_key="discord_api",
1214
+ )
1215
+ # result contains the return value from send_message_to_channel
1216
+
1217
+ async def _on_message(self, message: discord.Message) -> None:
1218
+ """Handle incoming Discord messages."""
1219
+ await self._channel_manager.on_message(message)
1220
+
1221
+ # Increment message processed counter
1222
+ self._messages_processed += 1
1223
+
1224
+ # Emit telemetry for message received
1225
+ await self._emit_telemetry(
1226
+ "discord.message.received",
1227
+ 1.0,
1228
+ {"adapter_type": "discord", "channel_id": str(message.channel.id), "author_id": str(message.author.id)},
1229
+ )
1230
+
1231
+ # Audit log the message received
1232
+ await self._audit_logger.log_message_received(
1233
+ channel_id=str(message.channel.id),
1234
+ author_id=str(message.author.id),
1235
+ author_name=message.author.name,
1236
+ message_id=str(message.id),
1237
+ )
1238
+
1239
+ def attach_to_client(self, client: discord.Client) -> None:
1240
+ """Attach message handlers to a Discord client."""
1241
+ logger.info("DiscordAdapter.attach_to_client: Attaching to Discord client")
1242
+ self._channel_manager.set_client(client)
1243
+ self._message_handler.set_client(client)
1244
+ self._guidance_handler.set_client(client)
1245
+ self._reaction_handler.set_client(client)
1246
+ self._tool_handler.set_client(client)
1247
+
1248
+ # Note: Event handlers are now managed by CIRISDiscordClient
1249
+ self._connection_manager.set_client(client)
1250
+ logger.info("DiscordAdapter.attach_to_client: All handlers attached")
1251
+
1252
+ async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent) -> None:
1253
+ """Handle raw reaction add events.
1254
+
1255
+ Args:
1256
+ payload: Discord reaction event payload
1257
+ """
1258
+ # Don't process reactions from bots
1259
+ if payload.member and payload.member.bot:
1260
+ return
1261
+
1262
+ await self._reaction_handler.handle_reaction(payload)
1263
+
1264
+ async def start(self) -> None:
1265
+ """
1266
+ Start the Discord adapter.
1267
+ Note: This doesn't start the Discord client connection - that's handled by the runtime.
1268
+ """
1269
+ try:
1270
+ # Capture start time
1271
+ self._start_time = self._time_service.now() if self._time_service else datetime.now(timezone.utc)
1272
+
1273
+ # Emit telemetry for adapter start
1274
+ await self._emit_telemetry("discord.adapter.starting", 1.0, {"adapter_type": "discord"})
1275
+
1276
+ await super().start()
1277
+
1278
+ # Set up audit service if available
1279
+ if self.bus_manager and hasattr(self.bus_manager, "audit"):
1280
+ # Try to get audit service from bus manager
1281
+ try:
1282
+ audit_service = self.bus_manager.audit
1283
+ if audit_service:
1284
+ self._audit_logger.set_audit_service(audit_service)
1285
+ logger.info("Discord adapter connected to audit service")
1286
+ except Exception as e:
1287
+ logger.debug(f"Could not connect to audit service: {e}")
1288
+
1289
+ client = self._channel_manager.client
1290
+ if client:
1291
+ logger.info("Discord adapter started with existing client (not yet connected)")
1292
+ else:
1293
+ logger.warning("Discord adapter started without client - attach_to_client() must be called separately")
1294
+
1295
+ logger.info("Discord adapter started successfully")
1296
+
1297
+ # Emit telemetry for successful start
1298
+ await self._emit_telemetry(
1299
+ "discord.adapter.started", 1.0, {"adapter_type": "discord", "has_client": str(client is not None)}
1300
+ )
1301
+
1302
+ # Set up connection monitoring
1303
+ if client:
1304
+ logger.info("Discord adapter setting up connection monitoring")
1305
+ await self._connection_manager.connect()
1306
+ logger.info("Discord adapter will wait for platform to establish connection")
1307
+ else:
1308
+ logger.warning("No Discord client attached - connection will be established later")
1309
+
1310
+ except Exception as e:
1311
+ logger.exception(f"Failed to start Discord adapter: {e}")
1312
+ raise
1313
+
1314
+ async def wait_until_ready(self, timeout: float = 30.0) -> bool:
1315
+ """
1316
+ Wait until the Discord client is ready or timeout.
1317
+
1318
+ Args:
1319
+ timeout: Maximum time to wait in seconds
1320
+
1321
+ Returns:
1322
+ True if ready, False if timeout
1323
+ """
1324
+ logger.info(f"Waiting for Discord adapter to be ready (timeout: {timeout}s)...")
1325
+ return await self._connection_manager.wait_until_ready(timeout)
1326
+
1327
+ async def stop(self) -> None:
1328
+ """
1329
+ Stop the Discord adapter and clean up resources.
1330
+ """
1331
+ try:
1332
+ logger.info("Stopping Discord adapter...")
1333
+
1334
+ # Emit telemetry for adapter stopping
1335
+ await self._emit_telemetry("discord.adapter.stopping", 1.0, {"adapter_type": "discord"})
1336
+
1337
+ self._tool_handler.clear_tool_results()
1338
+
1339
+ # Disconnect gracefully
1340
+ await self._connection_manager.disconnect()
1341
+
1342
+ await super().stop()
1343
+
1344
+ logger.info("Discord adapter stopped successfully")
1345
+
1346
+ # Emit telemetry for successful stop
1347
+ await self._emit_telemetry("discord.adapter.stopped", 1.0, {"adapter_type": "discord"})
1348
+ except AttributeError as e:
1349
+ # Handle the '_MissingSentinel' error that occurs during shutdown
1350
+ if "'_MissingSentinel' object has no attribute 'create_task'" in str(e):
1351
+ logger.debug("Discord client already shut down, ignoring event loop error")
1352
+ else:
1353
+ logger.error(f"AttributeError stopping Discord adapter: {e}")
1354
+ except Exception as e:
1355
+ logger.error(f"Error stopping Discord adapter: {e}")
1356
+
1357
+ async def is_healthy(self) -> bool:
1358
+ """Check if the Discord adapter is healthy"""
1359
+ try:
1360
+ result = self._connection_manager.is_connected()
1361
+ logger.debug(f"DiscordAdapter.is_healthy: connection_manager.is_connected() returned {result}")
1362
+ return result
1363
+ except Exception as e:
1364
+ logger.warning(f"DiscordAdapter.is_healthy: Exception checking health: {e}")
1365
+ return False
1366
+
1367
+ def get_service_type(self) -> ServiceType:
1368
+ """Get the type of this service."""
1369
+ return ServiceType.ADAPTER
1370
+
1371
+ def get_home_channel_id(self) -> Optional[str]:
1372
+ """Get the home channel ID for this Discord adapter.
1373
+
1374
+ Returns:
1375
+ The formatted channel ID (e.g., 'discord_123456789')
1376
+ or None if no home channel is configured.
1377
+ """
1378
+ # Get the raw channel ID from config
1379
+ raw_channel_id = self.discord_config.get_home_channel_id()
1380
+ logger.debug(f"DiscordAdapter.get_home_channel_id: raw_channel_id = {raw_channel_id}")
1381
+ if not raw_channel_id:
1382
+ logger.warning("DiscordAdapter: No home channel ID found in config")
1383
+ return None
1384
+
1385
+ # Format it with discord_ prefix
1386
+ # The guild ID will be added by the platform when available
1387
+ formatted_id = self.discord_config.get_formatted_startup_channel_id()
1388
+ logger.debug(f"DiscordAdapter.get_home_channel_id: formatted_id = {formatted_id}")
1389
+ return formatted_id
1390
+
1391
+ def get_channel_list(self) -> List[ChannelContext]:
1392
+ """
1393
+ Get list of available Discord channels.
1394
+
1395
+ Returns:
1396
+ List of ChannelContext objects for Discord channels.
1397
+ """
1398
+ channels: List[ChannelContext] = []
1399
+
1400
+ # Add configured channels from config
1401
+ if self.discord_config:
1402
+ # Add monitored channels
1403
+ for channel_id in self.discord_config.monitored_channel_ids:
1404
+ # Determine allowed actions based on channel type
1405
+ allowed_actions = ["speak", "observe", "memorize", "recall"]
1406
+ if channel_id == self.discord_config.home_channel_id:
1407
+ allowed_actions.append("wa_defer") # Home channel can defer to WA
1408
+ if channel_id == self.discord_config.deferral_channel_id:
1409
+ allowed_actions.append("wa_approve") # Deferral channel for WA approvals
1410
+
1411
+ channel_name = None
1412
+ # If bot is connected, get actual channel name
1413
+ if self._channel_manager and self._channel_manager.client and self._channel_manager.client.is_ready():
1414
+ try:
1415
+ discord_channel = self._channel_manager.client.get_channel(int(channel_id))
1416
+ if discord_channel and hasattr(discord_channel, "name"):
1417
+ channel_name = f"#{discord_channel.name}"
1418
+ except Exception as e:
1419
+ logger.debug(f"Could not get Discord channel info for {channel_id}: {e}")
1420
+
1421
+ channel = ChannelContext(
1422
+ channel_id=channel_id,
1423
+ channel_type="discord",
1424
+ created_at=datetime.now(), # We don't have creation time, use now
1425
+ channel_name=channel_name,
1426
+ is_private=False, # Discord channels are generally not private
1427
+ participants=[], # Could be populated from guild members if needed
1428
+ is_active=True,
1429
+ last_activity=None, # Could be populated from correlations
1430
+ message_count=0, # Could be populated from correlations
1431
+ allowed_actions=allowed_actions,
1432
+ moderation_level="standard",
1433
+ )
1434
+ channels.append(channel)
1435
+
1436
+ # Add deferral channel if not already in monitored
1437
+ if (
1438
+ self.discord_config.deferral_channel_id
1439
+ and self.discord_config.deferral_channel_id not in self.discord_config.monitored_channel_ids
1440
+ ):
1441
+ channel_name = None
1442
+ if self._channel_manager and self._channel_manager.client and self._channel_manager.client.is_ready():
1443
+ try:
1444
+ discord_channel = self._channel_manager.client.get_channel(
1445
+ int(self.discord_config.deferral_channel_id)
1446
+ )
1447
+ if discord_channel and hasattr(discord_channel, "name"):
1448
+ channel_name = f"#{discord_channel.name}"
1449
+ except Exception as e:
1450
+ logger.debug(f"Could not get Discord channel info for deferral channel: {e}")
1451
+
1452
+ channels.append(
1453
+ ChannelContext(
1454
+ channel_id=self.discord_config.deferral_channel_id,
1455
+ channel_type="discord",
1456
+ created_at=datetime.now(),
1457
+ channel_name=channel_name,
1458
+ is_private=False,
1459
+ participants=[],
1460
+ is_active=True,
1461
+ last_activity=None,
1462
+ message_count=0,
1463
+ allowed_actions=["wa_approve", "speak", "observe"], # Deferral channel
1464
+ moderation_level="strict", # Higher moderation for deferral channel
1465
+ )
1466
+ )
1467
+
1468
+ return channels
1469
+
1470
+ def _setup_connection_callbacks(self) -> None:
1471
+ """Set up callbacks for connection events."""
1472
+
1473
+ async def on_connected() -> None:
1474
+ """Handle successful connection."""
1475
+ try:
1476
+ # Log connection event
1477
+ if self._connection_manager.client:
1478
+ guild_count = len(self._connection_manager.client.guilds)
1479
+ user_count = len(self._connection_manager.client.users)
1480
+
1481
+ await self._audit_logger.log_connection_event(
1482
+ event_type="connected", guild_count=guild_count, user_count=user_count
1483
+ )
1484
+
1485
+ await self._emit_telemetry(
1486
+ "discord.connection.established",
1487
+ 1.0,
1488
+ {"adapter_type": "discord", "guilds": str(guild_count), "users": str(user_count)},
1489
+ )
1490
+ except Exception as e:
1491
+ logger.error(f"Error in connection callback: {e}")
1492
+
1493
+ async def on_disconnected(error: Optional[Exception]) -> None:
1494
+ """Handle disconnection."""
1495
+ try:
1496
+ await self._audit_logger.log_connection_event(
1497
+ event_type="disconnected", guild_count=0, user_count=0, error=str(error) if error else None
1498
+ )
1499
+
1500
+ await self._emit_telemetry(
1501
+ "discord.connection.lost",
1502
+ 1.0,
1503
+ {"adapter_type": "discord", "error": str(error) if error else "clean_disconnect"},
1504
+ )
1505
+ except Exception as e:
1506
+ logger.error(f"Error in disconnection callback: {e}")
1507
+
1508
+ async def on_reconnecting(attempt: int) -> None:
1509
+ """Handle reconnection attempts."""
1510
+ try:
1511
+ await self._emit_telemetry(
1512
+ "discord.connection.reconnecting",
1513
+ 1.0,
1514
+ {
1515
+ "adapter_type": "discord",
1516
+ "attempt": str(attempt),
1517
+ "max_attempts": str(self._connection_manager.max_reconnect_attempts),
1518
+ },
1519
+ )
1520
+ except Exception as e:
1521
+ logger.error(f"Error in reconnecting callback: {e}")
1522
+
1523
+ async def on_failed(reason: str) -> None:
1524
+ """Handle connection failure."""
1525
+ try:
1526
+ await self._audit_logger.log_connection_event(
1527
+ event_type="failed", guild_count=0, user_count=0, error=reason
1528
+ )
1529
+
1530
+ await self._emit_telemetry(
1531
+ "discord.connection.failed", 1.0, {"adapter_type": "discord", "reason": reason}
1532
+ )
1533
+ except Exception as e:
1534
+ logger.error(f"Error in failure callback: {e}")
1535
+
1536
+ # Set callbacks
1537
+ self._connection_manager.on_connected = on_connected
1538
+ self._connection_manager.on_disconnected = on_disconnected
1539
+ self._connection_manager.on_reconnecting = on_reconnecting
1540
+ self._connection_manager.on_failed = on_failed
1541
+
1542
+ @property
1543
+ def _client(self) -> Optional[discord.Client]:
1544
+ """Get the Discord client instance."""
1545
+ return self._channel_manager.client
1546
+
1547
+ def get_services_to_register(self) -> List["AdapterServiceRegistration"]:
1548
+ """Register Discord services for communication, tools, and wise authority."""
1549
+ from ciris_engine.logic.registries.base import Priority
1550
+ from ciris_engine.schemas.adapters.registration import AdapterServiceRegistration
1551
+ from ciris_engine.schemas.runtime.enums import ServiceType
1552
+
1553
+ registrations = [
1554
+ AdapterServiceRegistration(
1555
+ service_type=ServiceType.COMMUNICATION,
1556
+ provider=self, # The Discord adapter itself is the provider
1557
+ priority=Priority.HIGH,
1558
+ handlers=["SpeakHandler", "ObserveHandler"], # Specific handlers
1559
+ capabilities=["send_message", "fetch_messages"],
1560
+ ),
1561
+ AdapterServiceRegistration(
1562
+ service_type=ServiceType.TOOL,
1563
+ provider=self, # Discord adapter handles tools too
1564
+ priority=Priority.NORMAL, # Lower priority than CLI for tools
1565
+ handlers=["ToolHandler"],
1566
+ capabilities=["execute_tool", "get_available_tools", "get_tool_result", "validate_parameters"],
1567
+ ),
1568
+ AdapterServiceRegistration(
1569
+ service_type=ServiceType.WISE_AUTHORITY,
1570
+ provider=self, # Discord adapter can handle WA
1571
+ priority=Priority.HIGH,
1572
+ handlers=["DeferralHandler", "GuidanceHandler"],
1573
+ capabilities=[
1574
+ "send_deferral",
1575
+ "check_deferral",
1576
+ "fetch_guidance",
1577
+ "request_permission",
1578
+ "check_permission",
1579
+ "list_permissions",
1580
+ ],
1581
+ ),
1582
+ ]
1583
+
1584
+ return registrations