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,1987 @@
1
+ """
2
+ Telemetry & Observability endpoints for CIRIS API v1.
3
+
4
+ Consolidated metrics, traces, logs, and insights from all system components.
5
+ """
6
+
7
+ import logging
8
+ import sys
9
+ import uuid
10
+ from collections import defaultdict
11
+ from datetime import datetime, timedelta, timezone
12
+ from typing import Any, Dict, List, Optional
13
+
14
+ from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response
15
+ from pydantic import BaseModel, Field, field_serializer
16
+
17
+ from ciris_engine.schemas.api.auth import AuthContext
18
+ from ciris_engine.schemas.api.responses import ResponseMetadata, SuccessResponse
19
+ from ciris_engine.schemas.api.telemetry import (
20
+ APIResponseThoughtStep,
21
+ LogContext,
22
+ MetricTags,
23
+ QueryResult,
24
+ ServiceMetricValue,
25
+ TelemetryQueryFilters,
26
+ )
27
+ from ciris_engine.schemas.types import JSONDict
28
+
29
+ from ..constants import (
30
+ DESC_CURRENT_COGNITIVE_STATE,
31
+ DESC_END_TIME,
32
+ DESC_START_TIME,
33
+ ERROR_TELEMETRY_SERVICE_NOT_AVAILABLE,
34
+ )
35
+ from ..dependencies.auth import require_admin, require_observer
36
+
37
+ # Error message constants to avoid duplication
38
+ ERROR_TELEMETRY_NOT_INITIALIZED = "Critical system failure: Telemetry service not initialized"
39
+ ERROR_AUDIT_NOT_INITIALIZED = "Critical system failure: Audit service not initialized"
40
+
41
+ # Import extracted modules
42
+ from .telemetry_converters import convert_to_graphite, convert_to_prometheus
43
+ from .telemetry_helpers import get_telemetry_from_service
44
+ from .telemetry_models import (
45
+ LogEntry,
46
+ MetricData,
47
+ ResourceDataPoint,
48
+ ResourceHistoryResponse,
49
+ ResourceMetricData,
50
+ ResourceMetricStats,
51
+ ResourceUsage,
52
+ ServiceHealth,
53
+ SystemOverview,
54
+ TimePeriod,
55
+ )
56
+ from .telemetry_otlp import convert_logs_to_otlp_json, convert_to_otlp_json, convert_traces_to_otlp_json
57
+ from .telemetry_resource_helpers import (
58
+ MetricValueExtractor,
59
+ ResourceDataPointBuilder,
60
+ ResourceMetricBuilder,
61
+ ResourceMetricsCollector,
62
+ )
63
+
64
+ logger = logging.getLogger(__name__)
65
+
66
+ router = APIRouter(prefix="/telemetry", tags=["telemetry"])
67
+
68
+ # Additional request/response schemas not in telemetry_models.py
69
+
70
+
71
+ class DetailedMetric(BaseModel):
72
+ """Detailed metric information."""
73
+
74
+ name: str = Field(..., description="Metric name")
75
+ current_value: float = Field(..., description="Current value")
76
+ unit: Optional[str] = Field(None, description="Metric unit")
77
+ trend: str = Field("stable", description="Trend: up|down|stable")
78
+ hourly_average: float = Field(0.0, description="Average over last hour")
79
+ daily_average: float = Field(0.0, description="Average over last day")
80
+ by_service: List[ServiceMetricValue] = Field(default_factory=list, description="Values by service")
81
+ recent_data: List[MetricData] = Field(default_factory=list, description="Recent data points")
82
+
83
+
84
+ class MetricAggregate(BaseModel):
85
+ """Aggregated metric statistics."""
86
+
87
+ min: float = Field(0.0, description="Minimum value")
88
+ max: float = Field(0.0, description="Maximum value")
89
+ avg: float = Field(0.0, description="Average value")
90
+ sum: float = Field(0.0, description="Sum of values")
91
+ count: int = Field(0, description="Number of data points")
92
+ p50: Optional[float] = Field(None, description="50th percentile")
93
+ p95: Optional[float] = Field(None, description="95th percentile")
94
+ p99: Optional[float] = Field(None, description="99th percentile")
95
+
96
+
97
+ class MetricsResponse(BaseModel):
98
+ """Detailed metrics response."""
99
+
100
+ metrics: List[DetailedMetric] = Field(..., description="Detailed metrics")
101
+ summary: MetricAggregate = Field(..., description="Summary statistics")
102
+ period: str = Field(..., description="Time period")
103
+ timestamp: datetime = Field(..., description="Response timestamp")
104
+
105
+ @field_serializer("timestamp")
106
+ def serialize_timestamp(self, timestamp: datetime, _info: Any) -> Optional[str]:
107
+ return timestamp.isoformat() if timestamp else None
108
+
109
+
110
+ class ReasoningTraceData(BaseModel):
111
+ """Reasoning trace information."""
112
+
113
+ trace_id: str = Field(..., description="Unique trace ID")
114
+ task_id: Optional[str] = Field(None, description="Associated task ID")
115
+ task_description: Optional[str] = Field(None, description="Task description")
116
+ start_time: datetime = Field(..., description="Trace start time")
117
+ duration_ms: float = Field(..., description="Total duration")
118
+ thought_count: int = Field(0, description="Number of thoughts")
119
+ decision_count: int = Field(0, description="Number of decisions")
120
+ reasoning_depth: int = Field(0, description="Maximum reasoning depth")
121
+ thoughts: List[APIResponseThoughtStep] = Field(default_factory=list, description="Thought steps")
122
+ outcome: Optional[str] = Field(None, description="Final outcome")
123
+
124
+ @field_serializer("start_time")
125
+ def serialize_timestamp(self, timestamp: datetime, _info: Any) -> Optional[str]:
126
+ return timestamp.isoformat() if timestamp else None
127
+
128
+
129
+ class TracesResponse(BaseModel):
130
+ """Reasoning traces response."""
131
+
132
+ traces: List[ReasoningTraceData] = Field(..., description="Recent reasoning traces")
133
+ total: int = Field(..., description="Total trace count")
134
+ has_more: bool = Field(False, description="More traces available")
135
+
136
+
137
+ class LogEntryResponse(BaseModel):
138
+ """System log entry."""
139
+
140
+ timestamp: datetime = Field(..., description="Log timestamp")
141
+ level: str = Field(..., description="Log level: DEBUG|INFO|WARNING|ERROR|CRITICAL")
142
+ service: str = Field(..., description="Source service")
143
+ message: str = Field(..., description="Log message")
144
+ context: LogContext = Field(default_factory=lambda: LogContext.model_validate({}), description="Additional context")
145
+ trace_id: Optional[str] = Field(None, description="Associated trace ID")
146
+
147
+ @field_serializer("timestamp")
148
+ def serialize_timestamp(self, timestamp: datetime, _info: Any) -> Optional[str]:
149
+ return timestamp.isoformat() if timestamp else None
150
+
151
+
152
+ class LogsResponse(BaseModel):
153
+ """System logs response."""
154
+
155
+ logs: List[LogEntryResponse] = Field(..., description="Log entries")
156
+ total: int = Field(..., description="Total matching logs")
157
+ has_more: bool = Field(False, description="More logs available")
158
+
159
+
160
+ class TelemetryQuery(BaseModel):
161
+ """Custom telemetry query."""
162
+
163
+ query_type: str = Field(..., description="Query type: metrics|traces|logs|incidents|insights")
164
+ filters: TelemetryQueryFilters = Field(
165
+ default_factory=lambda: TelemetryQueryFilters.model_validate({}), description="Query filters"
166
+ )
167
+ aggregations: Optional[List[str]] = Field(None, description="Aggregations to apply")
168
+ start_time: Optional[datetime] = Field(None, description="Query start time")
169
+ end_time: Optional[datetime] = Field(None, description="Query end time")
170
+ limit: int = Field(100, ge=1, le=1000, description="Result limit")
171
+
172
+ @field_serializer("start_time", "end_time")
173
+ def serialize_times(self, dt: Optional[datetime], _info: Any) -> Optional[str]:
174
+ return dt.isoformat() if dt else None
175
+
176
+
177
+ class QueryResponse(BaseModel):
178
+ """Custom query response."""
179
+
180
+ query_type: str = Field(..., description="Query type executed")
181
+ results: List[QueryResult] = Field(..., description="Query results")
182
+ total: int = Field(..., description="Total results found")
183
+ execution_time_ms: float = Field(..., description="Query execution time")
184
+
185
+
186
+ # Helper functions
187
+
188
+
189
+ async def _update_telemetry_summary(overview: SystemOverview, telemetry_service: Any) -> None:
190
+ """Update overview with telemetry summary metrics."""
191
+ if telemetry_service and hasattr(telemetry_service, "get_telemetry_summary"):
192
+ try:
193
+ summary = await telemetry_service.get_telemetry_summary()
194
+ overview.messages_processed_24h = summary.messages_processed_24h
195
+ overview.thoughts_processed_24h = summary.thoughts_processed_24h
196
+ overview.tasks_completed_24h = summary.tasks_completed_24h
197
+ overview.errors_24h = summary.errors_24h
198
+ overview.tokens_last_hour = summary.tokens_last_hour
199
+ overview.cost_last_hour_cents = summary.cost_last_hour_cents
200
+ overview.carbon_last_hour_grams = summary.carbon_last_hour_grams
201
+ overview.energy_last_hour_kwh = summary.energy_last_hour_kwh
202
+ overview.tokens_24h = summary.tokens_24h
203
+ overview.cost_24h_cents = summary.cost_24h_cents
204
+ overview.carbon_24h_grams = summary.carbon_24h_grams
205
+ overview.energy_24h_kwh = summary.energy_24h_kwh
206
+ overview.error_rate_percent = summary.error_rate_percent
207
+ except Exception as e:
208
+ logger.warning(
209
+ f"Telemetry metric retrieval failed for telemetry summary: {type(e).__name__}: {str(e)} - Returning default/empty value"
210
+ )
211
+
212
+
213
+ async def _update_visibility_state(overview: SystemOverview, visibility_service: Any) -> None:
214
+ """Update overview with visibility state information."""
215
+ if visibility_service:
216
+ try:
217
+ snapshot = await visibility_service.get_current_state()
218
+ if snapshot:
219
+ overview.reasoning_depth = snapshot.reasoning_depth
220
+ if snapshot.current_task:
221
+ overview.current_task = snapshot.current_task.description
222
+ except Exception as e:
223
+ logger.warning(
224
+ f"Telemetry metric retrieval failed for visibility state: {type(e).__name__}: {str(e)} - Returning default/empty value"
225
+ )
226
+
227
+
228
+ def _create_empty_system_overview() -> SystemOverview:
229
+ """Create a SystemOverview with default values."""
230
+ return SystemOverview(
231
+ uptime_seconds=0.0,
232
+ cognitive_state="UNKNOWN",
233
+ messages_processed_24h=0,
234
+ thoughts_processed_24h=0,
235
+ tasks_completed_24h=0,
236
+ errors_24h=0,
237
+ tokens_last_hour=0.0,
238
+ cost_last_hour_cents=0.0,
239
+ carbon_last_hour_grams=0.0,
240
+ energy_last_hour_kwh=0.0,
241
+ tokens_24h=0.0,
242
+ cost_24h_cents=0.0,
243
+ carbon_24h_grams=0.0,
244
+ energy_24h_kwh=0.0,
245
+ memory_mb=0.0,
246
+ cpu_percent=0.0,
247
+ healthy_services=0,
248
+ degraded_services=0,
249
+ error_rate_percent=0.0,
250
+ current_task=None,
251
+ reasoning_depth=0,
252
+ active_deferrals=0,
253
+ recent_incidents=0,
254
+ total_metrics=0,
255
+ active_services=0,
256
+ )
257
+
258
+
259
+ def _validate_critical_services(telemetry_service: Any, time_service: Any) -> None:
260
+ """Validate that critical services are available."""
261
+ if not telemetry_service:
262
+ raise HTTPException(status_code=503, detail=ERROR_TELEMETRY_NOT_INITIALIZED)
263
+ if not time_service:
264
+ raise HTTPException(status_code=503, detail="Critical system failure: Time service not initialized")
265
+
266
+
267
+ def _update_uptime(overview: SystemOverview, time_service: Any) -> None:
268
+ """Update overview with system uptime."""
269
+ if not time_service:
270
+ return
271
+
272
+ try:
273
+ uptime = time_service.get_uptime()
274
+ overview.uptime_seconds = uptime
275
+ except Exception as e:
276
+ logger.warning(
277
+ f"Telemetry metric retrieval failed for uptime: {type(e).__name__}: {str(e)} - Returning default/empty value"
278
+ )
279
+
280
+
281
+ def _update_cognitive_state(overview: SystemOverview, request: Request) -> None:
282
+ """Update overview with cognitive state from runtime."""
283
+ runtime = getattr(request.app.state, "runtime", None)
284
+ if runtime and hasattr(runtime, "state_manager"):
285
+ overview.cognitive_state = runtime.state_manager.current_state
286
+
287
+
288
+ def _update_resource_usage(overview: SystemOverview, resource_monitor: Any) -> None:
289
+ """Update overview with resource usage metrics."""
290
+ if not resource_monitor:
291
+ return
292
+
293
+ try:
294
+ # Access the snapshot directly
295
+ if hasattr(resource_monitor, "snapshot"):
296
+ overview.memory_mb = float(resource_monitor.snapshot.memory_mb)
297
+ overview.cpu_percent = float(resource_monitor.snapshot.cpu_percent)
298
+ except Exception as e:
299
+ logger.warning(
300
+ f"Telemetry metric retrieval failed for resource usage: {type(e).__name__}: {str(e)} - Returning default/empty value"
301
+ )
302
+
303
+
304
+ def _get_service_health_counts(request: Request) -> tuple[int, int]:
305
+ """Count healthy and degraded services (22 core services + API consent_manager)."""
306
+ services = [
307
+ # Graph Services (6)
308
+ "memory_service",
309
+ "config_service",
310
+ "telemetry_service",
311
+ "audit_service",
312
+ "incident_management_service",
313
+ "tsdb_consolidation_service",
314
+ # Infrastructure Services (8)
315
+ "time_service",
316
+ "shutdown_service",
317
+ "initialization_service",
318
+ "authentication_service",
319
+ "resource_monitor",
320
+ "database_maintenance_service",
321
+ "secrets_service",
322
+ "consent_manager", # API-specific consent service
323
+ # Governance Services (4)
324
+ "wise_authority_service",
325
+ "adaptive_filter_service",
326
+ "visibility_service",
327
+ "self_observation_service",
328
+ # Runtime Services (3)
329
+ "llm_service",
330
+ "runtime_control_service",
331
+ "task_scheduler",
332
+ # Tool Services (1)
333
+ "secrets_tool_service",
334
+ ]
335
+
336
+ healthy = 0
337
+ degraded = 0
338
+ for service_attr in services:
339
+ if getattr(request.app.state, service_attr, None):
340
+ healthy += 1
341
+ else:
342
+ degraded += 1
343
+
344
+ return healthy, degraded
345
+
346
+
347
+ async def _update_incident_count(overview: SystemOverview, incident_service: Any) -> None:
348
+ """Update overview with recent incident count."""
349
+ if not incident_service:
350
+ return
351
+
352
+ try:
353
+ # Get count of incidents from the last hour
354
+ overview.recent_incidents = await incident_service.get_incident_count(hours=1)
355
+ except Exception as e:
356
+ logger.warning(
357
+ f"Telemetry metric retrieval failed for incident count: {type(e).__name__}: {str(e)} - Returning default/empty value"
358
+ )
359
+
360
+
361
+ async def _update_deferral_count(overview: SystemOverview, wise_authority: Any) -> None:
362
+ """Update overview with active deferral count."""
363
+ if not wise_authority:
364
+ return
365
+
366
+ try:
367
+ deferrals = await wise_authority.get_pending_deferrals()
368
+ overview.active_deferrals = len(deferrals) if deferrals else 0
369
+ except Exception as e:
370
+ logger.warning(
371
+ f"Telemetry metric retrieval failed for deferral count: {type(e).__name__}: {str(e)} - Returning default/empty value"
372
+ )
373
+
374
+
375
+ async def _update_metrics_count(overview: SystemOverview, telemetry_service: Any, healthy_services: int) -> None:
376
+ """Update overview with telemetry metrics count."""
377
+ if not telemetry_service:
378
+ return
379
+
380
+ try:
381
+ # Count total metrics collected
382
+ if hasattr(telemetry_service, "get_metric_count"):
383
+ overview.total_metrics = await telemetry_service.get_metric_count()
384
+ elif hasattr(telemetry_service, "query_metrics"):
385
+ overview.total_metrics = await _estimate_metrics_count(telemetry_service)
386
+
387
+ # Count active services (services that have reported metrics)
388
+ overview.active_services = healthy_services # Use healthy services count as proxy
389
+
390
+ except Exception as e:
391
+ logger.warning(
392
+ f"Telemetry metric retrieval failed for metrics count: {type(e).__name__}: {str(e)} - Returning default/empty value"
393
+ )
394
+
395
+
396
+ async def _estimate_metrics_count(telemetry_service: Any) -> int:
397
+ """Estimate total metrics count from common metric queries."""
398
+ metric_names = [
399
+ "llm.tokens.total", # Total tokens used
400
+ "llm_tokens_used", # Legacy token metric
401
+ "thought_processing_completed", # Thoughts completed
402
+ "action_selected_task_complete", # Tasks completed
403
+ "handler_invoked_total", # Total handler invocations
404
+ "action_selected_memorize", # Memory operations
405
+ ]
406
+
407
+ total = 0
408
+ now = datetime.now(timezone.utc)
409
+ day_ago = now - timedelta(hours=24)
410
+
411
+ for metric in metric_names:
412
+ try:
413
+ data = await telemetry_service.query_metrics(metric_name=metric, start_time=day_ago, end_time=now)
414
+ if data:
415
+ total += len(data)
416
+ except (AttributeError, TypeError, ValueError, RuntimeError) as e:
417
+ logger.debug(f"Failed to query metric '{metric}': {type(e).__name__}: {str(e)}")
418
+
419
+ return total
420
+
421
+
422
+ async def _get_system_overview(request: Request) -> SystemOverview:
423
+ """Build comprehensive system overview from all services."""
424
+ # Get core services - THESE MUST EXIST
425
+ telemetry_service = request.app.state.telemetry_service
426
+ visibility_service = request.app.state.visibility_service
427
+ time_service = request.app.state.time_service
428
+ resource_monitor = request.app.state.resource_monitor
429
+ incident_service = request.app.state.incident_management_service
430
+ wise_authority = request.app.state.wise_authority_service
431
+
432
+ # Validate critical services
433
+ _validate_critical_services(telemetry_service, time_service)
434
+
435
+ # Initialize overview with default values
436
+ overview = _create_empty_system_overview()
437
+
438
+ # Update overview with data from various services
439
+ _update_uptime(overview, time_service)
440
+ await _update_telemetry_summary(overview, telemetry_service)
441
+ _update_cognitive_state(overview, request)
442
+ await _update_visibility_state(overview, visibility_service)
443
+ _update_resource_usage(overview, resource_monitor)
444
+
445
+ # Update service health counts
446
+ healthy, degraded = _get_service_health_counts(request)
447
+ overview.healthy_services = healthy
448
+ overview.degraded_services = degraded
449
+
450
+ # Update incident and deferral counts
451
+ await _update_incident_count(overview, incident_service)
452
+ await _update_deferral_count(overview, wise_authority)
453
+ await _update_metrics_count(overview, telemetry_service, healthy)
454
+
455
+ return overview
456
+
457
+
458
+ # Endpoints
459
+
460
+
461
+ async def _export_otlp_metrics(telemetry_service: Any) -> JSONDict:
462
+ """Export metrics in OTLP format."""
463
+ if not telemetry_service:
464
+ raise HTTPException(status_code=503, detail=ERROR_TELEMETRY_SERVICE_NOT_AVAILABLE)
465
+
466
+ # Get aggregated telemetry
467
+ aggregated = await telemetry_service.get_aggregated_telemetry()
468
+
469
+ # Convert to dict for OTLP conversion
470
+ telemetry_dict = {
471
+ "system_healthy": aggregated.system_healthy,
472
+ "services_online": aggregated.services_online,
473
+ "services_total": aggregated.services_total,
474
+ "overall_error_rate": aggregated.overall_error_rate,
475
+ "overall_uptime_seconds": aggregated.overall_uptime_seconds,
476
+ "total_errors": aggregated.total_errors,
477
+ "total_requests": aggregated.total_requests,
478
+ "services": aggregated.services,
479
+ }
480
+
481
+ # Add covenant metrics if available
482
+ if hasattr(aggregated, "covenant_metrics"):
483
+ telemetry_dict["covenant_metrics"] = aggregated.covenant_metrics
484
+
485
+ return convert_to_otlp_json(telemetry_dict)
486
+
487
+
488
+ def _extract_basic_trace_fields(correlation: Any) -> JSONDict:
489
+ """Extract basic trace fields from correlation object."""
490
+ return {
491
+ "trace_id": (correlation.trace_context.trace_id if correlation.trace_context else correlation.correlation_id),
492
+ "span_id": (correlation.trace_context.span_id if correlation.trace_context else str(uuid.uuid4())),
493
+ "parent_span_id": (correlation.trace_context.parent_span_id if correlation.trace_context else None),
494
+ "timestamp": (
495
+ correlation.timestamp.isoformat() if correlation.timestamp else datetime.now(timezone.utc).isoformat()
496
+ ),
497
+ "operation": correlation.action_type or "unknown",
498
+ "service": correlation.service_type,
499
+ "handler": correlation.handler_name,
500
+ "status": (correlation.status.value if hasattr(correlation.status, "value") else str(correlation.status)),
501
+ }
502
+
503
+
504
+ def _extract_request_data_fields(correlation: Any, trace_data: JSONDict) -> None:
505
+ """Extract task/thought linkage from request data."""
506
+ if not correlation.request_data:
507
+ return
508
+
509
+ if hasattr(correlation.request_data, "task_id") and correlation.request_data.task_id:
510
+ trace_data["task_id"] = correlation.request_data.task_id
511
+ if hasattr(correlation.request_data, "thought_id") and correlation.request_data.thought_id:
512
+ trace_data["thought_id"] = correlation.request_data.thought_id
513
+
514
+
515
+ def _extract_response_data_fields(correlation: Any, trace_data: JSONDict) -> None:
516
+ """Extract performance data from response data."""
517
+ if not correlation.response_data:
518
+ return
519
+
520
+ if hasattr(correlation.response_data, "execution_time_ms"):
521
+ trace_data["duration_ms"] = correlation.response_data.execution_time_ms
522
+ if hasattr(correlation.response_data, "success"):
523
+ trace_data["success"] = correlation.response_data.success
524
+ if hasattr(correlation.response_data, "error_message"):
525
+ trace_data["error"] = correlation.response_data.error_message
526
+
527
+
528
+ def _extract_span_attributes(correlation: Any, trace_data: JSONDict) -> None:
529
+ """Extract span attributes from trace context."""
530
+ if not correlation.trace_context:
531
+ return
532
+
533
+ trace_data["span_name"] = (
534
+ correlation.trace_context.span_name
535
+ if hasattr(correlation.trace_context, "span_name")
536
+ else correlation.action_type
537
+ )
538
+ trace_data["span_kind"] = (
539
+ correlation.trace_context.span_kind if hasattr(correlation.trace_context, "span_kind") else "internal"
540
+ )
541
+
542
+
543
+ def _build_trace_data_from_correlation(correlation: Any) -> JSONDict:
544
+ """Build trace data dictionary from correlation object."""
545
+ trace_data = _extract_basic_trace_fields(correlation)
546
+ _extract_request_data_fields(correlation, trace_data)
547
+ _extract_response_data_fields(correlation, trace_data)
548
+ _extract_span_attributes(correlation, trace_data)
549
+ return trace_data
550
+
551
+
552
+ async def _export_otlp_traces(visibility_service: Any, limit: int) -> JSONDict:
553
+ """Export traces in OTLP format."""
554
+ if not visibility_service:
555
+ raise HTTPException(status_code=503, detail="Visibility service not available")
556
+
557
+ traces = []
558
+ try:
559
+ # Get service correlations (trace spans)
560
+ correlations = await visibility_service.get_recent_traces(limit=limit)
561
+
562
+ for correlation in correlations:
563
+ trace_data = _build_trace_data_from_correlation(correlation)
564
+ traces.append(trace_data)
565
+
566
+ except Exception as e:
567
+ print(f"ERROR in get_otlp_telemetry traces: {e}", file=sys.stderr)
568
+ import traceback
569
+
570
+ traceback.print_exc()
571
+
572
+ return convert_traces_to_otlp_json(traces)
573
+
574
+
575
+ def _build_log_data_from_entry(log_entry: Any) -> JSONDict:
576
+ """Build log data dictionary from log entry."""
577
+ log_data = {
578
+ "timestamp": log_entry.timestamp.isoformat(),
579
+ "level": log_entry.level,
580
+ "message": log_entry.message,
581
+ "service": log_entry.service,
582
+ }
583
+
584
+ # Add context data if available - handle both LogEntry formats
585
+ # LogEntryResponse has nested context object, telemetry_models.LogEntry has top-level fields
586
+ if hasattr(log_entry, "context") and log_entry.context:
587
+ if hasattr(log_entry.context, "correlation_id") and log_entry.context.correlation_id:
588
+ log_data["correlation_id"] = log_entry.context.correlation_id
589
+ if hasattr(log_entry.context, "trace_id") and log_entry.context.trace_id:
590
+ log_data["trace_id"] = log_entry.context.trace_id
591
+ if hasattr(log_entry.context, "user_id") and log_entry.context.user_id:
592
+ log_data["user_id"] = log_entry.context.user_id
593
+ if hasattr(log_entry.context, "entity_id") and log_entry.context.entity_id:
594
+ log_data["entity_id"] = log_entry.context.entity_id
595
+ else:
596
+ # Handle flat LogEntry format from telemetry_models
597
+ if hasattr(log_entry, "correlation_id") and log_entry.correlation_id:
598
+ log_data["correlation_id"] = log_entry.correlation_id
599
+ if hasattr(log_entry, "user_id") and log_entry.user_id:
600
+ log_data["user_id"] = log_entry.user_id
601
+
602
+ # Add trace ID at top level if available
603
+ if hasattr(log_entry, "trace_id") and log_entry.trace_id:
604
+ log_data["trace_id"] = log_entry.trace_id
605
+
606
+ return log_data
607
+
608
+
609
+ async def _export_otlp_logs(limit: int, start_time: Optional[datetime], end_time: Optional[datetime]) -> JSONDict:
610
+ """Export logs in OTLP format."""
611
+ logs = []
612
+ try:
613
+ from .telemetry_logs_reader import log_reader
614
+
615
+ # Read actual log files including incidents
616
+ file_logs = log_reader.read_logs(
617
+ level=None, # Get all levels
618
+ service=None, # Get all services
619
+ limit=limit,
620
+ start_time=start_time if start_time else None, # Don't default to 1 hour ago
621
+ end_time=end_time if end_time else None, # Don't default to now
622
+ include_incidents=True, # Include incident logs (WARNING/ERROR/CRITICAL)
623
+ )
624
+
625
+ # Convert LogEntry objects to dict format for OTLP
626
+ for log_entry in file_logs:
627
+ log_data = _build_log_data_from_entry(log_entry)
628
+ logs.append(log_data)
629
+
630
+ except ImportError:
631
+ # Log reader module not available
632
+ logger.warning("Log reader not available for OTLP logs export")
633
+ except Exception as e:
634
+ logger.warning(f"Failed to read log files for OTLP: {e}")
635
+
636
+ return convert_logs_to_otlp_json(logs)
637
+
638
+
639
+ @router.get("/otlp/{signal}", response_model=None)
640
+ async def get_otlp_telemetry(
641
+ signal: str,
642
+ request: Request,
643
+ auth: AuthContext = Depends(require_observer),
644
+ limit: int = Query(100, ge=1, le=1000, description="Maximum items to return"),
645
+ start_time: Optional[datetime] = Query(None, description=DESC_START_TIME),
646
+ end_time: Optional[datetime] = Query(None, description=DESC_END_TIME),
647
+ ) -> JSONDict:
648
+ """
649
+ OpenTelemetry Protocol (OTLP) JSON export.
650
+
651
+ Export telemetry data in OTLP JSON format for OpenTelemetry collectors.
652
+
653
+ Supported signals:
654
+ - metrics: System and service metrics
655
+ - traces: Distributed traces with spans
656
+ - logs: Structured log records
657
+
658
+ Returns OTLP JSON formatted data compatible with OpenTelemetry v1.7.0 specification.
659
+ """
660
+
661
+ if signal not in ["metrics", "traces", "logs"]:
662
+ raise HTTPException(
663
+ status_code=400, detail=f"Invalid signal type: {signal}. Must be one of: metrics, traces, logs"
664
+ )
665
+
666
+ try:
667
+ if signal == "metrics":
668
+ return await _export_otlp_metrics(request.app.state.telemetry_service)
669
+ elif signal == "traces":
670
+ return await _export_otlp_traces(request.app.state.visibility_service, limit)
671
+ else: # signal == "logs" - already validated above
672
+ return await _export_otlp_logs(limit, start_time, end_time)
673
+
674
+ except HTTPException:
675
+ raise
676
+ except Exception as e:
677
+ logger.error(f"OTLP export failed for {signal}: {e}")
678
+ raise HTTPException(status_code=500, detail=f"Failed to export {signal} in OTLP format")
679
+
680
+
681
+ @router.get("/overview", response_model=SuccessResponse[SystemOverview])
682
+ async def get_telemetry_overview(
683
+ request: Request, auth: AuthContext = Depends(require_observer)
684
+ ) -> SuccessResponse[SystemOverview]:
685
+ """
686
+ System metrics summary.
687
+
688
+ Comprehensive overview combining telemetry, visibility, incidents, and resource usage.
689
+ """
690
+ try:
691
+ overview = await _get_system_overview(request)
692
+ return SuccessResponse(
693
+ data=overview,
694
+ metadata=ResponseMetadata(
695
+ timestamp=datetime.now(timezone.utc), request_id=str(uuid.uuid4()), duration_ms=0
696
+ ),
697
+ )
698
+ except HTTPException:
699
+ # Re-raise HTTPException as-is to preserve status code
700
+ raise
701
+ except Exception as e:
702
+ raise HTTPException(status_code=500, detail=str(e))
703
+
704
+
705
+ class ResourceUsageData(BaseModel):
706
+ """Current resource usage data."""
707
+
708
+ cpu_percent: float = Field(..., description="CPU usage percentage")
709
+ memory_mb: float = Field(..., description="Memory usage in MB")
710
+ memory_percent: float = Field(..., description="Memory usage percentage")
711
+ disk_usage_bytes: int = Field(0, description="Disk usage in bytes")
712
+ disk_usage_gb: float = Field(0.0, description="Disk usage in GB")
713
+ active_threads: int = Field(0, description="Number of active threads")
714
+ open_files: int = Field(0, description="Number of open files")
715
+ timestamp: str = Field(..., description="Timestamp of measurement")
716
+
717
+
718
+ class ResourceLimits(BaseModel):
719
+ """Resource usage limits."""
720
+
721
+ max_memory_mb: float = Field(..., description="Maximum memory in MB")
722
+ max_cpu_percent: float = Field(100.0, description="Maximum CPU percentage")
723
+ max_disk_bytes: int = Field(0, description="Maximum disk usage in bytes")
724
+
725
+
726
+ class ResourceHistoryPoint(BaseModel):
727
+ """Historical resource usage point."""
728
+
729
+ timestamp: datetime = Field(..., description="Timestamp of measurement")
730
+ cpu_percent: float = Field(..., description="CPU usage percentage")
731
+ memory_mb: float = Field(..., description="Memory usage in MB")
732
+
733
+
734
+ class ResourceHealthStatus(BaseModel):
735
+ """Resource health status."""
736
+
737
+ status: str = Field(..., description="Health status: healthy|warning|critical")
738
+ warnings: List[str] = Field(default_factory=list, description="Warning messages")
739
+
740
+
741
+ class ResourceTelemetryResponse(BaseModel):
742
+ """Complete resource telemetry response."""
743
+
744
+ current: ResourceUsageData = Field(..., description="Current resource usage")
745
+ limits: ResourceLimits = Field(..., description="Resource limits")
746
+ history: List[ResourceHistoryPoint] = Field(default_factory=list, description="Historical data")
747
+ health: ResourceHealthStatus = Field(..., description="Health status")
748
+
749
+
750
+ @router.get("/resources", response_model=SuccessResponse[ResourceTelemetryResponse])
751
+ async def get_resource_telemetry(
752
+ request: Request, auth: AuthContext = Depends(require_observer)
753
+ ) -> SuccessResponse[ResourceTelemetryResponse]:
754
+ """
755
+ Get current resource usage telemetry.
756
+
757
+ Returns CPU, memory, disk, and other resource metrics.
758
+ """
759
+ # These services MUST exist - if they don't, we have a critical failure
760
+ resource_monitor = request.app.state.resource_monitor
761
+ telemetry_service = request.app.state.telemetry_service
762
+
763
+ if not resource_monitor:
764
+ raise HTTPException(status_code=503, detail="Critical system failure: Resource monitor service not initialized")
765
+ if not telemetry_service:
766
+ raise HTTPException(status_code=503, detail=ERROR_TELEMETRY_NOT_INITIALIZED)
767
+
768
+ try:
769
+ # Get current resource usage
770
+ current_usage = resource_monitor.snapshot
771
+
772
+ # Get resource limits
773
+ limits = resource_monitor.budget
774
+
775
+ # Get historical data if available
776
+ history_points = []
777
+ if telemetry_service and hasattr(telemetry_service, "query_metrics"):
778
+ now = datetime.now(timezone.utc)
779
+ hour_ago = now - timedelta(hours=1)
780
+
781
+ # Query CPU history
782
+ cpu_history = await telemetry_service.query_metrics(
783
+ metric_name="cpu_percent", start_time=hour_ago, end_time=now
784
+ )
785
+
786
+ # Query memory history
787
+ memory_history = await telemetry_service.query_metrics(
788
+ metric_name="memory_mb", start_time=hour_ago, end_time=now
789
+ )
790
+
791
+ # Build history points
792
+ for i in range(min(len(cpu_history), len(memory_history))):
793
+ history_points.append(
794
+ ResourceHistoryPoint(
795
+ timestamp=cpu_history[i].timestamp,
796
+ cpu_percent=float(cpu_history[i].value),
797
+ memory_mb=float(memory_history[i].value),
798
+ )
799
+ )
800
+
801
+ # Convert disk bytes to GB for the response
802
+ disk_bytes = getattr(current_usage, "disk_usage_bytes", 0)
803
+ disk_gb = disk_bytes / (1024 * 1024 * 1024) if disk_bytes > 0 else 0.0
804
+
805
+ response = ResourceTelemetryResponse(
806
+ current=ResourceUsageData(
807
+ cpu_percent=current_usage.cpu_percent,
808
+ memory_mb=current_usage.memory_mb,
809
+ memory_percent=current_usage.memory_percent,
810
+ disk_usage_bytes=disk_bytes,
811
+ disk_usage_gb=disk_gb,
812
+ active_threads=getattr(current_usage, "active_threads", 0),
813
+ open_files=getattr(current_usage, "open_files", 0),
814
+ timestamp=datetime.now(timezone.utc).isoformat(),
815
+ ),
816
+ limits=ResourceLimits(
817
+ max_memory_mb=getattr(limits, "max_memory_mb", 2048.0),
818
+ max_cpu_percent=getattr(limits, "max_cpu_percent", 100.0),
819
+ max_disk_bytes=getattr(limits, "max_disk_bytes", 0),
820
+ ),
821
+ history=history_points[-60:], # Last hour of data
822
+ health=ResourceHealthStatus(
823
+ status="healthy" if current_usage.memory_percent < 80 and current_usage.cpu_percent < 80 else "warning",
824
+ warnings=getattr(current_usage, "warnings", []),
825
+ ),
826
+ )
827
+
828
+ return SuccessResponse(
829
+ data=response,
830
+ metadata=ResponseMetadata(
831
+ timestamp=datetime.now(timezone.utc), request_id=str(uuid.uuid4()), duration_ms=0
832
+ ),
833
+ )
834
+
835
+ except HTTPException:
836
+ # Re-raise HTTPException as-is to preserve status code
837
+ raise
838
+ except Exception as e:
839
+ raise HTTPException(status_code=500, detail=str(e))
840
+
841
+
842
+ def _get_metric_unit(metric_name: str) -> Optional[str]:
843
+ """Determine unit from metric name."""
844
+ if "tokens" in metric_name:
845
+ return "tokens"
846
+ elif "time" in metric_name or "latency" in metric_name or "ms" in metric_name:
847
+ return "ms"
848
+ elif "percent" in metric_name or "rate" in metric_name or "cpu" in metric_name:
849
+ return "%"
850
+ elif "mb" in metric_name or "memory" in metric_name:
851
+ return "MB"
852
+ elif "cents" in metric_name:
853
+ return "cents"
854
+ elif "grams" in metric_name:
855
+ return "g"
856
+ elif "kwh" in metric_name:
857
+ return "kWh"
858
+ return "count"
859
+
860
+
861
+ def _calculate_trend(values: List[float]) -> str:
862
+ """Calculate trend from a list of values."""
863
+ if len(values) <= 1:
864
+ return "stable"
865
+
866
+ recent_avg = sum(values[-5:]) / len(values[-5:])
867
+ older_avg = sum(values[:-5]) / len(values[:-5]) if len(values) > 5 else values[0]
868
+
869
+ if recent_avg > older_avg * 1.1:
870
+ return "up"
871
+ elif recent_avg < older_avg * 0.9:
872
+ return "down"
873
+ return "stable"
874
+
875
+
876
+ async def _process_metric_data(telemetry_service: Any, metric_name: str, now: datetime) -> Optional[DetailedMetric]:
877
+ """Process metric data for a single metric name."""
878
+ if not hasattr(telemetry_service, "query_metrics"):
879
+ return None
880
+
881
+ day_ago = now - timedelta(hours=24)
882
+ hour_ago = now - timedelta(hours=1)
883
+
884
+ # Get hourly data
885
+ hourly_data = await telemetry_service.query_metrics(metric_name=metric_name, start_time=hour_ago, end_time=now)
886
+
887
+ # Get daily data
888
+ daily_data = await telemetry_service.query_metrics(metric_name=metric_name, start_time=day_ago, end_time=now)
889
+
890
+ if not (hourly_data or daily_data):
891
+ return None
892
+
893
+ # Calculate averages and trends
894
+ hourly_values = [float(dp.value) for dp in hourly_data] if hourly_data else [0.0]
895
+ daily_values = [float(dp.value) for dp in daily_data] if daily_data else [0.0]
896
+
897
+ hourly_avg = sum(hourly_values) / len(hourly_values) if hourly_values else 0.0
898
+ daily_avg = sum(daily_values) / len(daily_values) if daily_values else 0.0
899
+ current_value = hourly_values[-1] if hourly_values else 0.0
900
+
901
+ # Determine trend
902
+ trend = _calculate_trend(hourly_values)
903
+ unit = _get_metric_unit(metric_name)
904
+
905
+ return DetailedMetric(
906
+ name=metric_name,
907
+ current_value=current_value,
908
+ unit=unit,
909
+ trend=trend,
910
+ hourly_average=hourly_avg,
911
+ daily_average=daily_avg,
912
+ by_service=[], # Could aggregate by service if tags available
913
+ recent_data=[
914
+ MetricData(
915
+ timestamp=dp.timestamp,
916
+ value=float(dp.value),
917
+ tags=MetricTags(**dp.tags) if dp.tags else MetricTags(),
918
+ )
919
+ for dp in (hourly_data[-10:] if hourly_data else [])
920
+ ],
921
+ )
922
+
923
+
924
+ async def _get_legacy_metrics(telemetry_service: Any) -> List[DetailedMetric]:
925
+ """Get metrics from legacy get_metrics method."""
926
+ metrics: List[DetailedMetric] = []
927
+ if not (hasattr(telemetry_service, "get_metrics") and not hasattr(telemetry_service, "query_metrics")):
928
+ return metrics
929
+
930
+ legacy_metrics = await telemetry_service.get_metrics()
931
+ if not legacy_metrics:
932
+ return metrics
933
+
934
+ for metric_name, value in legacy_metrics.items():
935
+ unit = _get_metric_unit(metric_name)
936
+ metrics.append(
937
+ DetailedMetric(
938
+ name=metric_name,
939
+ current_value=float(value),
940
+ unit=unit,
941
+ trend="stable", # Default trend when no history
942
+ hourly_average=float(value),
943
+ daily_average=float(value),
944
+ by_service=[],
945
+ recent_data=[],
946
+ )
947
+ )
948
+ return metrics
949
+
950
+
951
+ def _calculate_metrics_summary(metrics: List[DetailedMetric]) -> MetricAggregate:
952
+ """Calculate summary statistics across all metrics."""
953
+ all_values = []
954
+ for metric in metrics:
955
+ if metric.recent_data:
956
+ all_values.extend([dp.value for dp in metric.recent_data])
957
+
958
+ if all_values:
959
+ return MetricAggregate(
960
+ min=min(all_values),
961
+ max=max(all_values),
962
+ avg=sum(all_values) / len(all_values),
963
+ sum=sum(all_values),
964
+ count=len(all_values),
965
+ )
966
+ else:
967
+ return MetricAggregate(min=0.0, max=0.0, avg=0.0, sum=0.0, count=0)
968
+
969
+
970
+ @router.get("/metrics", response_model=SuccessResponse[MetricsResponse])
971
+ async def get_detailed_metrics(
972
+ request: Request, auth: AuthContext = Depends(require_observer)
973
+ ) -> SuccessResponse[MetricsResponse]:
974
+ """
975
+ Detailed metrics.
976
+
977
+ Get detailed metrics with trends and breakdowns by service.
978
+ """
979
+ # Telemetry service MUST exist - if it doesn't, we have a critical failure
980
+ telemetry_service = request.app.state.telemetry_service
981
+ if not telemetry_service:
982
+ raise HTTPException(status_code=503, detail=ERROR_TELEMETRY_NOT_INITIALIZED)
983
+
984
+ try:
985
+ # Common metrics to query - use actual metric names that exist in TSDB nodes
986
+ metric_names = [
987
+ "llm_tokens_used", # Legacy LLM token usage
988
+ "llm_api_call_structured", # Legacy LLM API calls
989
+ "llm.tokens.total", # New format: total tokens
990
+ "llm.tokens.input", # New format: input tokens
991
+ "llm.tokens.output", # New format: output tokens
992
+ "llm.cost.cents", # Cost tracking
993
+ "llm.environmental.carbon_grams", # Carbon footprint
994
+ "llm.environmental.energy_kwh", # Energy usage
995
+ "handler_completed_total", # Handler completions
996
+ "handler_invoked_total", # Handler invocations
997
+ "thought_processing_completed", # Thought completion
998
+ "thought_processing_started", # Thought starts
999
+ "action_selected_task_complete", # Task completions
1000
+ "action_selected_memorize", # Memory operations
1001
+ ]
1002
+
1003
+ metrics = []
1004
+ now = datetime.now(timezone.utc)
1005
+
1006
+ # Process metrics with query_metrics method
1007
+ for metric_name in metric_names:
1008
+ metric = await _process_metric_data(telemetry_service, metric_name, now)
1009
+ if metric:
1010
+ metrics.append(metric)
1011
+
1012
+ # Fallback to legacy get_metrics if needed
1013
+ if not metrics:
1014
+ metrics = await _get_legacy_metrics(telemetry_service)
1015
+
1016
+ # Calculate summary statistics
1017
+ summary = _calculate_metrics_summary(metrics)
1018
+
1019
+ response = MetricsResponse(metrics=metrics, summary=summary, period="24h", timestamp=now)
1020
+
1021
+ return SuccessResponse(
1022
+ data=response,
1023
+ metadata=ResponseMetadata(
1024
+ timestamp=datetime.now(timezone.utc), request_id=str(uuid.uuid4()), duration_ms=0
1025
+ ),
1026
+ )
1027
+
1028
+ except HTTPException:
1029
+ # Re-raise HTTPException as-is to preserve status code
1030
+ raise
1031
+ except Exception as e:
1032
+ import traceback
1033
+
1034
+ logger.error(f"Error in get_detailed_metrics: {str(e)}\n{traceback.format_exc()}")
1035
+ raise HTTPException(status_code=500, detail=str(e))
1036
+
1037
+
1038
+ async def _get_trace_from_task(task: Any, visibility_service: Any) -> Optional[ReasoningTraceData]:
1039
+ """Extract a reasoning trace from a task via visibility service."""
1040
+ if not hasattr(visibility_service, "get_reasoning_trace"):
1041
+ return None
1042
+
1043
+ trace = await visibility_service.get_reasoning_trace(task.task_id)
1044
+ if not trace:
1045
+ return None
1046
+
1047
+ return ReasoningTraceData(
1048
+ trace_id=f"trace_{task.task_id}",
1049
+ task_id=task.task_id,
1050
+ task_description=task.description,
1051
+ start_time=(datetime.fromisoformat(task.created_at) if isinstance(task.created_at, str) else task.created_at),
1052
+ duration_ms=0, # TaskOutcome doesn't have completion timestamp
1053
+ thought_count=len(trace.thought_steps),
1054
+ decision_count=len(trace.decisions) if hasattr(trace, "decisions") else 0,
1055
+ reasoning_depth=len(trace.thought_steps) if hasattr(trace, "thought_steps") else 0,
1056
+ thoughts=[
1057
+ APIResponseThoughtStep(
1058
+ step=i,
1059
+ content=getattr(thought, "content", str(thought)),
1060
+ timestamp=getattr(thought, "timestamp", datetime.now(timezone.utc)),
1061
+ depth=getattr(thought, "depth", 0),
1062
+ action=getattr(thought, "action", None),
1063
+ confidence=getattr(thought, "confidence", None),
1064
+ )
1065
+ for i, thought in enumerate(trace.thought_steps)
1066
+ ],
1067
+ outcome=trace.outcome if hasattr(trace, "outcome") else None,
1068
+ )
1069
+
1070
+
1071
+ async def _get_traces_from_task_history(visibility_service: Any, limit: int) -> List[ReasoningTraceData]:
1072
+ """Get traces from task history via visibility service."""
1073
+ traces: List[ReasoningTraceData] = []
1074
+ if not hasattr(visibility_service, "get_task_history"):
1075
+ return traces
1076
+
1077
+ task_history = await visibility_service.get_task_history(limit=limit)
1078
+
1079
+ for task in task_history:
1080
+ trace_data = await _get_trace_from_task(task, visibility_service)
1081
+ if trace_data:
1082
+ traces.append(trace_data)
1083
+
1084
+ return traces
1085
+
1086
+
1087
+ async def _get_current_reasoning_trace(visibility_service: Any) -> Optional[ReasoningTraceData]:
1088
+ """Get current reasoning trace via visibility service."""
1089
+ if not hasattr(visibility_service, "get_current_reasoning"):
1090
+ return None
1091
+
1092
+ current = await visibility_service.get_current_reasoning()
1093
+ if not current:
1094
+ return None
1095
+
1096
+ return ReasoningTraceData(
1097
+ trace_id="trace_current",
1098
+ task_id=current.get("task_id"),
1099
+ task_description=current.get("task_description"),
1100
+ start_time=datetime.now(timezone.utc),
1101
+ duration_ms=0,
1102
+ thought_count=len(current.get("thoughts", [])),
1103
+ decision_count=0,
1104
+ reasoning_depth=current.get("depth", 0),
1105
+ thoughts=[_convert_thought_to_api_response(i, t) for i, t in enumerate(current.get("thoughts", []))],
1106
+ outcome=None,
1107
+ )
1108
+
1109
+
1110
+ def _extract_content_from_thought_data(thought_data: Any) -> str:
1111
+ """Extract content from thought data with fallbacks."""
1112
+ if hasattr(thought_data, "thought") and hasattr(thought_data.thought, "content"):
1113
+ return str(thought_data.thought.content)
1114
+ if isinstance(thought_data, dict):
1115
+ return str(thought_data.get("content", ""))
1116
+ return str(thought_data)
1117
+
1118
+
1119
+ def _extract_timestamp_from_thought_data(thought_data: Any) -> datetime:
1120
+ """Extract timestamp from thought data with fallbacks."""
1121
+ if hasattr(thought_data, "thought") and hasattr(thought_data.thought, "timestamp"):
1122
+ ts = thought_data.thought.timestamp
1123
+ if isinstance(ts, datetime):
1124
+ return ts
1125
+ return datetime.now(timezone.utc)
1126
+ if isinstance(thought_data, dict):
1127
+ ts_str = thought_data.get("timestamp", datetime.now(timezone.utc).isoformat())
1128
+ if isinstance(ts_str, str):
1129
+ return datetime.fromisoformat(ts_str)
1130
+ return datetime.now(timezone.utc)
1131
+
1132
+
1133
+ def _extract_depth_from_thought_data(thought_data: Any) -> int:
1134
+ """Extract depth from thought data with fallbacks."""
1135
+ if hasattr(thought_data, "thought") and hasattr(thought_data.thought, "depth"):
1136
+ depth = thought_data.thought.depth
1137
+ return int(depth) if depth is not None else 0
1138
+ if isinstance(thought_data, dict):
1139
+ depth = thought_data.get("depth", 0)
1140
+ return int(depth) if depth is not None else 0
1141
+ return 0
1142
+
1143
+
1144
+ def _extract_action_from_thought_data(thought_data: Any) -> Optional[str]:
1145
+ """Extract action from thought data with fallbacks."""
1146
+ if hasattr(thought_data, "thought") and hasattr(thought_data.thought, "action"):
1147
+ action = thought_data.thought.action
1148
+ return str(action) if action is not None else None
1149
+ if isinstance(thought_data, dict):
1150
+ action = thought_data.get("action")
1151
+ return str(action) if action is not None else None
1152
+ return None
1153
+
1154
+
1155
+ def _extract_confidence_from_thought_data(thought_data: Any) -> Optional[float]:
1156
+ """Extract confidence from thought data with fallbacks."""
1157
+ if hasattr(thought_data, "thought") and hasattr(thought_data.thought, "confidence"):
1158
+ conf = thought_data.thought.confidence
1159
+ return float(conf) if conf is not None else None
1160
+ if isinstance(thought_data, dict):
1161
+ conf = thought_data.get("confidence")
1162
+ return float(conf) if conf is not None else None
1163
+ return None
1164
+
1165
+
1166
+ def _convert_thought_to_api_response(step_index: int, thought_data: Any) -> APIResponseThoughtStep:
1167
+ """Convert thought data to APIResponseThoughtStep."""
1168
+ return APIResponseThoughtStep(
1169
+ step=step_index,
1170
+ content=_extract_content_from_thought_data(thought_data),
1171
+ timestamp=_extract_timestamp_from_thought_data(thought_data),
1172
+ depth=_extract_depth_from_thought_data(thought_data),
1173
+ action=_extract_action_from_thought_data(thought_data),
1174
+ confidence=_extract_confidence_from_thought_data(thought_data),
1175
+ )
1176
+
1177
+
1178
+ async def _get_traces_from_visibility_service(visibility_service: Any, limit: int) -> List[ReasoningTraceData]:
1179
+ """Get reasoning traces from visibility service."""
1180
+ traces = await _get_traces_from_task_history(visibility_service, limit)
1181
+
1182
+ # If no task history, try current reasoning
1183
+ if not traces:
1184
+ current_trace = await _get_current_reasoning_trace(visibility_service)
1185
+ if current_trace:
1186
+ traces.append(current_trace)
1187
+
1188
+ return traces
1189
+
1190
+
1191
+ def _parse_timestamp(timestamp_value: Any) -> datetime:
1192
+ """Parse timestamp from various formats."""
1193
+ if isinstance(timestamp_value, str):
1194
+ return datetime.fromisoformat(timestamp_value)
1195
+ elif isinstance(timestamp_value, datetime):
1196
+ return timestamp_value
1197
+ else:
1198
+ return datetime.now(timezone.utc)
1199
+
1200
+
1201
+ async def _get_traces_from_audit_service(
1202
+ audit_service: Any, start_time: Optional[datetime], end_time: Optional[datetime], limit: int
1203
+ ) -> List[ReasoningTraceData]:
1204
+ """Get reasoning traces from audit service as fallback."""
1205
+ # Query audit entries related to reasoning using the actual audit service method
1206
+ from ciris_engine.schemas.services.graph.audit import AuditQuery
1207
+
1208
+ query = AuditQuery(
1209
+ start_time=start_time,
1210
+ end_time=end_time,
1211
+ event_type="handler_action_ponder",
1212
+ limit=limit * 10, # Get more to group
1213
+ )
1214
+ entries = await audit_service.query_audit_trail(query)
1215
+
1216
+ # Group by correlation ID or time window
1217
+ trace_groups = defaultdict(list)
1218
+ for entry in entries:
1219
+ # AuditEntry objects have .context attribute which is AuditEntryContext
1220
+ context_data = entry.context.additional_data or {}
1221
+ timestamp = entry.timestamp
1222
+ trace_key = context_data.get("task_id", timestamp.strftime("%Y%m%d%H%M"))
1223
+ trace_groups[trace_key].append(entry)
1224
+
1225
+ traces = []
1226
+ for trace_id, entries in list(trace_groups.items())[:limit]:
1227
+ if entries:
1228
+ trace_data = _build_trace_from_audit_entries(trace_id, entries)
1229
+ traces.append(trace_data)
1230
+
1231
+ return traces
1232
+
1233
+
1234
+ def _build_trace_from_audit_entries(trace_id: str, entries: List[Any]) -> ReasoningTraceData:
1235
+ """Build a ReasoningTraceData from audit entries."""
1236
+ # Sort entries by timestamp - AuditEntry objects have .timestamp attribute
1237
+ entries.sort(key=lambda e: e.timestamp)
1238
+
1239
+ start_timestamp = entries[0].timestamp
1240
+ end_timestamp = entries[-1].timestamp
1241
+
1242
+ return ReasoningTraceData(
1243
+ trace_id=f"trace_{trace_id}",
1244
+ task_id=trace_id if trace_id != start_timestamp.strftime("%Y%m%d%H%M") else None,
1245
+ task_description=None,
1246
+ start_time=start_timestamp,
1247
+ duration_ms=(end_timestamp - start_timestamp).total_seconds() * 1000,
1248
+ thought_count=len(entries),
1249
+ decision_count=sum(1 for e in entries if "decision" in e.action.lower()),
1250
+ reasoning_depth=(max((e.context.additional_data or {}).get("depth", 0) for e in entries) if entries else 0),
1251
+ thoughts=[
1252
+ APIResponseThoughtStep(
1253
+ step=i,
1254
+ content=(e.context.additional_data or {}).get("thought", e.action),
1255
+ timestamp=e.timestamp,
1256
+ depth=(e.context.additional_data or {}).get("depth", 0),
1257
+ action=(e.context.additional_data or {}).get("action"),
1258
+ confidence=(e.context.additional_data or {}).get("confidence"),
1259
+ )
1260
+ for i, e in enumerate(entries)
1261
+ ],
1262
+ outcome=None,
1263
+ )
1264
+
1265
+
1266
+ @router.get("/traces", response_model=SuccessResponse[TracesResponse])
1267
+ async def get_reasoning_traces(
1268
+ request: Request,
1269
+ auth: AuthContext = Depends(require_observer),
1270
+ limit: int = Query(10, ge=1, le=100, description="Maximum traces to return"),
1271
+ start_time: Optional[datetime] = Query(None, description=DESC_START_TIME),
1272
+ end_time: Optional[datetime] = Query(None, description=DESC_END_TIME),
1273
+ ) -> SuccessResponse[TracesResponse]:
1274
+ """
1275
+ Reasoning traces.
1276
+
1277
+ Get reasoning traces showing agent thought processes and decision-making.
1278
+ """
1279
+ # These services MUST exist
1280
+ visibility_service = request.app.state.visibility_service
1281
+ audit_service = request.app.state.audit_service
1282
+
1283
+ if not visibility_service:
1284
+ raise HTTPException(status_code=503, detail="Critical system failure: Visibility service not initialized")
1285
+ if not audit_service:
1286
+ raise HTTPException(status_code=503, detail=ERROR_AUDIT_NOT_INITIALIZED)
1287
+
1288
+ traces = []
1289
+
1290
+ # Try to get from visibility service first
1291
+ try:
1292
+ traces = await _get_traces_from_visibility_service(visibility_service, limit)
1293
+ except Exception as e:
1294
+ logger.warning(
1295
+ f"Telemetry metric retrieval failed for reasoning traces from visibility service: {type(e).__name__}: {str(e)} - Returning default/empty value"
1296
+ )
1297
+
1298
+ # Fallback to audit-based traces
1299
+ if not traces:
1300
+ try:
1301
+ traces = await _get_traces_from_audit_service(audit_service, start_time, end_time, limit)
1302
+ except Exception as e:
1303
+ logger.warning(
1304
+ f"Telemetry metric retrieval failed for reasoning traces from audit service: {type(e).__name__}: {str(e)} - Returning default/empty value"
1305
+ )
1306
+
1307
+ response = TracesResponse(traces=traces, total=len(traces), has_more=len(traces) == limit)
1308
+
1309
+ return SuccessResponse(
1310
+ data=response,
1311
+ metadata=ResponseMetadata(timestamp=datetime.now(timezone.utc), request_id=str(uuid.uuid4()), duration_ms=0),
1312
+ )
1313
+
1314
+
1315
+ def _validate_audit_service(audit_service: Any) -> None:
1316
+ """Validate that audit service is available."""
1317
+ if not audit_service:
1318
+ raise HTTPException(status_code=503, detail=ERROR_AUDIT_NOT_INITIALIZED)
1319
+
1320
+
1321
+ def _determine_log_level(action: str) -> str:
1322
+ """Determine log level from audit action."""
1323
+ action_lower = action.lower()
1324
+ if "critical" in action_lower or "fatal" in action_lower:
1325
+ return "CRITICAL"
1326
+ elif "error" in action_lower or "fail" in action_lower:
1327
+ return "ERROR"
1328
+ elif "warning" in action_lower or "warn" in action_lower:
1329
+ return "WARNING"
1330
+ elif "debug" in action_lower:
1331
+ return "DEBUG"
1332
+ return "INFO"
1333
+
1334
+
1335
+ def _extract_service_name(actor: str) -> str:
1336
+ """Extract service name from actor string."""
1337
+ return actor.split(".")[0] if "." in actor else actor
1338
+
1339
+
1340
+ def _should_include_log(
1341
+ log_level: str, log_service: str, level_filter: Optional[str], service_filter: Optional[str]
1342
+ ) -> bool:
1343
+ """Check if log entry should be included based on filters."""
1344
+ if level_filter and log_level != level_filter.upper():
1345
+ return False
1346
+ if service_filter and log_service.lower() != service_filter.lower():
1347
+ return False
1348
+ return True
1349
+
1350
+
1351
+ def _build_log_entry(entry: Any, log_level: str, log_service: str) -> LogEntryResponse:
1352
+ """Build LogEntry from audit entry."""
1353
+ # AuditEntry.context is AuditEntryContext with .additional_data dict
1354
+ context_data = entry.context.additional_data or {}
1355
+
1356
+ return LogEntryResponse(
1357
+ timestamp=entry.timestamp,
1358
+ level=log_level,
1359
+ service=log_service,
1360
+ message=f"{entry.action}: {context_data.get('description', '')}".strip(": "),
1361
+ context=LogContext(
1362
+ trace_id=entry.context.correlation_id,
1363
+ correlation_id=entry.context.correlation_id,
1364
+ user_id=entry.context.user_id,
1365
+ entity_id=context_data.get("entity_id"),
1366
+ error_details=context_data.get("error_details", {}) if "error" in log_level.lower() else None,
1367
+ metadata=context_data,
1368
+ ),
1369
+ trace_id=entry.context.correlation_id,
1370
+ )
1371
+
1372
+
1373
+ async def _get_logs_from_audit_service(
1374
+ audit_service: Any,
1375
+ start_time: Optional[datetime],
1376
+ end_time: Optional[datetime],
1377
+ level: Optional[str],
1378
+ service: Optional[str],
1379
+ limit: int,
1380
+ ) -> List[LogEntryResponse]:
1381
+ """Get logs from audit service with filtering."""
1382
+ logs = []
1383
+ try:
1384
+ from ciris_engine.schemas.services.graph.audit import AuditQuery
1385
+
1386
+ audit_query = AuditQuery(start_time=start_time, end_time=end_time, limit=limit * 2) # Get extra for filtering
1387
+ entries = await audit_service.query_audit_trail(audit_query)
1388
+
1389
+ for entry in entries:
1390
+ log_level = _determine_log_level(entry.action)
1391
+ log_service = _extract_service_name(entry.actor)
1392
+
1393
+ if not _should_include_log(log_level, log_service, level, service):
1394
+ continue
1395
+
1396
+ log = _build_log_entry(entry, log_level, log_service)
1397
+ logs.append(log)
1398
+
1399
+ if len(logs) >= limit:
1400
+ break
1401
+ except Exception as e:
1402
+ logger.warning(f"Failed to get logs from audit service: {e}")
1403
+
1404
+ return logs
1405
+
1406
+
1407
+ async def _get_logs_from_file_reader(
1408
+ level: Optional[str],
1409
+ service: Optional[str],
1410
+ limit: int,
1411
+ start_time: Optional[datetime],
1412
+ end_time: Optional[datetime],
1413
+ ) -> List[LogEntryResponse]:
1414
+ """Get logs from file reader if available."""
1415
+ try:
1416
+ from .telemetry_logs_reader import log_reader
1417
+
1418
+ # The log_reader returns List[LogEntry] from telemetry_models, need to convert
1419
+ logs = log_reader.read_logs(
1420
+ level=level,
1421
+ service=service,
1422
+ limit=limit,
1423
+ start_time=start_time,
1424
+ end_time=end_time,
1425
+ include_incidents=True,
1426
+ )
1427
+ # Convert LogEntry to LogEntryResponse (they are the same structure now)
1428
+ return [LogEntryResponse(**log.model_dump()) for log in logs]
1429
+ except ImportError:
1430
+ logger.debug("Log reader not available, using audit entries only")
1431
+ return []
1432
+ except Exception as e:
1433
+ logger.warning(f"Failed to read log files: {e}, using audit entries only")
1434
+ return []
1435
+
1436
+
1437
+ @router.get("/logs", response_model=SuccessResponse[LogsResponse])
1438
+ async def get_system_logs(
1439
+ request: Request,
1440
+ auth: AuthContext = Depends(require_observer),
1441
+ start_time: Optional[datetime] = Query(None, description=DESC_START_TIME),
1442
+ end_time: Optional[datetime] = Query(None, description=DESC_END_TIME),
1443
+ level: Optional[str] = Query(None, description="Log level filter"),
1444
+ service: Optional[str] = Query(None, description="Service filter"),
1445
+ limit: int = Query(100, ge=1, le=1000, description="Maximum logs to return"),
1446
+ ) -> SuccessResponse[LogsResponse]:
1447
+ """
1448
+ System logs.
1449
+
1450
+ Get system logs from all services with filtering capabilities.
1451
+ """
1452
+ audit_service = request.app.state.audit_service
1453
+ _validate_audit_service(audit_service)
1454
+
1455
+ # Get logs from audit service
1456
+ logs = await _get_logs_from_audit_service(audit_service, start_time, end_time, level, service, limit)
1457
+
1458
+ # Add file logs if we haven't reached the limit
1459
+ if len(logs) < limit:
1460
+ file_logs = await _get_logs_from_file_reader(level, service, limit - len(logs), start_time, end_time)
1461
+ logs.extend(file_logs)
1462
+
1463
+ response = LogsResponse(logs=logs[:limit], total=len(logs), has_more=len(logs) > limit)
1464
+
1465
+ return SuccessResponse(
1466
+ data=response,
1467
+ metadata=ResponseMetadata(timestamp=datetime.now(timezone.utc), request_id=str(uuid.uuid4()), duration_ms=0),
1468
+ )
1469
+
1470
+
1471
+ async def _query_metrics(telemetry_service: Any, query: TelemetryQuery) -> List[QueryResult]:
1472
+ """Query metrics data."""
1473
+ results: List[QueryResult] = []
1474
+ if not (telemetry_service and hasattr(telemetry_service, "query_metrics")):
1475
+ return results
1476
+
1477
+ metric_names = query.filters.metric_names or []
1478
+ for metric_name in metric_names:
1479
+ data_points = await telemetry_service.query_metrics(
1480
+ metric_name=metric_name, start_time=query.start_time, end_time=query.end_time
1481
+ )
1482
+ if data_points:
1483
+ results.append(
1484
+ QueryResult(
1485
+ id=f"metric_{metric_name}",
1486
+ type="metric",
1487
+ timestamp=datetime.now(timezone.utc),
1488
+ data={
1489
+ "metric_name": metric_name,
1490
+ "data_points": data_points,
1491
+ "count": len(data_points),
1492
+ },
1493
+ )
1494
+ )
1495
+ return results
1496
+
1497
+
1498
+ async def _query_traces(visibility_service: Any, query: TelemetryQuery) -> List[QueryResult]:
1499
+ """Query reasoning traces."""
1500
+ results: List[QueryResult] = []
1501
+ if not visibility_service:
1502
+ return results
1503
+
1504
+ trace_limit = query.filters.limit or query.limit
1505
+ traces = []
1506
+
1507
+ if hasattr(visibility_service, "query_traces"):
1508
+ traces = await visibility_service.query_traces(
1509
+ start_time=query.start_time, end_time=query.end_time, limit=trace_limit
1510
+ )
1511
+
1512
+ for trace in traces:
1513
+ results.append(
1514
+ QueryResult(
1515
+ id=trace.trace_id,
1516
+ type="trace",
1517
+ timestamp=trace.start_time,
1518
+ data={
1519
+ "trace_id": trace.trace_id,
1520
+ "task_id": trace.task_id,
1521
+ "duration_ms": trace.duration_ms,
1522
+ "thought_count": trace.thought_count,
1523
+ },
1524
+ )
1525
+ )
1526
+ return results
1527
+
1528
+
1529
+ def _should_include_log_entry(entry: Any, filters: Any) -> bool:
1530
+ """Check if log entry should be included based on filters."""
1531
+ if not filters:
1532
+ return True
1533
+
1534
+ if filters.services and entry.actor not in filters.services:
1535
+ return False
1536
+
1537
+ if filters.severity:
1538
+ # Infer level from action
1539
+ if "error" in entry.action.lower() and filters.severity.upper() != "ERROR":
1540
+ return False
1541
+
1542
+ return True
1543
+
1544
+
1545
+ async def _query_logs(audit_service: Any, query: TelemetryQuery) -> List[QueryResult]:
1546
+ """Query logs data."""
1547
+ results: List[QueryResult] = []
1548
+ if not audit_service:
1549
+ return results
1550
+
1551
+ from ciris_engine.schemas.services.graph.audit import AuditQuery
1552
+
1553
+ audit_query = AuditQuery(start_time=query.start_time, end_time=query.end_time, limit=query.limit)
1554
+ log_entries = await audit_service.query_audit_trail(audit_query)
1555
+
1556
+ for entry in log_entries:
1557
+ if not _should_include_log_entry(entry, query.filters):
1558
+ continue
1559
+
1560
+ results.append(
1561
+ QueryResult(
1562
+ id=f"log_{entry.timestamp.timestamp()}_{entry.actor}",
1563
+ type="log",
1564
+ timestamp=entry.timestamp,
1565
+ data={
1566
+ "timestamp": entry.timestamp.isoformat(),
1567
+ "service": entry.actor,
1568
+ "action": entry.action,
1569
+ "context": entry.context.model_dump() if hasattr(entry.context, "model_dump") else entry.context,
1570
+ },
1571
+ )
1572
+ )
1573
+ return results
1574
+
1575
+
1576
+ async def _query_incidents(incident_service: Any, query: TelemetryQuery) -> List[QueryResult]:
1577
+ """Query incidents data."""
1578
+ results: List[QueryResult] = []
1579
+ if not incident_service:
1580
+ return results
1581
+
1582
+ incidents = await incident_service.query_incidents(
1583
+ start_time=query.start_time,
1584
+ end_time=query.end_time,
1585
+ severity=query.filters.severity,
1586
+ status=getattr(query.filters, "status", None),
1587
+ )
1588
+
1589
+ for incident in incidents:
1590
+ results.append(
1591
+ QueryResult(
1592
+ id=incident.id,
1593
+ type="incident",
1594
+ timestamp=getattr(incident, "created_at", incident.detected_at),
1595
+ data={
1596
+ "incident_id": incident.id,
1597
+ "severity": incident.severity,
1598
+ "status": incident.status,
1599
+ "description": incident.description,
1600
+ "created_at": getattr(incident, "created_at", incident.detected_at).isoformat(),
1601
+ },
1602
+ )
1603
+ )
1604
+ return results
1605
+
1606
+
1607
+ async def _query_insights(incident_service: Any, query: TelemetryQuery) -> List[QueryResult]:
1608
+ """Query adaptation insights."""
1609
+ results: List[QueryResult] = []
1610
+ if not (incident_service and hasattr(incident_service, "get_insights")):
1611
+ return results
1612
+
1613
+ insights = await incident_service.get_insights(
1614
+ start_time=query.start_time, end_time=query.end_time, limit=query.limit
1615
+ )
1616
+
1617
+ for insight in insights:
1618
+ results.append(
1619
+ QueryResult(
1620
+ id=insight.id,
1621
+ type="insight",
1622
+ timestamp=getattr(insight, "created_at", insight.analysis_timestamp),
1623
+ data={
1624
+ "insight_id": insight.id,
1625
+ "insight_type": insight.insight_type,
1626
+ "summary": insight.summary,
1627
+ "details": insight.details,
1628
+ "created_at": getattr(insight, "created_at", insight.analysis_timestamp).isoformat(),
1629
+ },
1630
+ )
1631
+ )
1632
+ return results
1633
+
1634
+
1635
+ def _apply_aggregations(
1636
+ results: List[QueryResult], aggregations: Optional[List[str]], query_type: str
1637
+ ) -> List[QueryResult]:
1638
+ """Apply aggregations to query results."""
1639
+ if not aggregations:
1640
+ return results
1641
+
1642
+ for agg in aggregations:
1643
+ if agg == "count":
1644
+ # Return count as a QueryResult
1645
+ return [
1646
+ QueryResult(
1647
+ id="aggregation_count",
1648
+ type="aggregation",
1649
+ timestamp=datetime.now(timezone.utc),
1650
+ data={"aggregation": "count", "value": len(results)},
1651
+ )
1652
+ ]
1653
+ elif agg == "group_by_service" and query_type == "logs":
1654
+ # Group logs by service
1655
+ grouped: Dict[str, int] = defaultdict(int)
1656
+ for r in results:
1657
+ # Access service from the data field
1658
+ service_val = r.data.get("service", "unknown")
1659
+ service = str(service_val) if service_val is not None else "unknown"
1660
+ grouped[service] += 1
1661
+
1662
+ # Convert grouped results to QueryResult objects
1663
+ return [
1664
+ QueryResult(
1665
+ id=f"aggregation_service_{k}",
1666
+ type="aggregation",
1667
+ timestamp=datetime.now(timezone.utc),
1668
+ data={"service": k, "count": v},
1669
+ )
1670
+ for k, v in grouped.items()
1671
+ ]
1672
+ return results
1673
+
1674
+
1675
+ def _validate_query_services(
1676
+ telemetry_service: Any, visibility_service: Any, audit_service: Any, incident_service: Any
1677
+ ) -> None:
1678
+ """Validate that all required services are available for queries."""
1679
+ if not telemetry_service:
1680
+ raise HTTPException(status_code=503, detail=ERROR_TELEMETRY_NOT_INITIALIZED)
1681
+ if not visibility_service:
1682
+ raise HTTPException(status_code=503, detail="Critical system failure: Visibility service not initialized")
1683
+ if not audit_service:
1684
+ raise HTTPException(status_code=503, detail=ERROR_AUDIT_NOT_INITIALIZED)
1685
+ if not incident_service:
1686
+ raise HTTPException(status_code=503, detail="Critical system failure: Incident service not initialized")
1687
+
1688
+
1689
+ async def _route_query_to_handler(
1690
+ query: TelemetryQuery, telemetry_service: Any, visibility_service: Any, audit_service: Any, incident_service: Any
1691
+ ) -> List[QueryResult]:
1692
+ """Route query to appropriate handler based on query type."""
1693
+ if query.query_type == "metrics":
1694
+ return await _query_metrics(telemetry_service, query)
1695
+ elif query.query_type == "traces":
1696
+ return await _query_traces(visibility_service, query)
1697
+ elif query.query_type == "logs":
1698
+ return await _query_logs(audit_service, query)
1699
+ elif query.query_type == "incidents":
1700
+ return await _query_incidents(incident_service, query)
1701
+ elif query.query_type == "insights":
1702
+ return await _query_insights(incident_service, query)
1703
+ return []
1704
+
1705
+
1706
+ def _build_query_response(
1707
+ query: TelemetryQuery, results: List[QueryResult], execution_time_ms: float
1708
+ ) -> SuccessResponse[QueryResponse]:
1709
+ """Build the final query response."""
1710
+ response = QueryResponse(
1711
+ query_type=query.query_type,
1712
+ results=results[: query.limit],
1713
+ total=len(results),
1714
+ execution_time_ms=execution_time_ms,
1715
+ )
1716
+
1717
+ return SuccessResponse(
1718
+ data=response,
1719
+ metadata=ResponseMetadata(timestamp=datetime.now(timezone.utc), request_id=str(uuid.uuid4()), duration_ms=0),
1720
+ )
1721
+
1722
+
1723
+ @router.post("/query", response_model=SuccessResponse[QueryResponse])
1724
+ async def query_telemetry(
1725
+ request: Request, query: TelemetryQuery, auth: AuthContext = Depends(require_admin)
1726
+ ) -> SuccessResponse[QueryResponse]:
1727
+ """
1728
+ Custom telemetry queries.
1729
+
1730
+ Execute custom queries against telemetry data including metrics, traces, logs, incidents, and insights.
1731
+ Requires ADMIN role.
1732
+ """
1733
+ start_time = datetime.now(timezone.utc)
1734
+
1735
+ # Get and validate services
1736
+ telemetry_service = request.app.state.telemetry_service
1737
+ visibility_service = request.app.state.visibility_service
1738
+ audit_service = request.app.state.audit_service
1739
+ incident_service = request.app.state.incident_management_service
1740
+
1741
+ _validate_query_services(telemetry_service, visibility_service, audit_service, incident_service)
1742
+
1743
+ try:
1744
+ # Route query to appropriate handler
1745
+ results = await _route_query_to_handler(
1746
+ query, telemetry_service, visibility_service, audit_service, incident_service
1747
+ )
1748
+
1749
+ # Apply aggregations if specified
1750
+ results = _apply_aggregations(results, query.aggregations, query.query_type)
1751
+
1752
+ # Calculate execution time and build response
1753
+ execution_time = (datetime.now(timezone.utc) - start_time).total_seconds() * 1000
1754
+ return _build_query_response(query, results, execution_time)
1755
+
1756
+ except HTTPException:
1757
+ raise
1758
+ except Exception as e:
1759
+ raise HTTPException(status_code=500, detail=str(e))
1760
+
1761
+
1762
+ @router.get("/metrics/{metric_name}", response_model=SuccessResponse[DetailedMetric])
1763
+ async def get_detailed_metric(
1764
+ request: Request,
1765
+ metric_name: str,
1766
+ auth: AuthContext = Depends(require_observer),
1767
+ hours: int = Query(24, ge=1, le=168, description="Hours of history to include"),
1768
+ ) -> SuccessResponse[DetailedMetric]:
1769
+ """
1770
+ Get detailed information about a specific metric.
1771
+
1772
+ Returns current value, trends, and historical data for the specified metric.
1773
+ """
1774
+ # Telemetry service MUST exist - if it doesn't, we have a critical failure
1775
+ telemetry_service = request.app.state.telemetry_service
1776
+ if not telemetry_service:
1777
+ raise HTTPException(status_code=503, detail=ERROR_TELEMETRY_NOT_INITIALIZED)
1778
+
1779
+ try:
1780
+ now = datetime.now(timezone.utc)
1781
+ start_time = now - timedelta(hours=hours)
1782
+
1783
+ # Get metric data
1784
+ data_points = []
1785
+ if hasattr(telemetry_service, "query_metrics"):
1786
+ data_points = await telemetry_service.query_metrics(
1787
+ metric_name=metric_name, start_time=start_time, end_time=now
1788
+ )
1789
+
1790
+ if not data_points:
1791
+ raise HTTPException(status_code=404, detail=f"Metric '{metric_name}' not found")
1792
+
1793
+ # Calculate statistics
1794
+ values = [float(dp.value) for dp in data_points]
1795
+ current_value = values[-1] if values else 0.0
1796
+ hourly_avg = sum(values[-60:]) / len(values[-60:]) if len(values) > 60 else sum(values) / len(values)
1797
+ daily_avg = sum(values) / len(values)
1798
+
1799
+ # Determine trend
1800
+ trend = "stable"
1801
+ if len(values) > 10:
1802
+ recent = sum(values[-10:]) / 10
1803
+ older = sum(values[-20:-10]) / 10
1804
+ if recent > older * 1.1:
1805
+ trend = "up"
1806
+ elif recent < older * 0.9:
1807
+ trend = "down"
1808
+
1809
+ # Determine unit
1810
+ unit = None
1811
+ if "tokens" in metric_name:
1812
+ unit = "tokens"
1813
+ elif "time" in metric_name or "latency" in metric_name:
1814
+ unit = "ms"
1815
+ elif "percent" in metric_name or "rate" in metric_name:
1816
+ unit = "%"
1817
+ elif "bytes" in metric_name or "memory" in metric_name:
1818
+ unit = "bytes"
1819
+ elif "count" in metric_name or "total" in metric_name:
1820
+ unit = "count"
1821
+
1822
+ metric = DetailedMetric(
1823
+ name=metric_name,
1824
+ current_value=current_value,
1825
+ unit=unit,
1826
+ trend=trend,
1827
+ hourly_average=hourly_avg,
1828
+ daily_average=daily_avg,
1829
+ by_service=[], # Could be populated if service tags are available
1830
+ recent_data=[
1831
+ MetricData(
1832
+ timestamp=dp.timestamp,
1833
+ value=float(dp.value),
1834
+ tags=MetricTags(**dp.tags) if dp.tags else MetricTags(),
1835
+ )
1836
+ for dp in data_points[-100:] # Last 100 data points
1837
+ ],
1838
+ )
1839
+
1840
+ return SuccessResponse(
1841
+ data=metric,
1842
+ metadata=ResponseMetadata(
1843
+ timestamp=datetime.now(timezone.utc), request_id=str(uuid.uuid4()), duration_ms=0
1844
+ ),
1845
+ )
1846
+
1847
+ except HTTPException:
1848
+ # Re-raise HTTPException as-is to preserve status code
1849
+ raise
1850
+ except Exception as e:
1851
+ raise HTTPException(status_code=500, detail=str(e))
1852
+
1853
+
1854
+ # All resource metric models are now in telemetry_models.py with proper type safety
1855
+
1856
+
1857
+ # Helper functions moved to telemetry_helpers.py
1858
+
1859
+
1860
+ @router.get("/unified", response_model=None)
1861
+ async def get_unified_telemetry(
1862
+ request: Request,
1863
+ auth: AuthContext = Depends(require_observer),
1864
+ view: str = Query("summary", description="View type: summary|health|operational|detailed|performance|reliability"),
1865
+ category: Optional[str] = Query(
1866
+ None, description="Filter by category: buses|graph|infrastructure|governance|runtime|adapters|components|all"
1867
+ ),
1868
+ format: str = Query("json", description="Output format: json|prometheus|graphite"),
1869
+ live: bool = Query(False, description="Force live collection (bypass cache)"),
1870
+ ) -> JSONDict | Response:
1871
+ """
1872
+ Unified enterprise telemetry endpoint.
1873
+
1874
+ This single endpoint replaces 78+ individual telemetry routes by intelligently
1875
+ aggregating metrics from all 22 required services using parallel collection.
1876
+
1877
+ Features:
1878
+ - Parallel collection from all services (10x faster than sequential)
1879
+ - Smart caching with 30-second TTL
1880
+ - Multiple views for different stakeholders
1881
+ - System health and reliability scoring
1882
+ - Export formats for monitoring tools
1883
+
1884
+ Examples:
1885
+ - /telemetry/unified?view=summary - Executive dashboard
1886
+ - /telemetry/unified?view=health - Quick health check
1887
+ - /telemetry/unified?view=operational&live=true - Live ops data
1888
+ - /telemetry/unified?view=reliability - System reliability metrics
1889
+ - /telemetry/unified?category=buses - Just bus metrics
1890
+ - /telemetry/unified?format=prometheus - Prometheus export
1891
+ """
1892
+ try:
1893
+ # Get the telemetry service
1894
+ telemetry_service = getattr(request.app.state, "telemetry_service", None)
1895
+ if not telemetry_service:
1896
+ raise HTTPException(status_code=503, detail=ERROR_TELEMETRY_SERVICE_NOT_AVAILABLE)
1897
+
1898
+ # Get telemetry data - NO FALLBACKS, fail FAST and LOUD per CIRIS philosophy
1899
+ if not hasattr(telemetry_service, "get_aggregated_telemetry"):
1900
+ raise HTTPException(
1901
+ status_code=503,
1902
+ detail="CRITICAL: Telemetry service does not have get_aggregated_telemetry method - NO FALLBACKS!",
1903
+ )
1904
+ result = await get_telemetry_from_service(telemetry_service, view, category, format, live)
1905
+
1906
+ # Handle export formats
1907
+ if format == "prometheus":
1908
+ content = convert_to_prometheus(result)
1909
+ return Response(content=content, media_type="text/plain; version=0.0.4; charset=utf-8")
1910
+ elif format == "graphite":
1911
+ content = convert_to_graphite(result)
1912
+ return Response(content=content, media_type="text/plain; charset=utf-8")
1913
+
1914
+ return result
1915
+
1916
+ except HTTPException:
1917
+ # Re-raise HTTPException as-is to preserve status code
1918
+ raise
1919
+ except Exception as e:
1920
+ raise HTTPException(status_code=500, detail=str(e))
1921
+
1922
+
1923
+ @router.get("/resources/history", response_model=SuccessResponse)
1924
+ async def get_resource_history(
1925
+ request: Request,
1926
+ auth: AuthContext = Depends(require_observer),
1927
+ hours: int = Query(24, ge=1, le=168, description="Hours of history"),
1928
+ ) -> SuccessResponse[ResourceHistoryResponse]:
1929
+ """
1930
+ Get historical resource usage data.
1931
+
1932
+ Returns time-series data for resource usage over the specified period.
1933
+ """
1934
+ # Telemetry service MUST exist - if it doesn't, we have a critical failure
1935
+ telemetry_service = request.app.state.telemetry_service
1936
+ if not telemetry_service:
1937
+ raise HTTPException(status_code=503, detail=ERROR_TELEMETRY_NOT_INITIALIZED)
1938
+
1939
+ try:
1940
+ now = datetime.now(timezone.utc)
1941
+ start_time = now - timedelta(hours=hours)
1942
+
1943
+ # Use helper classes for clean separation of concerns
1944
+ collector = ResourceMetricsCollector()
1945
+ extractor = MetricValueExtractor()
1946
+ builder = ResourceMetricBuilder()
1947
+
1948
+ # Fetch all metrics concurrently
1949
+ cpu_data, memory_data, disk_data = await collector.fetch_all_resource_metrics(
1950
+ telemetry_service, start_time, now
1951
+ )
1952
+
1953
+ # Extract values for statistics
1954
+ cpu_values, memory_values, disk_values = extractor.extract_all_values(cpu_data, memory_data, disk_data)
1955
+
1956
+ # Build data points
1957
+ default_timestamp = now.isoformat()
1958
+ point_builder = ResourceDataPointBuilder()
1959
+ cpu_points, memory_points, disk_points = point_builder.build_all_data_points(
1960
+ cpu_data, memory_data, disk_data, default_timestamp
1961
+ )
1962
+
1963
+ # Build complete metrics with stats
1964
+ cpu_metric, memory_metric, disk_metric = builder.build_all_metrics(
1965
+ cpu_points, cpu_values, memory_points, memory_values, disk_points, disk_values
1966
+ )
1967
+
1968
+ # Create properly typed response using Pydantic models
1969
+ response = ResourceHistoryResponse(
1970
+ period=TimePeriod(start=start_time.isoformat(), end=now.isoformat(), hours=hours),
1971
+ cpu=cpu_metric,
1972
+ memory=memory_metric,
1973
+ disk=disk_metric,
1974
+ )
1975
+
1976
+ return SuccessResponse(
1977
+ data=response,
1978
+ metadata=ResponseMetadata(
1979
+ timestamp=datetime.now(timezone.utc), request_id=str(uuid.uuid4()), duration_ms=0
1980
+ ),
1981
+ )
1982
+
1983
+ except HTTPException:
1984
+ # Re-raise HTTPException as-is to preserve status code
1985
+ raise
1986
+ except Exception as e:
1987
+ raise HTTPException(status_code=500, detail=str(e))