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.
- ciris_adapters/README.md +113 -0
- ciris_adapters/__init__.py +30 -0
- ciris_adapters/ciris_covenant_metrics/README.md +144 -0
- ciris_adapters/ciris_covenant_metrics/__init__.py +36 -0
- ciris_adapters/ciris_covenant_metrics/adapter.py +249 -0
- ciris_adapters/ciris_covenant_metrics/manifest.json +152 -0
- ciris_adapters/ciris_covenant_metrics/services.py +403 -0
- ciris_adapters/ciris_hosted_tools/__init__.py +24 -0
- ciris_adapters/ciris_hosted_tools/adapter.py +169 -0
- ciris_adapters/ciris_hosted_tools/manifest.json +94 -0
- ciris_adapters/ciris_hosted_tools/services.py +744 -0
- ciris_adapters/external_data_sql/README.md +559 -0
- ciris_adapters/external_data_sql/__init__.py +43 -0
- ciris_adapters/external_data_sql/adapter.py +144 -0
- ciris_adapters/external_data_sql/configurable.py +315 -0
- ciris_adapters/external_data_sql/dialects/__init__.py +37 -0
- ciris_adapters/external_data_sql/dialects/base.py +133 -0
- ciris_adapters/external_data_sql/dialects/mysql.py +63 -0
- ciris_adapters/external_data_sql/dialects/postgresql.py +59 -0
- ciris_adapters/external_data_sql/dialects/sqlite.py +62 -0
- ciris_adapters/external_data_sql/example_config.json +88 -0
- ciris_adapters/external_data_sql/example_privacy_schema.yaml +127 -0
- ciris_adapters/external_data_sql/manifest.json +195 -0
- ciris_adapters/external_data_sql/privacy_schema_loader.py +189 -0
- ciris_adapters/external_data_sql/protocol.py +101 -0
- ciris_adapters/external_data_sql/schemas.py +146 -0
- ciris_adapters/external_data_sql/service.py +1547 -0
- ciris_adapters/external_data_sql/service_old.py +492 -0
- ciris_adapters/home_assistant/__init__.py +63 -0
- ciris_adapters/home_assistant/adapter.py +201 -0
- ciris_adapters/home_assistant/communication_service.py +347 -0
- ciris_adapters/home_assistant/configurable.py +667 -0
- ciris_adapters/home_assistant/manifest.json +203 -0
- ciris_adapters/home_assistant/schemas.py +129 -0
- ciris_adapters/home_assistant/service.py +751 -0
- ciris_adapters/home_assistant/tool_service.py +441 -0
- ciris_adapters/mcp_client/__init__.py +82 -0
- ciris_adapters/mcp_client/adapter.py +847 -0
- ciris_adapters/mcp_client/config.py +280 -0
- ciris_adapters/mcp_client/configurable.py +422 -0
- ciris_adapters/mcp_client/manifest.json +185 -0
- ciris_adapters/mcp_client/mcp_communication_service.py +393 -0
- ciris_adapters/mcp_client/mcp_tool_service.py +463 -0
- ciris_adapters/mcp_client/mcp_wise_service.py +394 -0
- ciris_adapters/mcp_client/schemas.py +149 -0
- ciris_adapters/mcp_client/security.py +592 -0
- ciris_adapters/mcp_common/__init__.py +44 -0
- ciris_adapters/mcp_common/manifest.json +25 -0
- ciris_adapters/mcp_common/protocol.py +315 -0
- ciris_adapters/mcp_common/schemas.py +225 -0
- ciris_adapters/mcp_server/__init__.py +47 -0
- ciris_adapters/mcp_server/adapter.py +581 -0
- ciris_adapters/mcp_server/config.py +260 -0
- ciris_adapters/mcp_server/configurable.py +393 -0
- ciris_adapters/mcp_server/handlers.py +663 -0
- ciris_adapters/mcp_server/manifest.json +211 -0
- ciris_adapters/mcp_server/security.py +500 -0
- ciris_adapters/mock_llm/README.md +117 -0
- ciris_adapters/mock_llm/__init__.py +21 -0
- ciris_adapters/mock_llm/adapter.py +131 -0
- ciris_adapters/mock_llm/configurable.py +237 -0
- ciris_adapters/mock_llm/manifest.json +106 -0
- ciris_adapters/mock_llm/protocol.py +37 -0
- ciris_adapters/mock_llm/responses.py +520 -0
- ciris_adapters/mock_llm/responses_action_selection.py +1041 -0
- ciris_adapters/mock_llm/responses_epistemic.py +17 -0
- ciris_adapters/mock_llm/responses_feedback.py +27 -0
- ciris_adapters/mock_llm/schemas.py +35 -0
- ciris_adapters/mock_llm/service.py +294 -0
- ciris_adapters/navigation/__init__.py +21 -0
- ciris_adapters/navigation/adapter.py +129 -0
- ciris_adapters/navigation/configurable.py +239 -0
- ciris_adapters/navigation/manifest.json +104 -0
- ciris_adapters/navigation/service.py +487 -0
- ciris_adapters/reddit/README.md +132 -0
- ciris_adapters/reddit/REDDIT_ADAPTER_ANALYSIS.md +715 -0
- ciris_adapters/reddit/REDDIT_ADAPTER_SUMMARY.txt +278 -0
- ciris_adapters/reddit/REDDIT_ANALYSIS_INDEX.md +307 -0
- ciris_adapters/reddit/REDDIT_PRODUCTION_READINESS_PLAN.md +518 -0
- ciris_adapters/reddit/__init__.py +15 -0
- ciris_adapters/reddit/adapter.py +189 -0
- ciris_adapters/reddit/configurable.py +274 -0
- ciris_adapters/reddit/error_handler.py +307 -0
- ciris_adapters/reddit/manifest.json +218 -0
- ciris_adapters/reddit/observer.py +532 -0
- ciris_adapters/reddit/protocol.py +34 -0
- ciris_adapters/reddit/schemas.py +433 -0
- ciris_adapters/reddit/service.py +1471 -0
- ciris_adapters/sample_adapter/README.md +474 -0
- ciris_adapters/sample_adapter/__init__.py +45 -0
- ciris_adapters/sample_adapter/adapter.py +208 -0
- ciris_adapters/sample_adapter/configurable.py +469 -0
- ciris_adapters/sample_adapter/manifest.json +247 -0
- ciris_adapters/sample_adapter/services.py +486 -0
- ciris_adapters/weather/__init__.py +16 -0
- ciris_adapters/weather/adapter.py +130 -0
- ciris_adapters/weather/configurable.py +240 -0
- ciris_adapters/weather/manifest.json +156 -0
- ciris_adapters/weather/service.py +600 -0
- ciris_agent-1.7.7.dist-info/METADATA +284 -0
- ciris_agent-1.7.7.dist-info/RECORD +986 -0
- ciris_agent-1.7.7.dist-info/WHEEL +5 -0
- ciris_agent-1.7.7.dist-info/entry_points.txt +15 -0
- ciris_agent-1.7.7.dist-info/licenses/LICENSE +205 -0
- ciris_agent-1.7.7.dist-info/licenses/NOTICE +82 -0
- ciris_agent-1.7.7.dist-info/top_level.txt +4 -0
- ciris_engine/__init__.py +15 -0
- ciris_engine/ciris_templates/ally.yaml +632 -0
- ciris_engine/ciris_templates/default.yaml +411 -0
- ciris_engine/ciris_templates/echo-core.yaml +629 -0
- ciris_engine/ciris_templates/echo-speculative.yaml +764 -0
- ciris_engine/ciris_templates/echo.yaml +647 -0
- ciris_engine/ciris_templates/sage.yaml +332 -0
- ciris_engine/ciris_templates/scout.yaml +338 -0
- ciris_engine/ciris_templates/test.yaml +168 -0
- ciris_engine/cli.py +42 -0
- ciris_engine/config/CIRIS_SERVICES.json +19 -0
- ciris_engine/config/MODEL_CAPABILITIES.json +419 -0
- ciris_engine/config/PRICING_DATA.json +179 -0
- ciris_engine/config/__init__.py +50 -0
- ciris_engine/config/ciris_services.py +113 -0
- ciris_engine/config/model_capabilities.py +388 -0
- ciris_engine/config/pricing_models.py +276 -0
- ciris_engine/constants.py +35 -0
- ciris_engine/data/__init__.py +1 -0
- ciris_engine/data/covenant_1.0b.txt +978 -0
- ciris_engine/gui_static/11steps.svg +107 -0
- ciris_engine/gui_static/2x-schematics.png +0 -0
- ciris_engine/gui_static/404/index.html +1 -0
- ciris_engine/gui_static/404.html +1 -0
- ciris_engine/gui_static/_next/static/0edhkwDxd5UccTsCmtaBi/_buildManifest.js +1 -0
- ciris_engine/gui_static/_next/static/0edhkwDxd5UccTsCmtaBi/_ssgManifest.js +1 -0
- ciris_engine/gui_static/_next/static/U-3xTQao7hc2wnAi-Uekm/_buildManifest.js +1 -0
- ciris_engine/gui_static/_next/static/U-3xTQao7hc2wnAi-Uekm/_ssgManifest.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/3297-60e86ba0f8a7b040.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/3835-2aad4b7f5f8e4643.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/4499-99a0bc47de0b8975.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/4534-af88cd4ba6e99bff.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/4541-84b455f9e0dc4cfe.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/4789-61412711484754bb.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/6539-c6398bc9d7018430.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/704-8e827b26cc8c2d32.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/704-fb45d630f3192c6f.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/8072-de4952a2e6d2b33f.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/8315-b91d03a3949db0af.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/8386-f93a83ccbd789bd9.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/87c73c54-781a7f35148d5433.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/8903-fefea3339a02d41b.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/9090-e66485adf8d9d990.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/_not-found/page-a67d9808462c23b1.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/api-keys/page-2d7ee1583bbbd02e.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/api-keys/page-6a3c2bae6fe92b7b.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/consent/page-2ed3a035136bc4e8.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/consent/page-b2f5c91844a32422.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/page-25b90f89af3ea58c.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/page-b65d16c94ecaf69c.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/privacy/page-675b6d05c8f9184f.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/privacy/page-cbee2e1c8ab52145.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/settings/page-0f44da06697cf9f0.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/account/settings/page-563420253577edbf.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/adapters/page-1854631018bc32be.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/agents/page-8353752c176a7c70.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/agents/page-f61a529f110a6040.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/api-demo/page-7f19b9d20d39be28.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/api-demo/page-d1063938f249b8bd.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/audit/page-321b6728b8fff0bb.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/audit/page-ebac35ca961a1277.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/billing/page-6f3dc3bd02924f8e.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/billing/page-fa4a469f814c821a.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/comms/page-0d4f734269addd8f.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/comms/page-79227d426050089c.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/config/page-018d21d683b6e5bc.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/config/page-2aa5a5363ca2a371.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/consent/page-198373205fd316e2.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/consent/page-f2ca39e7713b13f8.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/dashboard/page-1dd5a196f643c60d.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/dashboard/page-530a04d3abbb8cda.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/docs/page-3193b06d094ab654.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/docs/page-330e996dedb87aba.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/layout-0a70f5fc460298b1.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/layout-21f2f99dd5b336e9.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/login/page-33240e6c6034a49d.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/login/page-68ffab6d54a7fdcd.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/logs/page-8a6167aecc4a475c.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/memory/page-9ca8c5d0056de3ff.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/memory/page-e961226941c18f81.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/page-6fdb065a787a4974.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/page-89f87d431be6064a.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/runtime/page-2e728b9c43aa164d.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/runtime/page-c7dd033dc40a72f0.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/services/page-ae9f0bdf11d01a95.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/services/page-b10feb79ca5d75e5.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/sessions/page-13ebe7ef1c16ae11.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/sessions/page-e6c82b16d617f785.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/setup/page-0beb5f5b5a5c20fc.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/setup/page-2595e729eae30c0e.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/status-dashboard/page-1037c987aecc3653.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/status-dashboard/page-2ffd147f6d3162ff.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/system/page-2c5798d58cafcd91.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/system/page-505b1ba4eceb01c3.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-auth/page-b0cad31d5cb1b2fa.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-auth/page-f3ecd7a8012df230.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-login/page-f35117fdc4105801.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-login/page-fb583a7924114906.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-sdk/page-50f116fd76935563.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/test-sdk/page-c37d8aa5ba623a44.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/tools/page-429aec7a707777ef.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/tools/page-5f705aad60e0c04e.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/users/page-13476b8b0f3808cc.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/users/page-7e500d154ed5bba4.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/wa/page-cc4a9d8a5cb44d08.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/app/wa/page-ec3e429efbc79230.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/framework-9d29490f5ba089ba.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/main-1f554952e47a82c4.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/main-app-26fa8aed029082e5.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/main-app-97b0486ef6bcef25.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/pages/_app-6ce685456e616eb2.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/pages/_error-d4bce98d93fe21e7.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- ciris_engine/gui_static/_next/static/chunks/webpack-fcebd240b7f8477d.js +1 -0
- ciris_engine/gui_static/_next/static/css/16b94b1fe0cc6e37.css +3 -0
- ciris_engine/gui_static/_next/static/css/77a24ceaae86deff.css +3 -0
- ciris_engine/gui_static/_next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/747892c23ea88013-s.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/8d697b304b401681-s.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/9610d9e46709d722-s.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
- ciris_engine/gui_static/_next/static/media/d8298875641ec7d4-s.p.woff2 +0 -0
- ciris_engine/gui_static/account/api-keys/index.html +1 -0
- ciris_engine/gui_static/account/api-keys/index.txt +27 -0
- ciris_engine/gui_static/account/consent/index.html +1 -0
- ciris_engine/gui_static/account/consent/index.txt +27 -0
- ciris_engine/gui_static/account/index.html +1 -0
- ciris_engine/gui_static/account/index.txt +27 -0
- ciris_engine/gui_static/account/privacy/index.html +1 -0
- ciris_engine/gui_static/account/privacy/index.txt +27 -0
- ciris_engine/gui_static/account/settings/index.html +1 -0
- ciris_engine/gui_static/account/settings/index.txt +27 -0
- ciris_engine/gui_static/adapters/index.html +1 -0
- ciris_engine/gui_static/adapters/index.txt +27 -0
- ciris_engine/gui_static/agents/index.html +1 -0
- ciris_engine/gui_static/agents/index.txt +27 -0
- ciris_engine/gui_static/andrew-roberts-euBRXcx57T4-unsplash.jpg +0 -0
- ciris_engine/gui_static/api-demo/index.html +1 -0
- ciris_engine/gui_static/api-demo/index.txt +27 -0
- ciris_engine/gui_static/audit/index.html +1 -0
- ciris_engine/gui_static/audit/index.txt +27 -0
- ciris_engine/gui_static/billing/index.html +1 -0
- ciris_engine/gui_static/billing/index.txt +27 -0
- ciris_engine/gui_static/blurryinfo.png +0 -0
- ciris_engine/gui_static/chip-vincent-PkQDwfl9Flc-unsplash.jpg +0 -0
- ciris_engine/gui_static/ciris-architecture.svg +338 -0
- ciris_engine/gui_static/comms/index.html +1 -0
- ciris_engine/gui_static/comms/index.txt +27 -0
- ciris_engine/gui_static/config/index.html +1 -0
- ciris_engine/gui_static/config/index.txt +27 -0
- ciris_engine/gui_static/consent/index.html +1 -0
- ciris_engine/gui_static/consent/index.txt +27 -0
- ciris_engine/gui_static/dashboard/index.html +1 -0
- ciris_engine/gui_static/dashboard/index.txt +27 -0
- ciris_engine/gui_static/docs/index.html +1 -0
- ciris_engine/gui_static/docs/index.txt +27 -0
- ciris_engine/gui_static/eric.png +0 -0
- ciris_engine/gui_static/file.svg +1 -0
- ciris_engine/gui_static/globe.svg +1 -0
- ciris_engine/gui_static/index.html +1 -0
- ciris_engine/gui_static/index.txt +27 -0
- ciris_engine/gui_static/infogfx-1@2x.png +0 -0
- ciris_engine/gui_static/infogfx-2.png +0 -0
- ciris_engine/gui_static/infogfx-dark-1.png +0 -0
- ciris_engine/gui_static/kelly-vohs-soSTXmIxTDU-unsplash.jpg +0 -0
- ciris_engine/gui_static/login/index.html +1 -0
- ciris_engine/gui_static/login/index.txt +27 -0
- ciris_engine/gui_static/logs/index.html +1 -0
- ciris_engine/gui_static/logs/index.txt +27 -0
- ciris_engine/gui_static/memory/index.html +1 -0
- ciris_engine/gui_static/memory/index.txt +27 -0
- ciris_engine/gui_static/nathan-farrish-ArcTfEoBgzs-unsplash.jpg +0 -0
- ciris_engine/gui_static/next.svg +1 -0
- ciris_engine/gui_static/overview.svg +512 -0
- ciris_engine/gui_static/overview1.svg +407 -0
- ciris_engine/gui_static/overview2.svg +370 -0
- ciris_engine/gui_static/pipeline-visualization.svg +278 -0
- ciris_engine/gui_static/privacy-policy.html +160 -0
- ciris_engine/gui_static/runtime/index.html +8 -0
- ciris_engine/gui_static/runtime/index.txt +27 -0
- ciris_engine/gui_static/services/index.html +1 -0
- ciris_engine/gui_static/services/index.txt +27 -0
- ciris_engine/gui_static/sessions/index.html +1 -0
- ciris_engine/gui_static/sessions/index.txt +27 -0
- ciris_engine/gui_static/setup/index.html +1 -0
- ciris_engine/gui_static/setup/index.txt +27 -0
- ciris_engine/gui_static/status-dashboard/index.html +1 -0
- ciris_engine/gui_static/status-dashboard/index.txt +27 -0
- ciris_engine/gui_static/system/index.html +1 -0
- ciris_engine/gui_static/system/index.txt +27 -0
- ciris_engine/gui_static/terms-of-service.html +174 -0
- ciris_engine/gui_static/test-auth/index.html +1 -0
- ciris_engine/gui_static/test-auth/index.txt +27 -0
- ciris_engine/gui_static/test-login/index.html +1 -0
- ciris_engine/gui_static/test-login/index.txt +27 -0
- ciris_engine/gui_static/test-sdk/index.html +1 -0
- ciris_engine/gui_static/test-sdk/index.txt +27 -0
- ciris_engine/gui_static/tools/index.html +1 -0
- ciris_engine/gui_static/tools/index.txt +27 -0
- ciris_engine/gui_static/users/index.html +1 -0
- ciris_engine/gui_static/users/index.txt +27 -0
- ciris_engine/gui_static/vercel.svg +1 -0
- ciris_engine/gui_static/videos/video1.mp4 +0 -0
- ciris_engine/gui_static/videos/video3.mp4 +0 -0
- ciris_engine/gui_static/wa/index.html +1 -0
- ciris_engine/gui_static/wa/index.txt +27 -0
- ciris_engine/gui_static/window.svg +1 -0
- ciris_engine/logic/__init__.py +8 -0
- ciris_engine/logic/adapters/__init__.py +74 -0
- ciris_engine/logic/adapters/api/__init__.py +5 -0
- ciris_engine/logic/adapters/api/adapter.py +1037 -0
- ciris_engine/logic/adapters/api/api_communication.py +370 -0
- ciris_engine/logic/adapters/api/api_document.py +330 -0
- ciris_engine/logic/adapters/api/api_observer.py +24 -0
- ciris_engine/logic/adapters/api/api_runtime_control.py +388 -0
- ciris_engine/logic/adapters/api/api_tools.py +299 -0
- ciris_engine/logic/adapters/api/api_vision.py +215 -0
- ciris_engine/logic/adapters/api/app.py +272 -0
- ciris_engine/logic/adapters/api/auth.py +159 -0
- ciris_engine/logic/adapters/api/config.py +101 -0
- ciris_engine/logic/adapters/api/constants.py +55 -0
- ciris_engine/logic/adapters/api/dependencies/__init__.py +1 -0
- ciris_engine/logic/adapters/api/dependencies/auth.py +260 -0
- ciris_engine/logic/adapters/api/endpoints/__init__.py +1 -0
- ciris_engine/logic/adapters/api/endpoints/emergency.py +86 -0
- ciris_engine/logic/adapters/api/middleware/__init__.py +1 -0
- ciris_engine/logic/adapters/api/middleware/rate_limiter.py +302 -0
- ciris_engine/logic/adapters/api/models.py +29 -0
- ciris_engine/logic/adapters/api/routes/__init__.py +52 -0
- ciris_engine/logic/adapters/api/routes/agent.py +1762 -0
- ciris_engine/logic/adapters/api/routes/audit.py +707 -0
- ciris_engine/logic/adapters/api/routes/auth.py +1745 -0
- ciris_engine/logic/adapters/api/routes/billing.py +895 -0
- ciris_engine/logic/adapters/api/routes/config.py +329 -0
- ciris_engine/logic/adapters/api/routes/connectors.py +534 -0
- ciris_engine/logic/adapters/api/routes/consent.py +637 -0
- ciris_engine/logic/adapters/api/routes/dsar.py +637 -0
- ciris_engine/logic/adapters/api/routes/dsar_multi_source.py +484 -0
- ciris_engine/logic/adapters/api/routes/emergency.py +302 -0
- ciris_engine/logic/adapters/api/routes/memory.py +733 -0
- ciris_engine/logic/adapters/api/routes/memory_filters.py +230 -0
- ciris_engine/logic/adapters/api/routes/memory_models.py +112 -0
- ciris_engine/logic/adapters/api/routes/memory_queries.py +236 -0
- ciris_engine/logic/adapters/api/routes/memory_query_helpers.py +394 -0
- ciris_engine/logic/adapters/api/routes/memory_visualization.py +359 -0
- ciris_engine/logic/adapters/api/routes/memory_visualization_helpers.py +110 -0
- ciris_engine/logic/adapters/api/routes/partnership.py +541 -0
- ciris_engine/logic/adapters/api/routes/setup.py +1374 -0
- ciris_engine/logic/adapters/api/routes/system.py +3049 -0
- ciris_engine/logic/adapters/api/routes/system_extensions.py +952 -0
- ciris_engine/logic/adapters/api/routes/telemetry.py +1987 -0
- ciris_engine/logic/adapters/api/routes/telemetry_converters.py +141 -0
- ciris_engine/logic/adapters/api/routes/telemetry_helpers.py +111 -0
- ciris_engine/logic/adapters/api/routes/telemetry_logs_reader.py +280 -0
- ciris_engine/logic/adapters/api/routes/telemetry_metrics.py +131 -0
- ciris_engine/logic/adapters/api/routes/telemetry_models.py +190 -0
- ciris_engine/logic/adapters/api/routes/telemetry_otlp.py +878 -0
- ciris_engine/logic/adapters/api/routes/telemetry_resource_helpers.py +191 -0
- ciris_engine/logic/adapters/api/routes/tickets.py +541 -0
- ciris_engine/logic/adapters/api/routes/tools.py +556 -0
- ciris_engine/logic/adapters/api/routes/transparency.py +281 -0
- ciris_engine/logic/adapters/api/routes/users.py +981 -0
- ciris_engine/logic/adapters/api/routes/verification.py +373 -0
- ciris_engine/logic/adapters/api/routes/wa.py +369 -0
- ciris_engine/logic/adapters/api/service_configuration.py +177 -0
- ciris_engine/logic/adapters/api/services/__init__.py +1 -0
- ciris_engine/logic/adapters/api/services/auth_service.py +1417 -0
- ciris_engine/logic/adapters/api/services/oauth_security.py +68 -0
- ciris_engine/logic/adapters/base.py +141 -0
- ciris_engine/logic/adapters/base_adapter.py +73 -0
- ciris_engine/logic/adapters/base_observer.py +1141 -0
- ciris_engine/logic/adapters/base_vision.py +312 -0
- ciris_engine/logic/adapters/cirisnode_client.py +307 -0
- ciris_engine/logic/adapters/cli/__init__.py +3 -0
- ciris_engine/logic/adapters/cli/adapter.py +207 -0
- ciris_engine/logic/adapters/cli/cli_adapter.py +902 -0
- ciris_engine/logic/adapters/cli/cli_observer.py +268 -0
- ciris_engine/logic/adapters/cli/cli_tools.py +427 -0
- ciris_engine/logic/adapters/cli/cli_wa_service.py +134 -0
- ciris_engine/logic/adapters/cli/config.py +73 -0
- ciris_engine/logic/adapters/discord/__init__.py +3 -0
- ciris_engine/logic/adapters/discord/adapter.py +783 -0
- ciris_engine/logic/adapters/discord/ciris_discord_client.py +159 -0
- ciris_engine/logic/adapters/discord/config.py +177 -0
- ciris_engine/logic/adapters/discord/constants.py +185 -0
- ciris_engine/logic/adapters/discord/discord-stubs.pyi +50 -0
- ciris_engine/logic/adapters/discord/discord_adapter.py +1584 -0
- ciris_engine/logic/adapters/discord/discord_audit.py +150 -0
- ciris_engine/logic/adapters/discord/discord_channel_manager.py +351 -0
- ciris_engine/logic/adapters/discord/discord_connection_manager.py +313 -0
- ciris_engine/logic/adapters/discord/discord_embed_formatter.py +369 -0
- ciris_engine/logic/adapters/discord/discord_error_classifier.py +302 -0
- ciris_engine/logic/adapters/discord/discord_error_handler.py +316 -0
- ciris_engine/logic/adapters/discord/discord_guidance_handler.py +460 -0
- ciris_engine/logic/adapters/discord/discord_message_handler.py +207 -0
- ciris_engine/logic/adapters/discord/discord_observer.py +670 -0
- ciris_engine/logic/adapters/discord/discord_rate_limiter.py +249 -0
- ciris_engine/logic/adapters/discord/discord_reaction_handler.py +278 -0
- ciris_engine/logic/adapters/discord/discord_tool_handler.py +465 -0
- ciris_engine/logic/adapters/discord/discord_tool_service.py +790 -0
- ciris_engine/logic/adapters/discord/discord_tools.py +90 -0
- ciris_engine/logic/adapters/discord/discord_vision_helper.py +148 -0
- ciris_engine/logic/adapters/discord/py.typed +0 -0
- ciris_engine/logic/adapters/document_parser.py +320 -0
- ciris_engine/logic/audit/__init__.py +10 -0
- ciris_engine/logic/audit/hash_chain.py +313 -0
- ciris_engine/logic/audit/signature_manager.py +352 -0
- ciris_engine/logic/audit/verifier.py +408 -0
- ciris_engine/logic/buses/__init__.py +21 -0
- ciris_engine/logic/buses/base_bus.py +178 -0
- ciris_engine/logic/buses/bus_manager.py +121 -0
- ciris_engine/logic/buses/communication_bus.py +387 -0
- ciris_engine/logic/buses/llm_bus.py +722 -0
- ciris_engine/logic/buses/memory_bus.py +577 -0
- ciris_engine/logic/buses/prohibitions.py +502 -0
- ciris_engine/logic/buses/runtime_control_bus.py +539 -0
- ciris_engine/logic/buses/tool_bus.py +482 -0
- ciris_engine/logic/buses/wise_bus.py +684 -0
- ciris_engine/logic/config/__init__.py +25 -0
- ciris_engine/logic/config/bootstrap.py +255 -0
- ciris_engine/logic/config/config_accessor.py +202 -0
- ciris_engine/logic/config/db_paths.py +194 -0
- ciris_engine/logic/config/env_utils.py +39 -0
- ciris_engine/logic/conscience/__init__.py +16 -0
- ciris_engine/logic/conscience/build_deferral_package.py +0 -0
- ciris_engine/logic/conscience/core.py +688 -0
- ciris_engine/logic/conscience/interface.py +33 -0
- ciris_engine/logic/conscience/registry.py +76 -0
- ciris_engine/logic/conscience/thought_depth_guardrail.py +231 -0
- ciris_engine/logic/conscience/updated_status_conscience.py +156 -0
- ciris_engine/logic/context/__init__.py +10 -0
- ciris_engine/logic/context/batch_context.py +550 -0
- ciris_engine/logic/context/builder.py +149 -0
- ciris_engine/logic/context/channel_resolution.py +136 -0
- ciris_engine/logic/context/secrets_snapshot.py +52 -0
- ciris_engine/logic/context/system_snapshot.py +116 -0
- ciris_engine/logic/context/system_snapshot_helpers.py +1651 -0
- ciris_engine/logic/covenant/__init__.py +33 -0
- ciris_engine/logic/covenant/executor.py +303 -0
- ciris_engine/logic/covenant/extractor.py +382 -0
- ciris_engine/logic/covenant/handler.py +241 -0
- ciris_engine/logic/covenant/verifier.py +383 -0
- ciris_engine/logic/dma/__init__.py +15 -0
- ciris_engine/logic/dma/action_selection/__init__.py +11 -0
- ciris_engine/logic/dma/action_selection/action_instruction_generator.py +444 -0
- ciris_engine/logic/dma/action_selection/context_builder.py +508 -0
- ciris_engine/logic/dma/action_selection/faculty_integration.py +193 -0
- ciris_engine/logic/dma/action_selection/special_cases.py +132 -0
- ciris_engine/logic/dma/action_selection_pdma.py +365 -0
- ciris_engine/logic/dma/base_dma.py +335 -0
- ciris_engine/logic/dma/csdma.py +239 -0
- ciris_engine/logic/dma/dma_executor.py +575 -0
- ciris_engine/logic/dma/dsdma_base.py +410 -0
- ciris_engine/logic/dma/exceptions.py +4 -0
- ciris_engine/logic/dma/factory.py +150 -0
- ciris_engine/logic/dma/pdma.py +120 -0
- ciris_engine/logic/dma/prompt_loader.py +189 -0
- ciris_engine/logic/dma/prompts/action_selection_pdma.yml +58 -0
- ciris_engine/logic/dma/prompts/csdma_common_sense.yml +28 -0
- ciris_engine/logic/dma/prompts/dsdma_base.yml +17 -0
- ciris_engine/logic/dma/prompts/pdma_ethical.yml +42 -0
- ciris_engine/logic/formatters/__init__.py +26 -0
- ciris_engine/logic/formatters/crisis_resources.py +80 -0
- ciris_engine/logic/formatters/escalation.py +21 -0
- ciris_engine/logic/formatters/identity.py +224 -0
- ciris_engine/logic/formatters/prompt_blocks.py +64 -0
- ciris_engine/logic/formatters/system_snapshot.py +193 -0
- ciris_engine/logic/formatters/user_profiles.py +108 -0
- ciris_engine/logic/handlers/__init__.py +1 -0
- ciris_engine/logic/handlers/control/__init__.py +1 -0
- ciris_engine/logic/handlers/control/defer_handler.py +195 -0
- ciris_engine/logic/handlers/control/ponder_handler.py +154 -0
- ciris_engine/logic/handlers/control/reject_handler.py +81 -0
- ciris_engine/logic/handlers/external/__init__.py +1 -0
- ciris_engine/logic/handlers/external/observe_handler.py +154 -0
- ciris_engine/logic/handlers/external/speak_handler.py +250 -0
- ciris_engine/logic/handlers/external/tool_handler.py +148 -0
- ciris_engine/logic/handlers/memory/__init__.py +1 -0
- ciris_engine/logic/handlers/memory/forget_handler.py +107 -0
- ciris_engine/logic/handlers/memory/memorize_handler.py +391 -0
- ciris_engine/logic/handlers/memory/recall_handler.py +213 -0
- ciris_engine/logic/handlers/terminal/__init__.py +1 -0
- ciris_engine/logic/handlers/terminal/task_complete_handler.py +299 -0
- ciris_engine/logic/infrastructure/__init__.py +1 -0
- ciris_engine/logic/infrastructure/handlers/__init__.py +8 -0
- ciris_engine/logic/infrastructure/handlers/action_dispatcher.py +382 -0
- ciris_engine/logic/infrastructure/handlers/base_handler.py +450 -0
- ciris_engine/logic/infrastructure/handlers/exceptions.py +2 -0
- ciris_engine/logic/infrastructure/handlers/handler_registry.py +59 -0
- ciris_engine/logic/infrastructure/handlers/helpers.py +55 -0
- ciris_engine/logic/infrastructure/step_streaming.py +149 -0
- ciris_engine/logic/infrastructure/sub_services/__init__.py +1 -0
- ciris_engine/logic/infrastructure/sub_services/identity_variance_monitor.py +1035 -0
- ciris_engine/logic/infrastructure/sub_services/pattern_analysis_loop.py +758 -0
- ciris_engine/logic/infrastructure/sub_services/wa_cli_bootstrap.py +229 -0
- ciris_engine/logic/infrastructure/sub_services/wa_cli_display.py +176 -0
- ciris_engine/logic/infrastructure/sub_services/wa_cli_oauth.py +404 -0
- ciris_engine/logic/infrastructure/sub_services/wa_cli_wizard.py +181 -0
- ciris_engine/logic/persistence/__init__.py +130 -0
- ciris_engine/logic/persistence/analytics.py +97 -0
- ciris_engine/logic/persistence/db/__init__.py +28 -0
- ciris_engine/logic/persistence/db/core.py +520 -0
- ciris_engine/logic/persistence/db/dialect.py +380 -0
- ciris_engine/logic/persistence/db/execution_helpers.py +216 -0
- ciris_engine/logic/persistence/db/migration_runner.py +191 -0
- ciris_engine/logic/persistence/db/operations.py +313 -0
- ciris_engine/logic/persistence/db/query_builder.py +232 -0
- ciris_engine/logic/persistence/db/retry.py +154 -0
- ciris_engine/logic/persistence/db/setup.py +18 -0
- ciris_engine/logic/persistence/migrations/postgres/001_initial_schema.sql +4 -0
- ciris_engine/logic/persistence/migrations/postgres/002_add_retry_status.sql +3 -0
- ciris_engine/logic/persistence/migrations/postgres/003_add_task_update_tracking.sql +8 -0
- ciris_engine/logic/persistence/migrations/postgres/004_add_occurrence_id.sql +54 -0
- ciris_engine/logic/persistence/migrations/postgres/005_add_consolidation_locks.sql +22 -0
- ciris_engine/logic/persistence/migrations/postgres/006_add_correlation_id_unique_index.sql +16 -0
- ciris_engine/logic/persistence/migrations/postgres/007_add_dsar_tickets.sql +39 -0
- ciris_engine/logic/persistence/migrations/postgres/008_rename_to_tickets_add_sop.sql +123 -0
- ciris_engine/logic/persistence/migrations/postgres/009_add_ticket_status_columns.sql +39 -0
- ciris_engine/logic/persistence/migrations/postgres/010_add_images_to_tasks.sql +5 -0
- ciris_engine/logic/persistence/migrations/sqlite/001_initial_schema.sql +357 -0
- ciris_engine/logic/persistence/migrations/sqlite/002_add_retry_status.sql +3 -0
- ciris_engine/logic/persistence/migrations/sqlite/003_add_task_update_tracking.sql +8 -0
- ciris_engine/logic/persistence/migrations/sqlite/004_add_occurrence_id.sql +45 -0
- ciris_engine/logic/persistence/migrations/sqlite/005_add_consolidation_locks.sql +22 -0
- ciris_engine/logic/persistence/migrations/sqlite/006_add_correlation_id_unique_index.sql +16 -0
- ciris_engine/logic/persistence/migrations/sqlite/007_add_dsar_tickets.sql +39 -0
- ciris_engine/logic/persistence/migrations/sqlite/008_rename_to_tickets_add_sop.sql +120 -0
- ciris_engine/logic/persistence/migrations/sqlite/009_add_ticket_status_columns.sql +129 -0
- ciris_engine/logic/persistence/migrations/sqlite/010_add_images_to_tasks.sql +17 -0
- ciris_engine/logic/persistence/models/__init__.py +141 -0
- ciris_engine/logic/persistence/models/correlations.py +881 -0
- ciris_engine/logic/persistence/models/deferral.py +68 -0
- ciris_engine/logic/persistence/models/dsar.py +286 -0
- ciris_engine/logic/persistence/models/graph.py +362 -0
- ciris_engine/logic/persistence/models/identity.py +264 -0
- ciris_engine/logic/persistence/models/queue_status.py +139 -0
- ciris_engine/logic/persistence/models/tasks.py +1043 -0
- ciris_engine/logic/persistence/models/thoughts.py +400 -0
- ciris_engine/logic/persistence/models/tickets.py +518 -0
- ciris_engine/logic/persistence/stores/__init__.py +13 -0
- ciris_engine/logic/persistence/stores/auth_helpers.py +117 -0
- ciris_engine/logic/persistence/stores/authentication_store.py +414 -0
- ciris_engine/logic/persistence/utils.py +212 -0
- ciris_engine/logic/processors/__init__.py +30 -0
- ciris_engine/logic/processors/core/__init__.py +1 -0
- ciris_engine/logic/processors/core/base_processor.py +280 -0
- ciris_engine/logic/processors/core/main_processor.py +1777 -0
- ciris_engine/logic/processors/core/step_decorators.py +1583 -0
- ciris_engine/logic/processors/core/thought_processor/__init__.py +20 -0
- ciris_engine/logic/processors/core/thought_processor/action_execution.py +49 -0
- ciris_engine/logic/processors/core/thought_processor/conscience_execution.py +382 -0
- ciris_engine/logic/processors/core/thought_processor/finalize_action.py +66 -0
- ciris_engine/logic/processors/core/thought_processor/gather_context.py +120 -0
- ciris_engine/logic/processors/core/thought_processor/main.py +920 -0
- ciris_engine/logic/processors/core/thought_processor/perform_aspdma.py +86 -0
- ciris_engine/logic/processors/core/thought_processor/perform_dmas.py +106 -0
- ciris_engine/logic/processors/core/thought_processor/recursive_processing.py +237 -0
- ciris_engine/logic/processors/core/thought_processor/round_complete.py +52 -0
- ciris_engine/logic/processors/core/thought_processor/start_round.py +64 -0
- ciris_engine/logic/processors/exceptions.py +59 -0
- ciris_engine/logic/processors/states/__init__.py +1 -0
- ciris_engine/logic/processors/states/dream_processor.py +1381 -0
- ciris_engine/logic/processors/states/play_processor.py +141 -0
- ciris_engine/logic/processors/states/shutdown_processor.py +623 -0
- ciris_engine/logic/processors/states/solitude_processor.py +305 -0
- ciris_engine/logic/processors/states/wakeup_processor.py +802 -0
- ciris_engine/logic/processors/states/work_processor.py +742 -0
- ciris_engine/logic/processors/support/__init__.py +1 -0
- ciris_engine/logic/processors/support/dma_orchestrator.py +336 -0
- ciris_engine/logic/processors/support/processing_queue.py +133 -0
- ciris_engine/logic/processors/support/shutdown_condition_evaluator.py +294 -0
- ciris_engine/logic/processors/support/state_manager.py +358 -0
- ciris_engine/logic/processors/support/task_manager.py +303 -0
- ciris_engine/logic/processors/support/thought_escalation.py +116 -0
- ciris_engine/logic/processors/support/thought_manager.py +328 -0
- ciris_engine/logic/processors/support/thought_manager_enhanced.py +105 -0
- ciris_engine/logic/registries/__init__.py +34 -0
- ciris_engine/logic/registries/base.py +653 -0
- ciris_engine/logic/registries/circuit_breaker.py +275 -0
- ciris_engine/logic/registries/typed_registries.py +184 -0
- ciris_engine/logic/runtime/__init__.py +7 -0
- ciris_engine/logic/runtime/adapter_loader.py +261 -0
- ciris_engine/logic/runtime/adapter_manager.py +1053 -0
- ciris_engine/logic/runtime/ciris_runtime.py +2342 -0
- ciris_engine/logic/runtime/ciris_runtime_helpers.py +923 -0
- ciris_engine/logic/runtime/component_builder.py +361 -0
- ciris_engine/logic/runtime/identity_manager.py +219 -0
- ciris_engine/logic/runtime/module_loader.py +207 -0
- ciris_engine/logic/runtime/prevent_sideeffects.py +30 -0
- ciris_engine/logic/runtime/runtime_interface.py +23 -0
- ciris_engine/logic/runtime/service_initializer.py +1623 -0
- ciris_engine/logic/secrets/__init__.py +30 -0
- ciris_engine/logic/secrets/encryption.py +175 -0
- ciris_engine/logic/secrets/filter.py +295 -0
- ciris_engine/logic/secrets/service.py +652 -0
- ciris_engine/logic/secrets/store.py +669 -0
- ciris_engine/logic/services/__init__.py +1 -0
- ciris_engine/logic/services/adaptation/__init__.py +3 -0
- ciris_engine/logic/services/base_graph_service.py +142 -0
- ciris_engine/logic/services/base_infrastructure_service.py +69 -0
- ciris_engine/logic/services/base_scheduled_service.py +136 -0
- ciris_engine/logic/services/base_service.py +247 -0
- ciris_engine/logic/services/governance/__init__.py +3 -0
- ciris_engine/logic/services/governance/adaptive_filter/__init__.py +14 -0
- ciris_engine/logic/services/governance/adaptive_filter/service.py +818 -0
- ciris_engine/logic/services/governance/consent/__init__.py +53 -0
- ciris_engine/logic/services/governance/consent/air.py +403 -0
- ciris_engine/logic/services/governance/consent/decay.py +324 -0
- ciris_engine/logic/services/governance/consent/dsar_automation.py +589 -0
- ciris_engine/logic/services/governance/consent/exceptions.py +106 -0
- ciris_engine/logic/services/governance/consent/metrics.py +270 -0
- ciris_engine/logic/services/governance/consent/partnership.py +533 -0
- ciris_engine/logic/services/governance/consent/service.py +1256 -0
- ciris_engine/logic/services/governance/dsar/__init__.py +29 -0
- ciris_engine/logic/services/governance/dsar/orchestrator.py +977 -0
- ciris_engine/logic/services/governance/dsar/schemas.py +141 -0
- ciris_engine/logic/services/governance/dsar/signature_service.py +283 -0
- ciris_engine/logic/services/governance/self_observation/__init__.py +20 -0
- ciris_engine/logic/services/governance/self_observation/service.py +1153 -0
- ciris_engine/logic/services/governance/visibility/__init__.py +17 -0
- ciris_engine/logic/services/governance/visibility/service.py +512 -0
- ciris_engine/logic/services/governance/wise_authority/__init__.py +15 -0
- ciris_engine/logic/services/governance/wise_authority/service.py +827 -0
- ciris_engine/logic/services/graph/__init__.py +5 -0
- ciris_engine/logic/services/graph/audit_service/__init__.py +5 -0
- ciris_engine/logic/services/graph/audit_service/service.py +1675 -0
- ciris_engine/logic/services/graph/base.py +208 -0
- ciris_engine/logic/services/graph/config_service/__init__.py +5 -0
- ciris_engine/logic/services/graph/config_service/service.py +372 -0
- ciris_engine/logic/services/graph/incident_service/__init__.py +5 -0
- ciris_engine/logic/services/graph/incident_service/service.py +803 -0
- ciris_engine/logic/services/graph/memory_service.py +1120 -0
- ciris_engine/logic/services/graph/telemetry_service/__init__.py +5 -0
- ciris_engine/logic/services/graph/telemetry_service/exceptions.py +104 -0
- ciris_engine/logic/services/graph/telemetry_service/helpers.py +1337 -0
- ciris_engine/logic/services/graph/telemetry_service/service.py +2429 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/__init__.py +17 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/aggregation_helpers.py +355 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/cleanup_helpers.py +438 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/compressor.py +260 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/__init__.py +27 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/audit.py +326 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/conversation.py +291 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/memory.py +197 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/metrics.py +251 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/task.py +257 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/consolidators/trace.py +363 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/data_converter.py +545 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/date_calculation_helpers.py +193 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/db_query_helpers.py +296 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/edge_helpers.py +92 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/edge_manager.py +896 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/extensive_helpers.py +322 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/period_manager.py +152 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/profound_helpers.py +277 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/query_manager.py +812 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/service.py +1692 -0
- ciris_engine/logic/services/graph/tsdb_consolidation/sql_builders.py +363 -0
- ciris_engine/logic/services/infrastructure/__init__.py +1 -0
- ciris_engine/logic/services/infrastructure/authentication/__init__.py +5 -0
- ciris_engine/logic/services/infrastructure/authentication/service.py +1634 -0
- ciris_engine/logic/services/infrastructure/database_maintenance/__init__.py +15 -0
- ciris_engine/logic/services/infrastructure/database_maintenance/service.py +764 -0
- ciris_engine/logic/services/infrastructure/resource_monitor/__init__.py +7 -0
- ciris_engine/logic/services/infrastructure/resource_monitor/ciris_billing_provider.py +755 -0
- ciris_engine/logic/services/infrastructure/resource_monitor/service.py +409 -0
- ciris_engine/logic/services/infrastructure/resource_monitor/simple_credit_provider.py +129 -0
- ciris_engine/logic/services/lifecycle/__init__.py +3 -0
- ciris_engine/logic/services/lifecycle/initialization/__init__.py +10 -0
- ciris_engine/logic/services/lifecycle/initialization/service.py +312 -0
- ciris_engine/logic/services/lifecycle/scheduler/__init__.py +5 -0
- ciris_engine/logic/services/lifecycle/scheduler/service.py +607 -0
- ciris_engine/logic/services/lifecycle/shutdown/__init__.py +9 -0
- ciris_engine/logic/services/lifecycle/shutdown/service.py +378 -0
- ciris_engine/logic/services/lifecycle/time/__init__.py +15 -0
- ciris_engine/logic/services/lifecycle/time/service.py +259 -0
- ciris_engine/logic/services/memory_service/__init__.py +8 -0
- ciris_engine/logic/services/mixins/__init__.py +13 -0
- ciris_engine/logic/services/mixins/example_usage.py +200 -0
- ciris_engine/logic/services/mixins/request_metrics.py +179 -0
- ciris_engine/logic/services/runtime/__init__.py +3 -0
- ciris_engine/logic/services/runtime/adapter_configuration/__init__.py +16 -0
- ciris_engine/logic/services/runtime/adapter_configuration/service.py +674 -0
- ciris_engine/logic/services/runtime/adapter_configuration/session.py +67 -0
- ciris_engine/logic/services/runtime/control_service/__init__.py +5 -0
- ciris_engine/logic/services/runtime/control_service/service.py +2269 -0
- ciris_engine/logic/services/runtime/llm_service/__init__.py +14 -0
- ciris_engine/logic/services/runtime/llm_service/pricing_calculator.py +279 -0
- ciris_engine/logic/services/runtime/llm_service/service.py +930 -0
- ciris_engine/logic/services/tools/__init__.py +5 -0
- ciris_engine/logic/services/tools/core_tool_service/__init__.py +8 -0
- ciris_engine/logic/services/tools/core_tool_service/service.py +852 -0
- ciris_engine/logic/setup/__init__.py +1 -0
- ciris_engine/logic/setup/first_run.py +250 -0
- ciris_engine/logic/setup/wizard.py +327 -0
- ciris_engine/logic/telemetry/__init__.py +46 -0
- ciris_engine/logic/telemetry/core.py +239 -0
- ciris_engine/logic/telemetry/hot_cold_config.py +133 -0
- ciris_engine/logic/telemetry/log_collector.py +190 -0
- ciris_engine/logic/telemetry/resource_monitor.py +7 -0
- ciris_engine/logic/telemetry/security.py +79 -0
- ciris_engine/logic/utils/__init__.py +18 -0
- ciris_engine/logic/utils/channel_utils.py +75 -0
- ciris_engine/logic/utils/consent/__init__.py +1 -0
- ciris_engine/logic/utils/consent/partnership_utils.py +172 -0
- ciris_engine/logic/utils/constants.py +92 -0
- ciris_engine/logic/utils/context_utils.py +145 -0
- ciris_engine/logic/utils/directory_setup.py +533 -0
- ciris_engine/logic/utils/graphql_context_provider.py +152 -0
- ciris_engine/logic/utils/identity_resolution.py +843 -0
- ciris_engine/logic/utils/incident_capture_handler.py +303 -0
- ciris_engine/logic/utils/initialization_manager.py +74 -0
- ciris_engine/logic/utils/jsondict_helpers.py +290 -0
- ciris_engine/logic/utils/log_sanitizer.py +97 -0
- ciris_engine/logic/utils/logging_config.py +151 -0
- ciris_engine/logic/utils/observability_decorators.py +544 -0
- ciris_engine/logic/utils/occurrence_utils.py +155 -0
- ciris_engine/logic/utils/path_resolution.py +281 -0
- ciris_engine/logic/utils/platform_detection.py +286 -0
- ciris_engine/logic/utils/privacy.py +266 -0
- ciris_engine/logic/utils/profile_loader.py +124 -0
- ciris_engine/logic/utils/profile_manager.py +16 -0
- ciris_engine/logic/utils/runtime_utils.py +69 -0
- ciris_engine/logic/utils/shutdown_manager.py +107 -0
- ciris_engine/logic/utils/task_formatters.py +60 -0
- ciris_engine/logic/utils/task_thought_factory.py +404 -0
- ciris_engine/logic/utils/thought_utils.py +54 -0
- ciris_engine/logic/utils/user_utils.py +70 -0
- ciris_engine/protocols/__init__.py +0 -0
- ciris_engine/protocols/adapters/__init__.py +35 -0
- ciris_engine/protocols/adapters/base.py +149 -0
- ciris_engine/protocols/adapters/configurable.py +265 -0
- ciris_engine/protocols/adapters/message.py +90 -0
- ciris_engine/protocols/audit/__init__.py +1 -0
- ciris_engine/protocols/buses/__init__.py +1 -0
- ciris_engine/protocols/config/__init__.py +1 -0
- ciris_engine/protocols/conscience/__init__.py +1 -0
- ciris_engine/protocols/consent.py +88 -0
- ciris_engine/protocols/context/__init__.py +1 -0
- ciris_engine/protocols/data/__init__.py +1 -0
- ciris_engine/protocols/dma/__init__.py +1 -0
- ciris_engine/protocols/dma/base.py +107 -0
- ciris_engine/protocols/faculties.py +34 -0
- ciris_engine/protocols/formatters/__init__.py +1 -0
- ciris_engine/protocols/handlers/__init__.py +1 -0
- ciris_engine/protocols/infrastructure/__init__.py +25 -0
- ciris_engine/protocols/infrastructure/base.py +377 -0
- ciris_engine/protocols/persistence/__init__.py +1 -0
- ciris_engine/protocols/pipeline_control.py +609 -0
- ciris_engine/protocols/processors/__init__.py +19 -0
- ciris_engine/protocols/processors/agent.py +299 -0
- ciris_engine/protocols/processors/base.py +130 -0
- ciris_engine/protocols/processors/orchestration.py +62 -0
- ciris_engine/protocols/registries/__init__.py +1 -0
- ciris_engine/protocols/runtime/__init__.py +1 -0
- ciris_engine/protocols/runtime/base.py +163 -0
- ciris_engine/protocols/secrets/__init__.py +1 -0
- ciris_engine/protocols/services/__init__.py +80 -0
- ciris_engine/protocols/services/adaptation/__init__.py +7 -0
- ciris_engine/protocols/services/adaptation/self_observation.py +265 -0
- ciris_engine/protocols/services/governance/__init__.py +20 -0
- ciris_engine/protocols/services/governance/communication.py +58 -0
- ciris_engine/protocols/services/governance/filter.py +56 -0
- ciris_engine/protocols/services/governance/visibility.py +32 -0
- ciris_engine/protocols/services/governance/wa_auth.py +192 -0
- ciris_engine/protocols/services/governance/wise_authority.py +75 -0
- ciris_engine/protocols/services/graph/__init__.py +19 -0
- ciris_engine/protocols/services/graph/audit.py +92 -0
- ciris_engine/protocols/services/graph/config.py +54 -0
- ciris_engine/protocols/services/graph/incident_management.py +103 -0
- ciris_engine/protocols/services/graph/memory.py +110 -0
- ciris_engine/protocols/services/graph/telemetry.py +51 -0
- ciris_engine/protocols/services/graph/tsdb_consolidation.py +87 -0
- ciris_engine/protocols/services/infrastructure/__init__.py +11 -0
- ciris_engine/protocols/services/infrastructure/authentication.py +159 -0
- ciris_engine/protocols/services/infrastructure/credit_gate.py +46 -0
- ciris_engine/protocols/services/infrastructure/database_maintenance.py +25 -0
- ciris_engine/protocols/services/infrastructure/resource_monitor.py +83 -0
- ciris_engine/protocols/services/lifecycle/__init__.py +13 -0
- ciris_engine/protocols/services/lifecycle/initialization.py +41 -0
- ciris_engine/protocols/services/lifecycle/scheduler.py +42 -0
- ciris_engine/protocols/services/lifecycle/shutdown.py +50 -0
- ciris_engine/protocols/services/lifecycle/time.py +31 -0
- ciris_engine/protocols/services/runtime/__init__.py +13 -0
- ciris_engine/protocols/services/runtime/llm.py +50 -0
- ciris_engine/protocols/services/runtime/runtime_control.py +193 -0
- ciris_engine/protocols/services/runtime/secrets.py +100 -0
- ciris_engine/protocols/services/runtime/tool.py +123 -0
- ciris_engine/protocols/telemetry/__init__.py +1 -0
- ciris_engine/protocols/utils/__init__.py +1 -0
- ciris_engine/schemas/__init__.py +112 -0
- ciris_engine/schemas/actions/__init__.py +37 -0
- ciris_engine/schemas/actions/parameters.py +137 -0
- ciris_engine/schemas/adapters/__init__.py +13 -0
- ciris_engine/schemas/adapters/cirisnode.py +135 -0
- ciris_engine/schemas/adapters/cli.py +97 -0
- ciris_engine/schemas/adapters/cli_tools.py +98 -0
- ciris_engine/schemas/adapters/discord.py +125 -0
- ciris_engine/schemas/adapters/graphql_core.py +144 -0
- ciris_engine/schemas/adapters/registration.py +47 -0
- ciris_engine/schemas/adapters/runtime_context.py +48 -0
- ciris_engine/schemas/adapters/tool_execution.py +45 -0
- ciris_engine/schemas/adapters/tools.py +96 -0
- ciris_engine/schemas/api/__init__.py +1 -0
- ciris_engine/schemas/api/agent.py +50 -0
- ciris_engine/schemas/api/audit.py +38 -0
- ciris_engine/schemas/api/auth.py +351 -0
- ciris_engine/schemas/api/config_security.py +242 -0
- ciris_engine/schemas/api/emergency.py +111 -0
- ciris_engine/schemas/api/responses.py +72 -0
- ciris_engine/schemas/api/runtime.py +26 -0
- ciris_engine/schemas/api/telemetry.py +109 -0
- ciris_engine/schemas/api/wa.py +90 -0
- ciris_engine/schemas/audit/__init__.py +13 -0
- ciris_engine/schemas/audit/core.py +139 -0
- ciris_engine/schemas/audit/hash_chain.py +58 -0
- ciris_engine/schemas/audit/verification.py +131 -0
- ciris_engine/schemas/buses/__init__.py +1 -0
- ciris_engine/schemas/config/__init__.py +41 -0
- ciris_engine/schemas/config/agent.py +279 -0
- ciris_engine/schemas/config/cognitive_state_behaviors.py +194 -0
- ciris_engine/schemas/config/default_dsar_sops.py +178 -0
- ciris_engine/schemas/config/essential.py +195 -0
- ciris_engine/schemas/config/tickets.py +86 -0
- ciris_engine/schemas/conscience/__init__.py +25 -0
- ciris_engine/schemas/conscience/context.py +34 -0
- ciris_engine/schemas/conscience/core.py +145 -0
- ciris_engine/schemas/conscience/results.py +24 -0
- ciris_engine/schemas/consent/__init__.py +5 -0
- ciris_engine/schemas/consent/core.py +404 -0
- ciris_engine/schemas/context/__init__.py +1 -0
- ciris_engine/schemas/covenant.py +382 -0
- ciris_engine/schemas/data/__init__.py +1 -0
- ciris_engine/schemas/dma/__init__.py +16 -0
- ciris_engine/schemas/dma/core.py +199 -0
- ciris_engine/schemas/dma/faculty.py +192 -0
- ciris_engine/schemas/dma/prompts.py +172 -0
- ciris_engine/schemas/dma/results.py +103 -0
- ciris_engine/schemas/formatters/__init__.py +1 -0
- ciris_engine/schemas/handlers/__init__.py +10 -0
- ciris_engine/schemas/handlers/context.py +119 -0
- ciris_engine/schemas/handlers/contexts.py +100 -0
- ciris_engine/schemas/handlers/core.py +167 -0
- ciris_engine/schemas/handlers/memory_schemas.py +67 -0
- ciris_engine/schemas/handlers/schemas.py +95 -0
- ciris_engine/schemas/identity.py +149 -0
- ciris_engine/schemas/infrastructure/__init__.py +1 -0
- ciris_engine/schemas/infrastructure/base.py +256 -0
- ciris_engine/schemas/infrastructure/behavioral_patterns.py +129 -0
- ciris_engine/schemas/infrastructure/feedback_loop.py +57 -0
- ciris_engine/schemas/infrastructure/identity_variance.py +141 -0
- ciris_engine/schemas/infrastructure/oauth.py +175 -0
- ciris_engine/schemas/infrastructure/wa_cli_wizard.py +54 -0
- ciris_engine/schemas/persistence/__init__.py +34 -0
- ciris_engine/schemas/persistence/core.py +140 -0
- ciris_engine/schemas/persistence/correlations.py +73 -0
- ciris_engine/schemas/persistence/postgres/__init__.py +1 -0
- ciris_engine/schemas/persistence/postgres/tables.py +280 -0
- ciris_engine/schemas/persistence/sqlite/__init__.py +1 -0
- ciris_engine/schemas/persistence/sqlite/tables.py +281 -0
- ciris_engine/schemas/platform.py +149 -0
- ciris_engine/schemas/processors/__init__.py +26 -0
- ciris_engine/schemas/processors/base.py +130 -0
- ciris_engine/schemas/processors/cognitive.py +77 -0
- ciris_engine/schemas/processors/context.py +35 -0
- ciris_engine/schemas/processors/core.py +152 -0
- ciris_engine/schemas/processors/dma.py +105 -0
- ciris_engine/schemas/processors/error.py +122 -0
- ciris_engine/schemas/processors/main.py +109 -0
- ciris_engine/schemas/processors/phase_results.py +21 -0
- ciris_engine/schemas/processors/results.py +99 -0
- ciris_engine/schemas/processors/solitude.py +79 -0
- ciris_engine/schemas/processors/state.py +202 -0
- ciris_engine/schemas/processors/state_example.py +177 -0
- ciris_engine/schemas/processors/states.py +21 -0
- ciris_engine/schemas/processors/status.py +34 -0
- ciris_engine/schemas/registries/__init__.py +1 -0
- ciris_engine/schemas/registries/base.py +66 -0
- ciris_engine/schemas/resources/__init__.py +15 -0
- ciris_engine/schemas/resources/crisis.py +315 -0
- ciris_engine/schemas/runtime/__init__.py +42 -0
- ciris_engine/schemas/runtime/adapter_management.py +186 -0
- ciris_engine/schemas/runtime/api.py +58 -0
- ciris_engine/schemas/runtime/audit.py +50 -0
- ciris_engine/schemas/runtime/bootstrap.py +33 -0
- ciris_engine/schemas/runtime/contexts.py +61 -0
- ciris_engine/schemas/runtime/core.py +161 -0
- ciris_engine/schemas/runtime/enums.py +167 -0
- ciris_engine/schemas/runtime/extended.py +232 -0
- ciris_engine/schemas/runtime/manifest.py +311 -0
- ciris_engine/schemas/runtime/memory.py +60 -0
- ciris_engine/schemas/runtime/messages.py +108 -0
- ciris_engine/schemas/runtime/models.py +156 -0
- ciris_engine/schemas/runtime/processing_context.py +43 -0
- ciris_engine/schemas/runtime/protocols_core.py +96 -0
- ciris_engine/schemas/runtime/resources.py +33 -0
- ciris_engine/schemas/runtime/system_context.py +417 -0
- ciris_engine/schemas/secrets/__init__.py +1 -0
- ciris_engine/schemas/secrets/core.py +267 -0
- ciris_engine/schemas/secrets/service.py +95 -0
- ciris_engine/schemas/services/__init__.py +33 -0
- ciris_engine/schemas/services/audit_summary_node.py +172 -0
- ciris_engine/schemas/services/authority/__init__.py +39 -0
- ciris_engine/schemas/services/authority/jwt.py +158 -0
- ciris_engine/schemas/services/authority/wa_updates.py +138 -0
- ciris_engine/schemas/services/authority/wise_authority.py +163 -0
- ciris_engine/schemas/services/authority_core.py +370 -0
- ciris_engine/schemas/services/capabilities.py +72 -0
- ciris_engine/schemas/services/community_core.py +95 -0
- ciris_engine/schemas/services/context.py +111 -0
- ciris_engine/schemas/services/conversation_summary_node.py +189 -0
- ciris_engine/schemas/services/core/__init__.py +153 -0
- ciris_engine/schemas/services/core/runtime.py +262 -0
- ciris_engine/schemas/services/core/runtime_config.py +117 -0
- ciris_engine/schemas/services/core/secrets.py +65 -0
- ciris_engine/schemas/services/correlation_node.py +179 -0
- ciris_engine/schemas/services/credit_gate.py +92 -0
- ciris_engine/schemas/services/discord_nodes.py +299 -0
- ciris_engine/schemas/services/feedback_core.py +131 -0
- ciris_engine/schemas/services/filters_core.py +270 -0
- ciris_engine/schemas/services/governance.py +26 -0
- ciris_engine/schemas/services/graph/__init__.py +26 -0
- ciris_engine/schemas/services/graph/attributes.py +254 -0
- ciris_engine/schemas/services/graph/audit.py +98 -0
- ciris_engine/schemas/services/graph/consolidation.py +338 -0
- ciris_engine/schemas/services/graph/edge_types.py +43 -0
- ciris_engine/schemas/services/graph/edges.py +88 -0
- ciris_engine/schemas/services/graph/incident.py +312 -0
- ciris_engine/schemas/services/graph/memory.py +84 -0
- ciris_engine/schemas/services/graph/node_data.py +174 -0
- ciris_engine/schemas/services/graph/query_results.py +82 -0
- ciris_engine/schemas/services/graph/telemetry.py +250 -0
- ciris_engine/schemas/services/graph/tsdb_consolidation.py +27 -0
- ciris_engine/schemas/services/graph/tsdb_models.py +107 -0
- ciris_engine/schemas/services/graph_core.py +196 -0
- ciris_engine/schemas/services/graph_typed_nodes.py +194 -0
- ciris_engine/schemas/services/infrastructure/__init__.py +1 -0
- ciris_engine/schemas/services/infrastructure/resource_monitor.py +20 -0
- ciris_engine/schemas/services/lifecycle/__init__.py +9 -0
- ciris_engine/schemas/services/lifecycle/initialization.py +33 -0
- ciris_engine/schemas/services/lifecycle/time.py +50 -0
- ciris_engine/schemas/services/llm.py +187 -0
- ciris_engine/schemas/services/metadata.py +43 -0
- ciris_engine/schemas/services/nodes.py +704 -0
- ciris_engine/schemas/services/operations.py +126 -0
- ciris_engine/schemas/services/requests.py +128 -0
- ciris_engine/schemas/services/resources_core.py +182 -0
- ciris_engine/schemas/services/runtime_control.py +1010 -0
- ciris_engine/schemas/services/shutdown.py +88 -0
- ciris_engine/schemas/services/special/__init__.py +0 -0
- ciris_engine/schemas/services/special/self_observation.py +396 -0
- ciris_engine/schemas/services/trace_summary_node.py +199 -0
- ciris_engine/schemas/services/visibility.py +98 -0
- ciris_engine/schemas/streaming/__init__.py +10 -0
- ciris_engine/schemas/streaming/reasoning_stream.py +95 -0
- ciris_engine/schemas/telemetry/__init__.py +0 -0
- ciris_engine/schemas/telemetry/collector.py +67 -0
- ciris_engine/schemas/telemetry/core.py +252 -0
- ciris_engine/schemas/telemetry/unified.py +59 -0
- ciris_engine/schemas/tools.py +72 -0
- ciris_engine/schemas/types.py +47 -0
- ciris_engine/schemas/utils/__init__.py +1 -0
- ciris_engine/schemas/utils/config_validator.py +54 -0
- ciris_engine/utils/__init__.py +1 -0
- ciris_engine/utils/serialization.py +35 -0
- ciris_sdk/__init__.py +124 -0
- ciris_sdk/auth_store.py +261 -0
- ciris_sdk/client.py +261 -0
- ciris_sdk/exceptions.py +73 -0
- ciris_sdk/model_types.py +258 -0
- ciris_sdk/models.py +354 -0
- ciris_sdk/pagination.py +214 -0
- ciris_sdk/rate_limiter.py +188 -0
- ciris_sdk/setup.py +17 -0
- ciris_sdk/telemetry_models.py +257 -0
- ciris_sdk/telemetry_responses.py +199 -0
- ciris_sdk/transport.py +177 -0
- ciris_sdk/websocket.py +400 -0
- main.py +766 -0
|
@@ -0,0 +1,1584 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import uuid
|
|
4
|
+
from datetime import datetime, timedelta, timezone
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Union
|
|
6
|
+
|
|
7
|
+
import discord
|
|
8
|
+
from discord.errors import ConnectionClosed, HTTPException
|
|
9
|
+
|
|
10
|
+
from ciris_engine.logic import persistence
|
|
11
|
+
from ciris_engine.logic.adapters.base import Service
|
|
12
|
+
from ciris_engine.protocols.services import CommunicationService, WiseAuthorityService
|
|
13
|
+
from ciris_engine.protocols.services.lifecycle.time import TimeServiceProtocol
|
|
14
|
+
from ciris_engine.schemas.adapters.discord import DiscordApprovalData, DiscordChannelInfo, DiscordGuidanceData
|
|
15
|
+
from ciris_engine.schemas.adapters.tools import ToolExecutionResult
|
|
16
|
+
from ciris_engine.schemas.runtime.enums import ServiceType
|
|
17
|
+
from ciris_engine.schemas.runtime.messages import FetchedMessage, IncomingMessage
|
|
18
|
+
from ciris_engine.schemas.runtime.system_context import ChannelContext
|
|
19
|
+
from ciris_engine.schemas.services.authority.wise_authority import PendingDeferral
|
|
20
|
+
from ciris_engine.schemas.services.authority_core import (
|
|
21
|
+
DeferralApprovalContext,
|
|
22
|
+
DeferralRequest,
|
|
23
|
+
DeferralResponse,
|
|
24
|
+
GuidanceRequest,
|
|
25
|
+
GuidanceResponse,
|
|
26
|
+
WAPermission,
|
|
27
|
+
)
|
|
28
|
+
from ciris_engine.schemas.services.context import DeferralContext, GuidanceContext
|
|
29
|
+
from ciris_engine.schemas.services.core import ServiceCapabilities, ServiceStatus
|
|
30
|
+
from ciris_engine.schemas.services.discord_nodes import DiscordApprovalNode, DiscordDeferralNode
|
|
31
|
+
from ciris_engine.schemas.services.graph_core import GraphNodeAttributes, GraphScope
|
|
32
|
+
from ciris_engine.schemas.telemetry.core import (
|
|
33
|
+
ServiceCorrelation,
|
|
34
|
+
ServiceCorrelationStatus,
|
|
35
|
+
ServiceRequestData,
|
|
36
|
+
ServiceResponseData,
|
|
37
|
+
)
|
|
38
|
+
from ciris_engine.schemas.types import JSONDict
|
|
39
|
+
|
|
40
|
+
from .config import DiscordAdapterConfig
|
|
41
|
+
from .constants import ACTION_OBSERVE
|
|
42
|
+
from .discord_audit import DiscordAuditLogger
|
|
43
|
+
from .discord_channel_manager import DiscordChannelManager
|
|
44
|
+
from .discord_connection_manager import DiscordConnectionManager
|
|
45
|
+
from .discord_embed_formatter import DiscordEmbedFormatter
|
|
46
|
+
from .discord_error_handler import DiscordErrorHandler
|
|
47
|
+
from .discord_guidance_handler import DiscordGuidanceHandler
|
|
48
|
+
from .discord_message_handler import DiscordMessageHandler
|
|
49
|
+
from .discord_rate_limiter import DiscordRateLimiter
|
|
50
|
+
from .discord_reaction_handler import ApprovalRequest, ApprovalStatus, DiscordReactionHandler
|
|
51
|
+
from .discord_tool_handler import DiscordToolHandler
|
|
52
|
+
|
|
53
|
+
if TYPE_CHECKING:
|
|
54
|
+
from ciris_engine.protocols.services.lifecycle.time import TimeServiceProtocol
|
|
55
|
+
from ciris_engine.schemas.adapters.registration import AdapterServiceRegistration
|
|
56
|
+
|
|
57
|
+
logger = logging.getLogger(__name__)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class DiscordAdapter(Service, CommunicationService, WiseAuthorityService):
|
|
61
|
+
"""
|
|
62
|
+
Discord adapter implementing CommunicationService and WiseAuthorityService protocols.
|
|
63
|
+
Coordinates specialized handlers for different aspects of Discord functionality.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
token: str,
|
|
69
|
+
bot: Optional[discord.Client] = None,
|
|
70
|
+
on_message: Optional[Callable[[IncomingMessage], Awaitable[None]]] = None,
|
|
71
|
+
time_service: Optional["TimeServiceProtocol"] = None,
|
|
72
|
+
bus_manager: Optional[Any] = None,
|
|
73
|
+
config: Optional[DiscordAdapterConfig] = None,
|
|
74
|
+
) -> None:
|
|
75
|
+
retry_config = {
|
|
76
|
+
"retry": {
|
|
77
|
+
"global": {
|
|
78
|
+
"max_retries": 3,
|
|
79
|
+
"base_delay": 2.0,
|
|
80
|
+
"max_delay": 30.0,
|
|
81
|
+
},
|
|
82
|
+
"discord_api": {
|
|
83
|
+
"retryable_exceptions": (HTTPException, ConnectionClosed, asyncio.TimeoutError),
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
super().__init__(config=retry_config)
|
|
88
|
+
|
|
89
|
+
self.token = token
|
|
90
|
+
self._time_service = time_service
|
|
91
|
+
self.bus_manager = bus_manager
|
|
92
|
+
self.discord_config = config or DiscordAdapterConfig()
|
|
93
|
+
|
|
94
|
+
# Ensure we have a time service
|
|
95
|
+
if self._time_service is None:
|
|
96
|
+
from ciris_engine.logic.services.lifecycle.time import TimeService
|
|
97
|
+
|
|
98
|
+
self._time_service = TimeService()
|
|
99
|
+
|
|
100
|
+
# Pass monitored channel IDs from config
|
|
101
|
+
monitored_channels = self.discord_config.monitored_channel_ids if self.discord_config else []
|
|
102
|
+
|
|
103
|
+
# Get filter and consent services from bus manager if available
|
|
104
|
+
filter_service = None
|
|
105
|
+
consent_service = None
|
|
106
|
+
if bus_manager:
|
|
107
|
+
filter_service = getattr(bus_manager, "adaptive_filter_service", None)
|
|
108
|
+
consent_service = getattr(bus_manager, "consent_service", None)
|
|
109
|
+
|
|
110
|
+
self._channel_manager = DiscordChannelManager(
|
|
111
|
+
token=token,
|
|
112
|
+
client=bot,
|
|
113
|
+
on_message_callback=on_message,
|
|
114
|
+
monitored_channel_ids=monitored_channels,
|
|
115
|
+
filter_service=filter_service,
|
|
116
|
+
consent_service=consent_service,
|
|
117
|
+
)
|
|
118
|
+
self._message_handler = DiscordMessageHandler(bot)
|
|
119
|
+
self._guidance_handler = DiscordGuidanceHandler(
|
|
120
|
+
bot, self._time_service, self.bus_manager.memory if self.bus_manager else None
|
|
121
|
+
)
|
|
122
|
+
self._reaction_handler = DiscordReactionHandler(bot, self._time_service)
|
|
123
|
+
self._audit_logger = DiscordAuditLogger(self._time_service)
|
|
124
|
+
self._connection_manager = DiscordConnectionManager(token, bot, self._time_service)
|
|
125
|
+
self._error_handler = DiscordErrorHandler()
|
|
126
|
+
self._rate_limiter = DiscordRateLimiter()
|
|
127
|
+
self._embed_formatter = DiscordEmbedFormatter()
|
|
128
|
+
self._tool_handler = DiscordToolHandler(None, bot, self._time_service)
|
|
129
|
+
self._start_time: Optional[datetime] = None
|
|
130
|
+
self._approval_timeout_task: Optional[asyncio.Task[None]] = None
|
|
131
|
+
|
|
132
|
+
# Metrics tracking for v1.4.3 - Discord adapter metrics
|
|
133
|
+
self._messages_processed = 0
|
|
134
|
+
self._commands_handled = 0
|
|
135
|
+
self._errors_total = 0
|
|
136
|
+
|
|
137
|
+
# Set up connection callbacks
|
|
138
|
+
self._setup_connection_callbacks()
|
|
139
|
+
|
|
140
|
+
async def _retry_discord_operation(
|
|
141
|
+
self,
|
|
142
|
+
operation: Callable[..., Awaitable[Any]],
|
|
143
|
+
*args: Any,
|
|
144
|
+
operation_name: str,
|
|
145
|
+
config_key: str = "discord_api",
|
|
146
|
+
**kwargs: Any,
|
|
147
|
+
) -> Any:
|
|
148
|
+
"""Wrapper for retry_with_backoff that handles Discord-specific configuration."""
|
|
149
|
+
# Apply rate limiting before the operation
|
|
150
|
+
endpoint = kwargs.get("endpoint", operation_name)
|
|
151
|
+
await self._rate_limiter.acquire(endpoint)
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
# Get retry config from base class config (which is a dict)
|
|
155
|
+
retry_cfg = (
|
|
156
|
+
self.config.get("retry", {}).get(config_key, {})
|
|
157
|
+
if hasattr(self, "config") and isinstance(self.config, dict)
|
|
158
|
+
else {}
|
|
159
|
+
)
|
|
160
|
+
result = await self.retry_with_backoff(
|
|
161
|
+
operation,
|
|
162
|
+
*args,
|
|
163
|
+
max_retries=retry_cfg.get("max_retries", 3),
|
|
164
|
+
base_delay=retry_cfg.get("base_delay", 2.0),
|
|
165
|
+
max_delay=retry_cfg.get("max_delay", 30.0),
|
|
166
|
+
# Include all connection-related errors as retryable
|
|
167
|
+
retryable_exceptions=retry_cfg.get(
|
|
168
|
+
"retryable_exceptions",
|
|
169
|
+
(
|
|
170
|
+
HTTPException, # Discord API errors
|
|
171
|
+
ConnectionClosed, # WebSocket closed
|
|
172
|
+
asyncio.TimeoutError, # Timeout errors
|
|
173
|
+
RuntimeError, # Session closed errors
|
|
174
|
+
OSError, # SSL and network errors
|
|
175
|
+
ConnectionError, # Base connection errors
|
|
176
|
+
ConnectionResetError, # Connection reset by peer
|
|
177
|
+
ConnectionAbortedError, # Connection aborted
|
|
178
|
+
),
|
|
179
|
+
),
|
|
180
|
+
**kwargs,
|
|
181
|
+
)
|
|
182
|
+
return result
|
|
183
|
+
except Exception as e:
|
|
184
|
+
# Handle errors with the error handler
|
|
185
|
+
if isinstance(e, (HTTPException, ConnectionClosed)):
|
|
186
|
+
error_info = self._error_handler.handle_channel_error(
|
|
187
|
+
kwargs.get("channel_id", "unknown"), e, operation_name
|
|
188
|
+
)
|
|
189
|
+
# Re-raise if not retryable
|
|
190
|
+
if not error_info.can_retry:
|
|
191
|
+
raise
|
|
192
|
+
raise
|
|
193
|
+
|
|
194
|
+
async def _emit_telemetry(
|
|
195
|
+
self, metric_name: str, value: float = 1.0, tags: Optional[dict[str, Union[str, float, int, bool]]] = None
|
|
196
|
+
) -> None:
|
|
197
|
+
"""Emit telemetry as TSDBGraphNode through memory bus."""
|
|
198
|
+
if not self.bus_manager or not self.bus_manager.memory:
|
|
199
|
+
return # No bus manager, can't emit telemetry
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
# If value is in tags, extract it
|
|
203
|
+
if tags and "value" in tags:
|
|
204
|
+
value = float(tags.pop("value"))
|
|
205
|
+
elif tags and "execution_time" in tags:
|
|
206
|
+
value = float(tags["execution_time"])
|
|
207
|
+
elif tags and "success" in tags:
|
|
208
|
+
# For boolean success, use 1.0 for true, 0.0 for false
|
|
209
|
+
value = 1.0 if tags["success"] else 0.0
|
|
210
|
+
|
|
211
|
+
# Convert all tag values to strings as required by memorize_metric
|
|
212
|
+
string_tags = {k: str(v) for k, v in (tags or {}).items()}
|
|
213
|
+
|
|
214
|
+
# Use memorize_metric instead of creating GraphNode directly
|
|
215
|
+
await self.bus_manager.memory.memorize_metric(
|
|
216
|
+
metric_name=metric_name, value=value, tags=string_tags, scope="local", handler_name="adapter.discord"
|
|
217
|
+
)
|
|
218
|
+
except Exception as e:
|
|
219
|
+
logger.debug(f"Failed to emit telemetry {metric_name}: {e}")
|
|
220
|
+
|
|
221
|
+
async def send_message(self, channel_id: str, content: str) -> bool:
|
|
222
|
+
"""Implementation of CommunicationService.send_message"""
|
|
223
|
+
# Check if client exists, but let retry logic handle connection state
|
|
224
|
+
if not self._client:
|
|
225
|
+
logger.warning(f"Discord client not initialized, cannot send message to channel {channel_id}")
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
correlation_id = str(uuid.uuid4())
|
|
229
|
+
time_service = self._time_service
|
|
230
|
+
if time_service is None:
|
|
231
|
+
logger.error("Time service not initialized")
|
|
232
|
+
raise RuntimeError("Time service not initialized")
|
|
233
|
+
start_time = time_service.now()
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
# The retry logic will handle connection issues and wait for reconnection
|
|
237
|
+
await self._retry_discord_operation(
|
|
238
|
+
self._message_handler.send_message_to_channel,
|
|
239
|
+
channel_id,
|
|
240
|
+
content,
|
|
241
|
+
operation_name="send_message",
|
|
242
|
+
config_key="discord_api",
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
end_time = time_service.now()
|
|
246
|
+
execution_time_ms = (end_time - start_time).total_seconds() * 1000
|
|
247
|
+
|
|
248
|
+
# result contains the return value from send_message_to_channel
|
|
249
|
+
persistence.add_correlation(
|
|
250
|
+
ServiceCorrelation(
|
|
251
|
+
correlation_id=correlation_id,
|
|
252
|
+
service_type="discord",
|
|
253
|
+
handler_name="DiscordAdapter",
|
|
254
|
+
action_type="speak",
|
|
255
|
+
request_data=ServiceRequestData(
|
|
256
|
+
service_type="discord",
|
|
257
|
+
method_name="send_message",
|
|
258
|
+
channel_id=channel_id,
|
|
259
|
+
parameters={"content": content},
|
|
260
|
+
request_timestamp=start_time,
|
|
261
|
+
),
|
|
262
|
+
response_data=ServiceResponseData(
|
|
263
|
+
success=True,
|
|
264
|
+
result_summary="Message sent successfully",
|
|
265
|
+
execution_time_ms=execution_time_ms,
|
|
266
|
+
response_timestamp=end_time,
|
|
267
|
+
),
|
|
268
|
+
status=ServiceCorrelationStatus.COMPLETED,
|
|
269
|
+
created_at=start_time,
|
|
270
|
+
updated_at=end_time,
|
|
271
|
+
timestamp=start_time,
|
|
272
|
+
),
|
|
273
|
+
time_service,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# Increment message processed counter for sent messages too
|
|
277
|
+
self._messages_processed += 1
|
|
278
|
+
|
|
279
|
+
# Emit telemetry for message sent
|
|
280
|
+
await self._emit_telemetry(
|
|
281
|
+
"discord.message.sent",
|
|
282
|
+
1.0,
|
|
283
|
+
{"adapter_type": "discord", "channel_id": channel_id, "execution_time": str(execution_time_ms)},
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
# Audit log the operation
|
|
287
|
+
await self._audit_logger.log_message_sent(
|
|
288
|
+
channel_id=channel_id,
|
|
289
|
+
author_id="discord_adapter",
|
|
290
|
+
message_content=content,
|
|
291
|
+
correlation_id=correlation_id,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
return True
|
|
295
|
+
except (HTTPException, ConnectionClosed, asyncio.TimeoutError, RuntimeError, OSError, ConnectionError) as e:
|
|
296
|
+
# These are retryable exceptions - let them propagate so retry logic can handle them
|
|
297
|
+
# But first log the error for debugging
|
|
298
|
+
self._errors_total += 1
|
|
299
|
+
error_info = self._error_handler.handle_message_error(e, content, channel_id)
|
|
300
|
+
logger.error(f"Failed to send message via Discord: {error_info}")
|
|
301
|
+
# Re-raise the exception so retry logic can handle it
|
|
302
|
+
raise
|
|
303
|
+
except Exception as e:
|
|
304
|
+
# Handle non-retryable errors
|
|
305
|
+
self._errors_total += 1
|
|
306
|
+
error_info = self._error_handler.handle_message_error(e, content, channel_id)
|
|
307
|
+
logger.error(f"Failed to send message via Discord (non-retryable): {error_info}")
|
|
308
|
+
return False
|
|
309
|
+
|
|
310
|
+
async def fetch_messages(
|
|
311
|
+
self, channel_id: str, *, limit: int = 50, before: Optional[datetime] = None
|
|
312
|
+
) -> List[FetchedMessage]:
|
|
313
|
+
"""Implementation of CommunicationService.fetch_messages - fetches from Discord API to include all messages"""
|
|
314
|
+
# Primary: Fetch directly from Discord API to include messages from all users and bots
|
|
315
|
+
if self._channel_manager.client:
|
|
316
|
+
try:
|
|
317
|
+
messages_result = await self._retry_discord_operation(
|
|
318
|
+
self._message_handler.fetch_messages_from_channel,
|
|
319
|
+
channel_id,
|
|
320
|
+
limit,
|
|
321
|
+
operation_name="fetch_messages",
|
|
322
|
+
config_key="discord_api",
|
|
323
|
+
)
|
|
324
|
+
# Messages from handler are already FetchedMessage objects
|
|
325
|
+
if messages_result:
|
|
326
|
+
# Type narrow: we know this should be List[FetchedMessage]
|
|
327
|
+
return list(messages_result) if isinstance(messages_result, list) else []
|
|
328
|
+
except Exception as e:
|
|
329
|
+
logger.warning(f"Failed to fetch messages from Discord API for channel {channel_id}: {e}")
|
|
330
|
+
|
|
331
|
+
# Fallback: Try correlation database (only includes messages this agent observed/spoke)
|
|
332
|
+
from ciris_engine.logic.persistence import get_correlations_by_channel
|
|
333
|
+
|
|
334
|
+
try:
|
|
335
|
+
# Get correlations for this channel
|
|
336
|
+
correlations = get_correlations_by_channel(channel_id=channel_id, limit=limit)
|
|
337
|
+
|
|
338
|
+
messages = []
|
|
339
|
+
for corr in correlations:
|
|
340
|
+
# Extract message data from correlation
|
|
341
|
+
if corr.action_type == "speak" and corr.request_data:
|
|
342
|
+
# This is an outgoing message from the agent
|
|
343
|
+
content = ""
|
|
344
|
+
if hasattr(corr.request_data, "parameters") and corr.request_data.parameters:
|
|
345
|
+
content = corr.request_data.parameters.get("content", "")
|
|
346
|
+
|
|
347
|
+
messages.append(
|
|
348
|
+
FetchedMessage(
|
|
349
|
+
message_id=corr.correlation_id,
|
|
350
|
+
author_id="ciris",
|
|
351
|
+
author_name="CIRIS",
|
|
352
|
+
content=content,
|
|
353
|
+
timestamp=(
|
|
354
|
+
(corr.timestamp or corr.created_at).isoformat()
|
|
355
|
+
if corr.timestamp or corr.created_at
|
|
356
|
+
else None
|
|
357
|
+
),
|
|
358
|
+
is_bot=True,
|
|
359
|
+
)
|
|
360
|
+
)
|
|
361
|
+
elif corr.action_type == ACTION_OBSERVE and corr.request_data:
|
|
362
|
+
# This is an incoming message from a user
|
|
363
|
+
content = ""
|
|
364
|
+
author_id = "unknown"
|
|
365
|
+
author_name = "User"
|
|
366
|
+
|
|
367
|
+
if hasattr(corr.request_data, "parameters") and corr.request_data.parameters:
|
|
368
|
+
params = corr.request_data.parameters
|
|
369
|
+
content = params.get("content", "")
|
|
370
|
+
author_id = params.get("author_id", "unknown")
|
|
371
|
+
author_name = params.get("author_name", "User")
|
|
372
|
+
|
|
373
|
+
messages.append(
|
|
374
|
+
FetchedMessage(
|
|
375
|
+
message_id=corr.correlation_id,
|
|
376
|
+
author_id=author_id,
|
|
377
|
+
author_name=author_name,
|
|
378
|
+
content=content,
|
|
379
|
+
timestamp=(
|
|
380
|
+
(corr.timestamp or corr.created_at).isoformat()
|
|
381
|
+
if corr.timestamp or corr.created_at
|
|
382
|
+
else None
|
|
383
|
+
),
|
|
384
|
+
is_bot=False,
|
|
385
|
+
)
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
# Sort by timestamp
|
|
389
|
+
messages.sort(key=lambda m: m.timestamp or "")
|
|
390
|
+
|
|
391
|
+
return messages
|
|
392
|
+
|
|
393
|
+
except Exception as e:
|
|
394
|
+
logger.error(f"Failed to fetch messages from correlations for Discord channel {channel_id}: {e}")
|
|
395
|
+
return []
|
|
396
|
+
|
|
397
|
+
# --- WiseAuthorityService ---
|
|
398
|
+
async def fetch_guidance(self, context: GuidanceContext) -> Optional[str]:
|
|
399
|
+
"""Send a guidance request to the configured guidance channel and wait for a response."""
|
|
400
|
+
deferral_channel_id = self.discord_config.deferral_channel_id
|
|
401
|
+
if not deferral_channel_id:
|
|
402
|
+
logger.error("DiscordAdapter: Guidance channel not configured.")
|
|
403
|
+
raise RuntimeError("Guidance channel not configured.")
|
|
404
|
+
|
|
405
|
+
time_service = self._time_service
|
|
406
|
+
if time_service is None:
|
|
407
|
+
logger.error("Time service not initialized")
|
|
408
|
+
return None
|
|
409
|
+
start_time = time_service.now()
|
|
410
|
+
|
|
411
|
+
try:
|
|
412
|
+
correlation_id = str(uuid.uuid4())
|
|
413
|
+
guidance_result = await self._retry_discord_operation(
|
|
414
|
+
self._guidance_handler.fetch_guidance_from_channel,
|
|
415
|
+
deferral_channel_id,
|
|
416
|
+
context.model_dump(),
|
|
417
|
+
operation_name="fetch_guidance",
|
|
418
|
+
config_key="discord_api",
|
|
419
|
+
)
|
|
420
|
+
# guidance_result should be a dict from fetch_guidance_from_channel
|
|
421
|
+
guidance = guidance_result if isinstance(guidance_result, dict) else {}
|
|
422
|
+
|
|
423
|
+
end_time = time_service.now()
|
|
424
|
+
execution_time_ms = (end_time - start_time).total_seconds() * 1000
|
|
425
|
+
|
|
426
|
+
persistence.add_correlation(
|
|
427
|
+
ServiceCorrelation(
|
|
428
|
+
correlation_id=correlation_id,
|
|
429
|
+
service_type="discord",
|
|
430
|
+
handler_name="DiscordAdapter",
|
|
431
|
+
action_type="fetch_guidance",
|
|
432
|
+
request_data=ServiceRequestData(
|
|
433
|
+
service_type="discord",
|
|
434
|
+
method_name="fetch_guidance",
|
|
435
|
+
channel_id=deferral_channel_id,
|
|
436
|
+
parameters={"context": str(context.model_dump())},
|
|
437
|
+
request_timestamp=start_time,
|
|
438
|
+
),
|
|
439
|
+
response_data=ServiceResponseData(
|
|
440
|
+
success=True,
|
|
441
|
+
result_summary=f"Guidance received: {guidance.get('guidance', 'None')}",
|
|
442
|
+
execution_time_ms=execution_time_ms,
|
|
443
|
+
response_timestamp=end_time,
|
|
444
|
+
),
|
|
445
|
+
status=ServiceCorrelationStatus.COMPLETED,
|
|
446
|
+
created_at=start_time,
|
|
447
|
+
updated_at=end_time,
|
|
448
|
+
timestamp=start_time,
|
|
449
|
+
),
|
|
450
|
+
time_service,
|
|
451
|
+
)
|
|
452
|
+
# Note: Guidance requests are already audited via defer handler action
|
|
453
|
+
|
|
454
|
+
guidance_text = guidance.get("guidance")
|
|
455
|
+
return guidance_text
|
|
456
|
+
except Exception as e:
|
|
457
|
+
self._errors_total += 1
|
|
458
|
+
logger.exception(f"Failed to fetch guidance from Discord: {e}")
|
|
459
|
+
raise
|
|
460
|
+
|
|
461
|
+
async def check_authorization(self, wa_id: str, action: str, resource: Optional[str] = None) -> bool:
|
|
462
|
+
"""Check if a Discord user is authorized for an action."""
|
|
463
|
+
# In Discord, authorization is based on roles:
|
|
464
|
+
# - AUTHORITY role can do anything
|
|
465
|
+
# - OBSERVER role can only observe
|
|
466
|
+
# - No role = no permissions
|
|
467
|
+
try:
|
|
468
|
+
if not self._channel_manager.client:
|
|
469
|
+
return False
|
|
470
|
+
|
|
471
|
+
# Get user from all guilds the bot is in
|
|
472
|
+
user = None
|
|
473
|
+
for guild in self._channel_manager.client.guilds:
|
|
474
|
+
member = guild.get_member(int(wa_id))
|
|
475
|
+
if member:
|
|
476
|
+
user = member
|
|
477
|
+
break
|
|
478
|
+
|
|
479
|
+
if not user:
|
|
480
|
+
return False
|
|
481
|
+
|
|
482
|
+
# Check roles
|
|
483
|
+
role_names = [role.name.upper() for role in user.roles]
|
|
484
|
+
|
|
485
|
+
# AUTHORITY can do anything
|
|
486
|
+
if "AUTHORITY" in role_names:
|
|
487
|
+
return True
|
|
488
|
+
|
|
489
|
+
# OBSERVER can only observe/read
|
|
490
|
+
if "OBSERVER" in role_names and action in ["read", "observe", "fetch"]:
|
|
491
|
+
return True
|
|
492
|
+
|
|
493
|
+
return False
|
|
494
|
+
except Exception as e:
|
|
495
|
+
logger.error(f"Error checking authorization for {wa_id}: {e}")
|
|
496
|
+
return False
|
|
497
|
+
|
|
498
|
+
async def request_approval(self, action: str, context: DeferralApprovalContext) -> bool:
|
|
499
|
+
"""Request approval for an action through the deferral channel."""
|
|
500
|
+
deferral_channel_id = self.discord_config.deferral_channel_id
|
|
501
|
+
if not deferral_channel_id:
|
|
502
|
+
logger.error("DiscordAdapter: Deferral channel not configured.")
|
|
503
|
+
return False
|
|
504
|
+
|
|
505
|
+
try:
|
|
506
|
+
# Create approval request embed
|
|
507
|
+
approval_data = DiscordApprovalData(
|
|
508
|
+
action=action,
|
|
509
|
+
task_id=context.task_id,
|
|
510
|
+
thought_id=context.thought_id,
|
|
511
|
+
requester_id=context.requester_id,
|
|
512
|
+
action_name=context.action_name,
|
|
513
|
+
action_params=context.action_params or {},
|
|
514
|
+
channel_id=context.channel_id,
|
|
515
|
+
)
|
|
516
|
+
embed = self._embed_formatter.format_approval_request(action, approval_data)
|
|
517
|
+
|
|
518
|
+
# Get channel for sending embed
|
|
519
|
+
channel = await self._channel_manager.resolve_channel(deferral_channel_id)
|
|
520
|
+
if not channel:
|
|
521
|
+
logger.error(f"Could not resolve deferral channel {deferral_channel_id}")
|
|
522
|
+
return False
|
|
523
|
+
|
|
524
|
+
# Send embed message
|
|
525
|
+
sent_message = await channel.send(embed=embed)
|
|
526
|
+
|
|
527
|
+
# Create approval result container
|
|
528
|
+
approval_result = None
|
|
529
|
+
|
|
530
|
+
async def handle_approval(approval: ApprovalRequest) -> None:
|
|
531
|
+
nonlocal approval_result
|
|
532
|
+
approval_result = approval
|
|
533
|
+
|
|
534
|
+
# Create approval request using the sent message
|
|
535
|
+
approval_request = ApprovalRequest(
|
|
536
|
+
message_id=sent_message.id,
|
|
537
|
+
channel_id=int(deferral_channel_id),
|
|
538
|
+
request_type="action_approval",
|
|
539
|
+
context={
|
|
540
|
+
"action": action,
|
|
541
|
+
"task_id": context.task_id,
|
|
542
|
+
"thought_id": context.thought_id,
|
|
543
|
+
"requester_id": context.requester_id,
|
|
544
|
+
},
|
|
545
|
+
timeout_seconds=300, # 5 minute timeout
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
# Add reactions
|
|
549
|
+
await sent_message.add_reaction("✅")
|
|
550
|
+
await sent_message.add_reaction("❌")
|
|
551
|
+
|
|
552
|
+
# Register with reaction handler
|
|
553
|
+
self._reaction_handler._pending_approvals[sent_message.id] = approval_request
|
|
554
|
+
self._reaction_handler._approval_callbacks[sent_message.id] = handle_approval
|
|
555
|
+
|
|
556
|
+
# Schedule timeout
|
|
557
|
+
self._approval_timeout_task = asyncio.create_task(self._reaction_handler._handle_timeout(approval_request))
|
|
558
|
+
|
|
559
|
+
if not approval_request:
|
|
560
|
+
return False
|
|
561
|
+
|
|
562
|
+
# Wait for approval resolution (up to timeout)
|
|
563
|
+
max_wait = approval_request.timeout_seconds + 5
|
|
564
|
+
time_service = self._time_service
|
|
565
|
+
if time_service is None:
|
|
566
|
+
logger.error("Time service not initialized")
|
|
567
|
+
return False
|
|
568
|
+
start_time = time_service.now()
|
|
569
|
+
|
|
570
|
+
while approval_result is None:
|
|
571
|
+
await asyncio.sleep(0.5)
|
|
572
|
+
elapsed = (time_service.now() - start_time).total_seconds()
|
|
573
|
+
if elapsed > max_wait:
|
|
574
|
+
logger.error("Approval request timed out")
|
|
575
|
+
return False
|
|
576
|
+
|
|
577
|
+
# Store approval request in memory
|
|
578
|
+
if approval_result and self.bus_manager and self.bus_manager.memory:
|
|
579
|
+
try:
|
|
580
|
+
approval_node = DiscordApprovalNode(
|
|
581
|
+
id=f"discord_approval/{approval_request.message_id}",
|
|
582
|
+
scope=GraphScope.LOCAL,
|
|
583
|
+
attributes=GraphNodeAttributes(created_by="discord_adapter", tags=["discord", "approval"]),
|
|
584
|
+
approval_id=str(approval_request.message_id),
|
|
585
|
+
action=action,
|
|
586
|
+
request_type="action_approval",
|
|
587
|
+
channel_id=deferral_channel_id,
|
|
588
|
+
message_id=str(approval_request.message_id),
|
|
589
|
+
task_id=context.task_id,
|
|
590
|
+
thought_id=context.thought_id,
|
|
591
|
+
requester_id=context.requester_id,
|
|
592
|
+
status=approval_result.status.value,
|
|
593
|
+
resolved_at=approval_result.resolved_at,
|
|
594
|
+
resolver_id=approval_result.resolver_id,
|
|
595
|
+
resolver_name=approval_result.resolver_name,
|
|
596
|
+
context={"channel_id": context.channel_id} if context.channel_id else {},
|
|
597
|
+
action_params=context.action_params,
|
|
598
|
+
updated_at=time_service.now(),
|
|
599
|
+
updated_by="discord_adapter",
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
await self.bus_manager.memory.store(
|
|
603
|
+
node_id=str(approval_request.message_id),
|
|
604
|
+
node_type="DISCORD_APPROVAL",
|
|
605
|
+
attributes=approval_node.to_graph_node().attributes,
|
|
606
|
+
scope="local",
|
|
607
|
+
handler_name="discord_adapter",
|
|
608
|
+
)
|
|
609
|
+
except Exception as e:
|
|
610
|
+
logger.error(f"Failed to store approval in memory: {e}")
|
|
611
|
+
|
|
612
|
+
# Note: Approval requests are already audited via handler actions
|
|
613
|
+
|
|
614
|
+
# Return true only if approved
|
|
615
|
+
return approval_result.status == ApprovalStatus.APPROVED
|
|
616
|
+
|
|
617
|
+
except Exception as e:
|
|
618
|
+
self._errors_total += 1
|
|
619
|
+
logger.exception(f"Failed to request approval: {e}")
|
|
620
|
+
return False
|
|
621
|
+
|
|
622
|
+
async def get_guidance(self, request: GuidanceRequest) -> GuidanceResponse:
|
|
623
|
+
"""Get guidance using the structured request/response format."""
|
|
624
|
+
# Convert GuidanceRequest to GuidanceContext for fetch_guidance
|
|
625
|
+
# Generate IDs if not available
|
|
626
|
+
context = GuidanceContext(
|
|
627
|
+
thought_id=f"guidance_{uuid.uuid4().hex[:8]}",
|
|
628
|
+
task_id=f"task_{uuid.uuid4().hex[:8]}",
|
|
629
|
+
question=request.context, # GuidanceRequest.context is the question
|
|
630
|
+
ethical_considerations=request.options if request.options else [],
|
|
631
|
+
domain_context={"urgency": request.urgency} if request.urgency else {},
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
guidance = await self.fetch_guidance(context)
|
|
635
|
+
|
|
636
|
+
# Increment commands handled counter for guidance requests
|
|
637
|
+
self._commands_handled += 1
|
|
638
|
+
|
|
639
|
+
return GuidanceResponse(
|
|
640
|
+
selected_option=guidance if guidance in request.options else None,
|
|
641
|
+
custom_guidance=guidance if guidance not in request.options else None,
|
|
642
|
+
reasoning="Guidance provided by Discord WA channel",
|
|
643
|
+
wa_id="discord_wa",
|
|
644
|
+
signature=f"discord_{uuid.uuid4().hex[:8]}",
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
async def get_pending_deferrals(self, wa_id: Optional[str] = None) -> List[PendingDeferral]:
|
|
648
|
+
"""Get pending deferrals from the deferral channel."""
|
|
649
|
+
if not self.bus_manager or not self.bus_manager.memory:
|
|
650
|
+
logger.warning("No memory bus available for deferral tracking")
|
|
651
|
+
return []
|
|
652
|
+
|
|
653
|
+
try:
|
|
654
|
+
# Query memory for pending deferrals
|
|
655
|
+
query = {"node_type": "DISCORD_DEFERRAL", "status": "pending"}
|
|
656
|
+
|
|
657
|
+
# Add WA filter if specified
|
|
658
|
+
if wa_id:
|
|
659
|
+
query["created_by"] = wa_id
|
|
660
|
+
|
|
661
|
+
# Search memory
|
|
662
|
+
nodes = await self.bus_manager.memory.search(query)
|
|
663
|
+
|
|
664
|
+
# Convert to PendingDeferral objects
|
|
665
|
+
pending = []
|
|
666
|
+
for node in nodes:
|
|
667
|
+
if isinstance(node.attributes, dict):
|
|
668
|
+
attrs = node.attributes
|
|
669
|
+
else:
|
|
670
|
+
attrs = node.attributes.model_dump() if hasattr(node.attributes, "model_dump") else {}
|
|
671
|
+
|
|
672
|
+
pending.append(
|
|
673
|
+
PendingDeferral(
|
|
674
|
+
deferral_id=attrs.get("deferral_id", node.id),
|
|
675
|
+
task_id=attrs.get("task_id", ""),
|
|
676
|
+
thought_id=attrs.get("thought_id", ""),
|
|
677
|
+
reason=attrs.get("reason", ""),
|
|
678
|
+
created_at=attrs.get(
|
|
679
|
+
"created_at", self._time_service.now() if self._time_service else datetime.now()
|
|
680
|
+
),
|
|
681
|
+
deferred_by=attrs.get("created_by", "discord_agent"),
|
|
682
|
+
channel_id=attrs.get("channel_id"),
|
|
683
|
+
priority=attrs.get("priority", "normal"),
|
|
684
|
+
)
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
return pending
|
|
688
|
+
|
|
689
|
+
except Exception as e:
|
|
690
|
+
logger.error(f"Failed to get pending deferrals: {e}")
|
|
691
|
+
return []
|
|
692
|
+
|
|
693
|
+
async def resolve_deferral(self, deferral_id: str, response: DeferralResponse) -> bool:
|
|
694
|
+
"""Resolve a deferred decision."""
|
|
695
|
+
deferral_channel_id = self.discord_config.deferral_channel_id
|
|
696
|
+
if not deferral_channel_id:
|
|
697
|
+
return False
|
|
698
|
+
|
|
699
|
+
try:
|
|
700
|
+
# Send resolution message
|
|
701
|
+
message = "**DEFERRAL RESOLVED**\n"
|
|
702
|
+
message += f"ID: {deferral_id}\n"
|
|
703
|
+
message += f"Approved: {'Yes' if response.approved else 'No'}\n"
|
|
704
|
+
if response.reason:
|
|
705
|
+
message += f"Reason: {response.reason}\n"
|
|
706
|
+
if response.modified_time:
|
|
707
|
+
message += f"Modified Time: {response.modified_time.isoformat()}\n"
|
|
708
|
+
message += f"WA ID: {response.wa_id}\n"
|
|
709
|
+
|
|
710
|
+
return await self.send_message(deferral_channel_id, message)
|
|
711
|
+
except Exception as e:
|
|
712
|
+
logger.error(f"Failed to resolve deferral: {e}")
|
|
713
|
+
return False
|
|
714
|
+
|
|
715
|
+
async def grant_permission(self, wa_id: str, permission: str, resource: Optional[str] = None) -> bool:
|
|
716
|
+
"""Grant AUTHORITY or OBSERVER role to a Discord user."""
|
|
717
|
+
if permission.upper() not in ["AUTHORITY", "OBSERVER"]:
|
|
718
|
+
logger.error(f"Invalid permission: {permission}. Must be AUTHORITY or OBSERVER.")
|
|
719
|
+
return False
|
|
720
|
+
|
|
721
|
+
try:
|
|
722
|
+
if not self._channel_manager.client:
|
|
723
|
+
return False
|
|
724
|
+
|
|
725
|
+
# Find user in guilds and grant role
|
|
726
|
+
for guild in self._channel_manager.client.guilds:
|
|
727
|
+
member = guild.get_member(int(wa_id))
|
|
728
|
+
if member:
|
|
729
|
+
# Find or create role
|
|
730
|
+
role = discord.utils.get(guild.roles, name=permission.upper())
|
|
731
|
+
if not role:
|
|
732
|
+
# Create role if it doesn't exist
|
|
733
|
+
role = await guild.create_role(name=permission.upper())
|
|
734
|
+
|
|
735
|
+
# Grant role
|
|
736
|
+
await member.add_roles(role)
|
|
737
|
+
logger.info(f"Granted {permission} to user {wa_id} in guild {guild.name}")
|
|
738
|
+
|
|
739
|
+
# Note: Permission changes are already audited via grant/revoke handler actions
|
|
740
|
+
|
|
741
|
+
return True
|
|
742
|
+
|
|
743
|
+
logger.error(f"User {wa_id} not found in any guild")
|
|
744
|
+
return False
|
|
745
|
+
except Exception as e:
|
|
746
|
+
logger.exception(f"Failed to grant permission: {e}")
|
|
747
|
+
return False
|
|
748
|
+
|
|
749
|
+
async def revoke_permission(self, wa_id: str, permission: str, resource: Optional[str] = None) -> bool:
|
|
750
|
+
"""Revoke AUTHORITY or OBSERVER role from a Discord user."""
|
|
751
|
+
if permission.upper() not in ["AUTHORITY", "OBSERVER"]:
|
|
752
|
+
logger.error(f"Invalid permission: {permission}. Must be AUTHORITY or OBSERVER.")
|
|
753
|
+
return False
|
|
754
|
+
|
|
755
|
+
try:
|
|
756
|
+
if not self._channel_manager.client:
|
|
757
|
+
return False
|
|
758
|
+
|
|
759
|
+
# Find user in guilds and remove role
|
|
760
|
+
for guild in self._channel_manager.client.guilds:
|
|
761
|
+
member = guild.get_member(int(wa_id))
|
|
762
|
+
if member:
|
|
763
|
+
role = discord.utils.get(guild.roles, name=permission.upper())
|
|
764
|
+
if role and role in member.roles:
|
|
765
|
+
await member.remove_roles(role)
|
|
766
|
+
logger.info(f"Revoked {permission} from user {wa_id} in guild {guild.name}")
|
|
767
|
+
|
|
768
|
+
# Note: Permission changes are already audited via grant/revoke handler actions
|
|
769
|
+
|
|
770
|
+
return True
|
|
771
|
+
|
|
772
|
+
return False
|
|
773
|
+
except Exception as e:
|
|
774
|
+
logger.exception(f"Failed to revoke permission: {e}")
|
|
775
|
+
return False
|
|
776
|
+
|
|
777
|
+
def get_active_channels(self) -> List[DiscordChannelInfo]:
|
|
778
|
+
"""Get list of active Discord channels."""
|
|
779
|
+
channels: List[DiscordChannelInfo] = []
|
|
780
|
+
|
|
781
|
+
logger.info(
|
|
782
|
+
f"[DISCORD] get_active_channels called, client ready: {self._channel_manager.client.is_ready() if self._channel_manager.client else False}"
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
if not self._channel_manager.client or not self._channel_manager.client.is_ready():
|
|
786
|
+
logger.warning("[DISCORD] Client not ready, returning empty channels")
|
|
787
|
+
return channels
|
|
788
|
+
|
|
789
|
+
try:
|
|
790
|
+
# Get all monitored channels
|
|
791
|
+
logger.info(f"[DISCORD] Checking {len(self.discord_config.monitored_channel_ids)} monitored channels")
|
|
792
|
+
for channel_id in self.discord_config.monitored_channel_ids:
|
|
793
|
+
channel = self._channel_manager.client.get_channel(int(channel_id))
|
|
794
|
+
logger.info(f"[DISCORD] Channel {channel_id}: {'found' if channel else 'not found'}")
|
|
795
|
+
if channel:
|
|
796
|
+
channels.append(
|
|
797
|
+
DiscordChannelInfo(
|
|
798
|
+
channel_id=f"discord_{channel_id}",
|
|
799
|
+
channel_type="discord",
|
|
800
|
+
display_name=(
|
|
801
|
+
f"#{channel.name}" if hasattr(channel, "name") else f"Discord Channel {channel_id}"
|
|
802
|
+
),
|
|
803
|
+
is_active=True,
|
|
804
|
+
created_at=(
|
|
805
|
+
channel.created_at.isoformat()
|
|
806
|
+
if hasattr(channel, "created_at") and channel.created_at
|
|
807
|
+
else None
|
|
808
|
+
),
|
|
809
|
+
last_activity=None, # Could track this if needed
|
|
810
|
+
message_count=0, # Could track this if needed
|
|
811
|
+
)
|
|
812
|
+
)
|
|
813
|
+
|
|
814
|
+
# Add deferral channel if configured
|
|
815
|
+
if self.discord_config.deferral_channel_id:
|
|
816
|
+
channel = self._channel_manager.client.get_channel(int(self.discord_config.deferral_channel_id))
|
|
817
|
+
if channel and f"discord_{self.discord_config.deferral_channel_id}" not in [
|
|
818
|
+
ch.channel_id for ch in channels
|
|
819
|
+
]:
|
|
820
|
+
channels.append(
|
|
821
|
+
DiscordChannelInfo(
|
|
822
|
+
channel_id=f"discord_{self.discord_config.deferral_channel_id}",
|
|
823
|
+
channel_type="discord",
|
|
824
|
+
display_name=(
|
|
825
|
+
f"#{channel.name} (Deferrals)"
|
|
826
|
+
if hasattr(channel, "name")
|
|
827
|
+
else "Discord Deferral Channel"
|
|
828
|
+
),
|
|
829
|
+
is_active=True,
|
|
830
|
+
created_at=(
|
|
831
|
+
channel.created_at.isoformat()
|
|
832
|
+
if hasattr(channel, "created_at") and channel.created_at
|
|
833
|
+
else None
|
|
834
|
+
),
|
|
835
|
+
last_activity=None,
|
|
836
|
+
message_count=0,
|
|
837
|
+
)
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
except Exception as e:
|
|
841
|
+
logger.error(f"Error getting active channels: {e}", exc_info=True)
|
|
842
|
+
|
|
843
|
+
logger.info(f"[DISCORD] Returning {len(channels)} channels")
|
|
844
|
+
return channels
|
|
845
|
+
|
|
846
|
+
async def execute_tool(
|
|
847
|
+
self,
|
|
848
|
+
tool_name: str,
|
|
849
|
+
tool_args: Optional[Dict[str, Union[str, int, float, bool, List[Any], JSONDict]]] = None,
|
|
850
|
+
*,
|
|
851
|
+
parameters: Optional[Dict[str, Union[str, int, float, bool, List[Any], JSONDict]]] = None,
|
|
852
|
+
) -> ToolExecutionResult:
|
|
853
|
+
"""Execute a tool through the tool handler."""
|
|
854
|
+
|
|
855
|
+
# Support both tool_args and parameters for compatibility
|
|
856
|
+
from typing import cast
|
|
857
|
+
|
|
858
|
+
args = tool_args or parameters or {}
|
|
859
|
+
result = await self._tool_handler.execute_tool(tool_name, cast(JSONDict, args))
|
|
860
|
+
|
|
861
|
+
# Increment commands handled counter
|
|
862
|
+
self._commands_handled += 1
|
|
863
|
+
|
|
864
|
+
# Emit telemetry for tool execution
|
|
865
|
+
await self._emit_telemetry(
|
|
866
|
+
"discord.tool.executed",
|
|
867
|
+
1.0,
|
|
868
|
+
{
|
|
869
|
+
"adapter_type": "discord",
|
|
870
|
+
"tool_name": tool_name,
|
|
871
|
+
"success": str(result.success),
|
|
872
|
+
"status": result.status.value if hasattr(result.status, "value") else str(result.status),
|
|
873
|
+
},
|
|
874
|
+
)
|
|
875
|
+
|
|
876
|
+
return result
|
|
877
|
+
|
|
878
|
+
async def list_tools(self) -> List[str]:
|
|
879
|
+
"""List available tools through the tool handler."""
|
|
880
|
+
return self._tool_handler.get_available_tools()
|
|
881
|
+
|
|
882
|
+
async def list_permissions(self, wa_id: str) -> List[WAPermission]:
|
|
883
|
+
"""List all permissions for a Discord user."""
|
|
884
|
+
permissions: List[WAPermission] = []
|
|
885
|
+
|
|
886
|
+
try:
|
|
887
|
+
if not self._channel_manager.client:
|
|
888
|
+
return permissions
|
|
889
|
+
|
|
890
|
+
# Check all guilds
|
|
891
|
+
for guild in self._channel_manager.client.guilds:
|
|
892
|
+
member = guild.get_member(int(wa_id))
|
|
893
|
+
if member:
|
|
894
|
+
for role in member.roles:
|
|
895
|
+
if role.name.upper() in ["AUTHORITY", "OBSERVER"]:
|
|
896
|
+
permissions.append(
|
|
897
|
+
WAPermission(
|
|
898
|
+
permission_id=f"discord_{guild.id}_{role.name.upper()}_{wa_id}",
|
|
899
|
+
wa_id=wa_id,
|
|
900
|
+
permission_type="role",
|
|
901
|
+
permission_name=role.name.upper(),
|
|
902
|
+
resource=f"guild:{guild.id}",
|
|
903
|
+
granted_at=self._time_service.now() if self._time_service else datetime.now(),
|
|
904
|
+
granted_by="discord_adapter",
|
|
905
|
+
)
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
return permissions
|
|
909
|
+
except Exception as e:
|
|
910
|
+
logger.error(f"Failed to list permissions: {e}")
|
|
911
|
+
return []
|
|
912
|
+
|
|
913
|
+
async def send_deferral(self, deferral: DeferralRequest) -> str:
|
|
914
|
+
"""Send a decision deferral to human WAs - returns deferral ID."""
|
|
915
|
+
deferral_channel_id = self.discord_config.deferral_channel_id
|
|
916
|
+
if not deferral_channel_id:
|
|
917
|
+
logger.error("DiscordAdapter: Deferral channel not configured.")
|
|
918
|
+
logger.error(f" - Current config: {self.discord_config}")
|
|
919
|
+
logger.error(f" - Monitored channels: {self.discord_config.monitored_channel_ids}")
|
|
920
|
+
logger.error(f" - Admin user IDs: {self.discord_config.admin_user_ids}")
|
|
921
|
+
raise RuntimeError("Deferral channel not configured.")
|
|
922
|
+
|
|
923
|
+
logger.info(f"Sending deferral to channel {deferral_channel_id}")
|
|
924
|
+
logger.info(f" - Task ID: {deferral.task_id}")
|
|
925
|
+
logger.info(f" - Thought ID: {deferral.thought_id}")
|
|
926
|
+
logger.info(f" - Reason: {deferral.reason}")
|
|
927
|
+
|
|
928
|
+
time_service = self._time_service
|
|
929
|
+
if time_service is None:
|
|
930
|
+
logger.error("Time service not initialized")
|
|
931
|
+
raise RuntimeError("Time service not initialized")
|
|
932
|
+
start_time = time_service.now()
|
|
933
|
+
|
|
934
|
+
try:
|
|
935
|
+
correlation_id = str(uuid.uuid4())
|
|
936
|
+
|
|
937
|
+
# Create deferral data for embed formatter
|
|
938
|
+
deferral_data = DiscordGuidanceData(
|
|
939
|
+
deferral_id=correlation_id,
|
|
940
|
+
task_id=deferral.task_id,
|
|
941
|
+
thought_id=deferral.thought_id,
|
|
942
|
+
reason=deferral.reason,
|
|
943
|
+
defer_until=deferral.defer_until,
|
|
944
|
+
context=deferral.context or {},
|
|
945
|
+
)
|
|
946
|
+
|
|
947
|
+
# Create rich embed
|
|
948
|
+
embed = self._embed_formatter.format_deferral_request(deferral_data)
|
|
949
|
+
|
|
950
|
+
# Send the embed with a plain text notification
|
|
951
|
+
message_text = (
|
|
952
|
+
f"**DEFERRAL REQUEST (ID: {correlation_id})**\n"
|
|
953
|
+
f"Task ID: {deferral.task_id}\n"
|
|
954
|
+
f"Thought ID: {deferral.thought_id}\n"
|
|
955
|
+
f"Reason: {deferral.reason}"
|
|
956
|
+
)
|
|
957
|
+
if deferral.defer_until:
|
|
958
|
+
message_text += f"\nDefer Until: {deferral.defer_until}"
|
|
959
|
+
if deferral.context:
|
|
960
|
+
context_str = ", ".join(f"{k}: {v}" for k, v in deferral.context.items())
|
|
961
|
+
message_text += f"\nContext: {context_str}"
|
|
962
|
+
|
|
963
|
+
# Get the Discord client from channel manager
|
|
964
|
+
client = self._channel_manager.client
|
|
965
|
+
if not client:
|
|
966
|
+
raise RuntimeError("Discord client not available")
|
|
967
|
+
|
|
968
|
+
# Get the channel
|
|
969
|
+
channel = client.get_channel(int(deferral_channel_id))
|
|
970
|
+
if not channel:
|
|
971
|
+
raise RuntimeError(f"Deferral channel {deferral_channel_id} not found")
|
|
972
|
+
|
|
973
|
+
# Check if channel supports sending messages
|
|
974
|
+
if not isinstance(
|
|
975
|
+
channel,
|
|
976
|
+
(discord.TextChannel, discord.DMChannel, discord.Thread, discord.VoiceChannel, discord.StageChannel),
|
|
977
|
+
):
|
|
978
|
+
raise RuntimeError(f"Channel {deferral_channel_id} does not support sending messages")
|
|
979
|
+
|
|
980
|
+
# Split the message if needed using the message handler's method
|
|
981
|
+
chunks = self._message_handler._split_message(message_text, max_length=1900)
|
|
982
|
+
|
|
983
|
+
# Send the first chunk with the embed
|
|
984
|
+
if chunks:
|
|
985
|
+
sent_message = await channel.send(content=chunks[0], embed=embed)
|
|
986
|
+
|
|
987
|
+
# Send additional chunks if any (without embed)
|
|
988
|
+
for i in range(1, len(chunks)):
|
|
989
|
+
continuation = f"*(Continued from deferral {correlation_id})*\n\n{chunks[i]}"
|
|
990
|
+
await channel.send(content=continuation)
|
|
991
|
+
await asyncio.sleep(0.5) # Small delay between messages
|
|
992
|
+
else:
|
|
993
|
+
# Fallback if no chunks
|
|
994
|
+
sent_message = await channel.send(content="**DEFERRAL REQUEST** (content too long)", embed=embed)
|
|
995
|
+
|
|
996
|
+
# Add reaction UI for WAs to respond
|
|
997
|
+
await sent_message.add_reaction("✅") # Approve
|
|
998
|
+
await sent_message.add_reaction("❌") # Deny
|
|
999
|
+
await sent_message.add_reaction("🔄") # Request more info
|
|
1000
|
+
|
|
1001
|
+
# Store message ID for tracking responses
|
|
1002
|
+
if hasattr(self._reaction_handler, "track_deferral"):
|
|
1003
|
+
await self._reaction_handler.track_deferral(
|
|
1004
|
+
message_id=str(sent_message.id),
|
|
1005
|
+
deferral_id=correlation_id,
|
|
1006
|
+
task_id=deferral.task_id,
|
|
1007
|
+
thought_id=deferral.thought_id,
|
|
1008
|
+
)
|
|
1009
|
+
|
|
1010
|
+
# Store deferral in memory graph
|
|
1011
|
+
if self.bus_manager and self.bus_manager.memory:
|
|
1012
|
+
try:
|
|
1013
|
+
deferral_node = DiscordDeferralNode(
|
|
1014
|
+
id=f"discord_deferral/{correlation_id}",
|
|
1015
|
+
scope=GraphScope.LOCAL,
|
|
1016
|
+
attributes=GraphNodeAttributes(created_by="discord_adapter", tags=["discord", "deferral"]),
|
|
1017
|
+
deferral_id=correlation_id,
|
|
1018
|
+
task_id=deferral.task_id,
|
|
1019
|
+
thought_id=deferral.thought_id,
|
|
1020
|
+
reason=deferral.reason,
|
|
1021
|
+
defer_until=deferral.defer_until,
|
|
1022
|
+
channel_id=deferral_channel_id,
|
|
1023
|
+
status="pending",
|
|
1024
|
+
context=deferral.context,
|
|
1025
|
+
updated_at=start_time,
|
|
1026
|
+
updated_by="discord_adapter",
|
|
1027
|
+
)
|
|
1028
|
+
|
|
1029
|
+
await self.bus_manager.memory.store(
|
|
1030
|
+
node_id=correlation_id,
|
|
1031
|
+
node_type="DISCORD_DEFERRAL",
|
|
1032
|
+
attributes=deferral_node.to_graph_node().attributes,
|
|
1033
|
+
scope="local",
|
|
1034
|
+
handler_name="discord_adapter",
|
|
1035
|
+
)
|
|
1036
|
+
except Exception as e:
|
|
1037
|
+
logger.error(f"Failed to store deferral in memory: {e}")
|
|
1038
|
+
|
|
1039
|
+
end_time = time_service.now()
|
|
1040
|
+
execution_time_ms = (end_time - start_time).total_seconds() * 1000
|
|
1041
|
+
|
|
1042
|
+
persistence.add_correlation(
|
|
1043
|
+
ServiceCorrelation(
|
|
1044
|
+
correlation_id=correlation_id,
|
|
1045
|
+
service_type="discord",
|
|
1046
|
+
handler_name="DiscordAdapter",
|
|
1047
|
+
action_type="send_deferral",
|
|
1048
|
+
request_data=ServiceRequestData(
|
|
1049
|
+
service_type="discord",
|
|
1050
|
+
method_name="send_deferral",
|
|
1051
|
+
channel_id=deferral_channel_id,
|
|
1052
|
+
parameters={
|
|
1053
|
+
"reason": deferral.reason,
|
|
1054
|
+
"task_id": deferral.task_id,
|
|
1055
|
+
"thought_id": deferral.thought_id,
|
|
1056
|
+
},
|
|
1057
|
+
request_timestamp=start_time,
|
|
1058
|
+
),
|
|
1059
|
+
response_data=ServiceResponseData(
|
|
1060
|
+
success=True,
|
|
1061
|
+
result_summary=f"Deferral sent to channel {deferral_channel_id}",
|
|
1062
|
+
execution_time_ms=execution_time_ms,
|
|
1063
|
+
response_timestamp=end_time,
|
|
1064
|
+
),
|
|
1065
|
+
status=ServiceCorrelationStatus.COMPLETED,
|
|
1066
|
+
created_at=start_time,
|
|
1067
|
+
updated_at=end_time,
|
|
1068
|
+
timestamp=start_time,
|
|
1069
|
+
),
|
|
1070
|
+
time_service,
|
|
1071
|
+
)
|
|
1072
|
+
|
|
1073
|
+
# Increment commands handled counter for deferral requests
|
|
1074
|
+
self._commands_handled += 1
|
|
1075
|
+
|
|
1076
|
+
return correlation_id
|
|
1077
|
+
except Exception as e:
|
|
1078
|
+
self._errors_total += 1
|
|
1079
|
+
logger.exception(f"Failed to send deferral to Discord: {e}")
|
|
1080
|
+
raise
|
|
1081
|
+
|
|
1082
|
+
# Legacy method for backward compatibility
|
|
1083
|
+
async def send_deferral_legacy(self, context: DeferralContext) -> bool:
|
|
1084
|
+
"""Send a deferral report to the configured deferral channel (legacy)."""
|
|
1085
|
+
try:
|
|
1086
|
+
# Convert DeferralContext to DeferralRequest
|
|
1087
|
+
request = DeferralRequest(
|
|
1088
|
+
task_id=context.task_id,
|
|
1089
|
+
thought_id=context.thought_id,
|
|
1090
|
+
reason=context.reason,
|
|
1091
|
+
defer_until=context.defer_until
|
|
1092
|
+
or (self._time_service.now() if self._time_service else datetime.now()) + timedelta(hours=1),
|
|
1093
|
+
context=context.metadata,
|
|
1094
|
+
)
|
|
1095
|
+
await self.send_deferral(request)
|
|
1096
|
+
return True
|
|
1097
|
+
except Exception:
|
|
1098
|
+
return False
|
|
1099
|
+
|
|
1100
|
+
def get_capabilities(self) -> ServiceCapabilities:
|
|
1101
|
+
"""Return service capabilities in the proper format."""
|
|
1102
|
+
return ServiceCapabilities(
|
|
1103
|
+
service_name="DiscordAdapter",
|
|
1104
|
+
actions=[
|
|
1105
|
+
# Communication capabilities
|
|
1106
|
+
"send_message",
|
|
1107
|
+
"fetch_messages",
|
|
1108
|
+
# Tool capabilities
|
|
1109
|
+
"execute_tool",
|
|
1110
|
+
"list_tools",
|
|
1111
|
+
# WiseAuthority capabilities
|
|
1112
|
+
"fetch_guidance",
|
|
1113
|
+
"send_deferral",
|
|
1114
|
+
"check_authorization",
|
|
1115
|
+
"request_approval",
|
|
1116
|
+
"get_guidance",
|
|
1117
|
+
"get_pending_deferrals",
|
|
1118
|
+
"resolve_deferral",
|
|
1119
|
+
"grant_permission",
|
|
1120
|
+
"revoke_permission",
|
|
1121
|
+
"list_permissions",
|
|
1122
|
+
],
|
|
1123
|
+
version="1.0.0",
|
|
1124
|
+
dependencies=["discord.py"],
|
|
1125
|
+
)
|
|
1126
|
+
|
|
1127
|
+
def get_status(self) -> ServiceStatus:
|
|
1128
|
+
"""Return current service status."""
|
|
1129
|
+
try:
|
|
1130
|
+
# Check if client is ready without blocking
|
|
1131
|
+
is_healthy = self._channel_manager.client is not None and not self._channel_manager.client.is_closed()
|
|
1132
|
+
except Exception as e:
|
|
1133
|
+
logger.warning(
|
|
1134
|
+
f"Discord health check failed: {type(e).__name__}: {str(e)} - Client state unknown, latency check failed"
|
|
1135
|
+
)
|
|
1136
|
+
is_healthy = False
|
|
1137
|
+
|
|
1138
|
+
# Get actual latency from Discord client
|
|
1139
|
+
latency_ms = 0.0
|
|
1140
|
+
if self._message_handler and self._message_handler.client:
|
|
1141
|
+
# Discord.py provides latency in seconds, convert to milliseconds
|
|
1142
|
+
latency_seconds = self._message_handler.client.latency
|
|
1143
|
+
if latency_seconds is not None and latency_seconds >= 0:
|
|
1144
|
+
latency_ms = latency_seconds * 1000.0
|
|
1145
|
+
|
|
1146
|
+
return ServiceStatus(
|
|
1147
|
+
service_name="DiscordAdapter",
|
|
1148
|
+
service_type="adapter",
|
|
1149
|
+
is_healthy=is_healthy,
|
|
1150
|
+
uptime_seconds=(
|
|
1151
|
+
float((self._time_service.now() - self._start_time).total_seconds())
|
|
1152
|
+
if self._start_time and self._time_service
|
|
1153
|
+
else 0.0
|
|
1154
|
+
),
|
|
1155
|
+
metrics={"latency": latency_ms},
|
|
1156
|
+
)
|
|
1157
|
+
|
|
1158
|
+
def _get_time_service(self) -> TimeServiceProtocol:
|
|
1159
|
+
"""Get time service instance."""
|
|
1160
|
+
# Discord adapter already has _time_service set in __init__
|
|
1161
|
+
if self._time_service is None:
|
|
1162
|
+
raise RuntimeError("TimeService not available")
|
|
1163
|
+
return self._time_service
|
|
1164
|
+
|
|
1165
|
+
def _collect_metrics(self) -> Dict[str, float]:
|
|
1166
|
+
"""Collect base metrics for the Discord adapter."""
|
|
1167
|
+
uptime = 0.0
|
|
1168
|
+
if self._start_time:
|
|
1169
|
+
uptime = (self._get_time_service().now() - self._start_time).total_seconds()
|
|
1170
|
+
|
|
1171
|
+
is_running = self._channel_manager and self._channel_manager.client and self._channel_manager.client.is_ready()
|
|
1172
|
+
|
|
1173
|
+
return {
|
|
1174
|
+
"healthy": True if is_running else False,
|
|
1175
|
+
"uptime_seconds": uptime,
|
|
1176
|
+
"request_count": float(self._messages_processed),
|
|
1177
|
+
"error_count": float(self._errors_total),
|
|
1178
|
+
"error_rate": float(self._errors_total) / max(1, self._messages_processed),
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
def get_metrics(self) -> Dict[str, float]:
|
|
1182
|
+
"""Get all metrics including base, custom, and v1.4.3 specific."""
|
|
1183
|
+
# Get all base + custom metrics
|
|
1184
|
+
metrics = self._collect_metrics()
|
|
1185
|
+
|
|
1186
|
+
# Add v1.4.3 specific metrics
|
|
1187
|
+
# Get active guild count from client if available
|
|
1188
|
+
guilds_active = 0.0
|
|
1189
|
+
if self._channel_manager and self._channel_manager.client and self._channel_manager.client.is_ready():
|
|
1190
|
+
try:
|
|
1191
|
+
guilds_active = float(len(self._channel_manager.client.guilds))
|
|
1192
|
+
except Exception:
|
|
1193
|
+
guilds_active = 0.0
|
|
1194
|
+
|
|
1195
|
+
metrics.update(
|
|
1196
|
+
{
|
|
1197
|
+
"discord_messages_processed": float(self._messages_processed),
|
|
1198
|
+
"discord_commands_handled": float(self._commands_handled),
|
|
1199
|
+
"discord_errors_total": float(self._errors_total),
|
|
1200
|
+
"discord_guilds_active": guilds_active,
|
|
1201
|
+
}
|
|
1202
|
+
)
|
|
1203
|
+
|
|
1204
|
+
return metrics
|
|
1205
|
+
|
|
1206
|
+
async def _send_output(self, channel_id: str, content: str) -> None:
|
|
1207
|
+
"""Send output to a Discord channel with retry logic"""
|
|
1208
|
+
await self._retry_discord_operation(
|
|
1209
|
+
self._message_handler.send_message_to_channel,
|
|
1210
|
+
channel_id,
|
|
1211
|
+
content,
|
|
1212
|
+
operation_name="send_output",
|
|
1213
|
+
config_key="discord_api",
|
|
1214
|
+
)
|
|
1215
|
+
# result contains the return value from send_message_to_channel
|
|
1216
|
+
|
|
1217
|
+
async def _on_message(self, message: discord.Message) -> None:
|
|
1218
|
+
"""Handle incoming Discord messages."""
|
|
1219
|
+
await self._channel_manager.on_message(message)
|
|
1220
|
+
|
|
1221
|
+
# Increment message processed counter
|
|
1222
|
+
self._messages_processed += 1
|
|
1223
|
+
|
|
1224
|
+
# Emit telemetry for message received
|
|
1225
|
+
await self._emit_telemetry(
|
|
1226
|
+
"discord.message.received",
|
|
1227
|
+
1.0,
|
|
1228
|
+
{"adapter_type": "discord", "channel_id": str(message.channel.id), "author_id": str(message.author.id)},
|
|
1229
|
+
)
|
|
1230
|
+
|
|
1231
|
+
# Audit log the message received
|
|
1232
|
+
await self._audit_logger.log_message_received(
|
|
1233
|
+
channel_id=str(message.channel.id),
|
|
1234
|
+
author_id=str(message.author.id),
|
|
1235
|
+
author_name=message.author.name,
|
|
1236
|
+
message_id=str(message.id),
|
|
1237
|
+
)
|
|
1238
|
+
|
|
1239
|
+
def attach_to_client(self, client: discord.Client) -> None:
|
|
1240
|
+
"""Attach message handlers to a Discord client."""
|
|
1241
|
+
logger.info("DiscordAdapter.attach_to_client: Attaching to Discord client")
|
|
1242
|
+
self._channel_manager.set_client(client)
|
|
1243
|
+
self._message_handler.set_client(client)
|
|
1244
|
+
self._guidance_handler.set_client(client)
|
|
1245
|
+
self._reaction_handler.set_client(client)
|
|
1246
|
+
self._tool_handler.set_client(client)
|
|
1247
|
+
|
|
1248
|
+
# Note: Event handlers are now managed by CIRISDiscordClient
|
|
1249
|
+
self._connection_manager.set_client(client)
|
|
1250
|
+
logger.info("DiscordAdapter.attach_to_client: All handlers attached")
|
|
1251
|
+
|
|
1252
|
+
async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent) -> None:
|
|
1253
|
+
"""Handle raw reaction add events.
|
|
1254
|
+
|
|
1255
|
+
Args:
|
|
1256
|
+
payload: Discord reaction event payload
|
|
1257
|
+
"""
|
|
1258
|
+
# Don't process reactions from bots
|
|
1259
|
+
if payload.member and payload.member.bot:
|
|
1260
|
+
return
|
|
1261
|
+
|
|
1262
|
+
await self._reaction_handler.handle_reaction(payload)
|
|
1263
|
+
|
|
1264
|
+
async def start(self) -> None:
|
|
1265
|
+
"""
|
|
1266
|
+
Start the Discord adapter.
|
|
1267
|
+
Note: This doesn't start the Discord client connection - that's handled by the runtime.
|
|
1268
|
+
"""
|
|
1269
|
+
try:
|
|
1270
|
+
# Capture start time
|
|
1271
|
+
self._start_time = self._time_service.now() if self._time_service else datetime.now(timezone.utc)
|
|
1272
|
+
|
|
1273
|
+
# Emit telemetry for adapter start
|
|
1274
|
+
await self._emit_telemetry("discord.adapter.starting", 1.0, {"adapter_type": "discord"})
|
|
1275
|
+
|
|
1276
|
+
await super().start()
|
|
1277
|
+
|
|
1278
|
+
# Set up audit service if available
|
|
1279
|
+
if self.bus_manager and hasattr(self.bus_manager, "audit"):
|
|
1280
|
+
# Try to get audit service from bus manager
|
|
1281
|
+
try:
|
|
1282
|
+
audit_service = self.bus_manager.audit
|
|
1283
|
+
if audit_service:
|
|
1284
|
+
self._audit_logger.set_audit_service(audit_service)
|
|
1285
|
+
logger.info("Discord adapter connected to audit service")
|
|
1286
|
+
except Exception as e:
|
|
1287
|
+
logger.debug(f"Could not connect to audit service: {e}")
|
|
1288
|
+
|
|
1289
|
+
client = self._channel_manager.client
|
|
1290
|
+
if client:
|
|
1291
|
+
logger.info("Discord adapter started with existing client (not yet connected)")
|
|
1292
|
+
else:
|
|
1293
|
+
logger.warning("Discord adapter started without client - attach_to_client() must be called separately")
|
|
1294
|
+
|
|
1295
|
+
logger.info("Discord adapter started successfully")
|
|
1296
|
+
|
|
1297
|
+
# Emit telemetry for successful start
|
|
1298
|
+
await self._emit_telemetry(
|
|
1299
|
+
"discord.adapter.started", 1.0, {"adapter_type": "discord", "has_client": str(client is not None)}
|
|
1300
|
+
)
|
|
1301
|
+
|
|
1302
|
+
# Set up connection monitoring
|
|
1303
|
+
if client:
|
|
1304
|
+
logger.info("Discord adapter setting up connection monitoring")
|
|
1305
|
+
await self._connection_manager.connect()
|
|
1306
|
+
logger.info("Discord adapter will wait for platform to establish connection")
|
|
1307
|
+
else:
|
|
1308
|
+
logger.warning("No Discord client attached - connection will be established later")
|
|
1309
|
+
|
|
1310
|
+
except Exception as e:
|
|
1311
|
+
logger.exception(f"Failed to start Discord adapter: {e}")
|
|
1312
|
+
raise
|
|
1313
|
+
|
|
1314
|
+
async def wait_until_ready(self, timeout: float = 30.0) -> bool:
|
|
1315
|
+
"""
|
|
1316
|
+
Wait until the Discord client is ready or timeout.
|
|
1317
|
+
|
|
1318
|
+
Args:
|
|
1319
|
+
timeout: Maximum time to wait in seconds
|
|
1320
|
+
|
|
1321
|
+
Returns:
|
|
1322
|
+
True if ready, False if timeout
|
|
1323
|
+
"""
|
|
1324
|
+
logger.info(f"Waiting for Discord adapter to be ready (timeout: {timeout}s)...")
|
|
1325
|
+
return await self._connection_manager.wait_until_ready(timeout)
|
|
1326
|
+
|
|
1327
|
+
async def stop(self) -> None:
|
|
1328
|
+
"""
|
|
1329
|
+
Stop the Discord adapter and clean up resources.
|
|
1330
|
+
"""
|
|
1331
|
+
try:
|
|
1332
|
+
logger.info("Stopping Discord adapter...")
|
|
1333
|
+
|
|
1334
|
+
# Emit telemetry for adapter stopping
|
|
1335
|
+
await self._emit_telemetry("discord.adapter.stopping", 1.0, {"adapter_type": "discord"})
|
|
1336
|
+
|
|
1337
|
+
self._tool_handler.clear_tool_results()
|
|
1338
|
+
|
|
1339
|
+
# Disconnect gracefully
|
|
1340
|
+
await self._connection_manager.disconnect()
|
|
1341
|
+
|
|
1342
|
+
await super().stop()
|
|
1343
|
+
|
|
1344
|
+
logger.info("Discord adapter stopped successfully")
|
|
1345
|
+
|
|
1346
|
+
# Emit telemetry for successful stop
|
|
1347
|
+
await self._emit_telemetry("discord.adapter.stopped", 1.0, {"adapter_type": "discord"})
|
|
1348
|
+
except AttributeError as e:
|
|
1349
|
+
# Handle the '_MissingSentinel' error that occurs during shutdown
|
|
1350
|
+
if "'_MissingSentinel' object has no attribute 'create_task'" in str(e):
|
|
1351
|
+
logger.debug("Discord client already shut down, ignoring event loop error")
|
|
1352
|
+
else:
|
|
1353
|
+
logger.error(f"AttributeError stopping Discord adapter: {e}")
|
|
1354
|
+
except Exception as e:
|
|
1355
|
+
logger.error(f"Error stopping Discord adapter: {e}")
|
|
1356
|
+
|
|
1357
|
+
async def is_healthy(self) -> bool:
|
|
1358
|
+
"""Check if the Discord adapter is healthy"""
|
|
1359
|
+
try:
|
|
1360
|
+
result = self._connection_manager.is_connected()
|
|
1361
|
+
logger.debug(f"DiscordAdapter.is_healthy: connection_manager.is_connected() returned {result}")
|
|
1362
|
+
return result
|
|
1363
|
+
except Exception as e:
|
|
1364
|
+
logger.warning(f"DiscordAdapter.is_healthy: Exception checking health: {e}")
|
|
1365
|
+
return False
|
|
1366
|
+
|
|
1367
|
+
def get_service_type(self) -> ServiceType:
|
|
1368
|
+
"""Get the type of this service."""
|
|
1369
|
+
return ServiceType.ADAPTER
|
|
1370
|
+
|
|
1371
|
+
def get_home_channel_id(self) -> Optional[str]:
|
|
1372
|
+
"""Get the home channel ID for this Discord adapter.
|
|
1373
|
+
|
|
1374
|
+
Returns:
|
|
1375
|
+
The formatted channel ID (e.g., 'discord_123456789')
|
|
1376
|
+
or None if no home channel is configured.
|
|
1377
|
+
"""
|
|
1378
|
+
# Get the raw channel ID from config
|
|
1379
|
+
raw_channel_id = self.discord_config.get_home_channel_id()
|
|
1380
|
+
logger.debug(f"DiscordAdapter.get_home_channel_id: raw_channel_id = {raw_channel_id}")
|
|
1381
|
+
if not raw_channel_id:
|
|
1382
|
+
logger.warning("DiscordAdapter: No home channel ID found in config")
|
|
1383
|
+
return None
|
|
1384
|
+
|
|
1385
|
+
# Format it with discord_ prefix
|
|
1386
|
+
# The guild ID will be added by the platform when available
|
|
1387
|
+
formatted_id = self.discord_config.get_formatted_startup_channel_id()
|
|
1388
|
+
logger.debug(f"DiscordAdapter.get_home_channel_id: formatted_id = {formatted_id}")
|
|
1389
|
+
return formatted_id
|
|
1390
|
+
|
|
1391
|
+
def get_channel_list(self) -> List[ChannelContext]:
|
|
1392
|
+
"""
|
|
1393
|
+
Get list of available Discord channels.
|
|
1394
|
+
|
|
1395
|
+
Returns:
|
|
1396
|
+
List of ChannelContext objects for Discord channels.
|
|
1397
|
+
"""
|
|
1398
|
+
channels: List[ChannelContext] = []
|
|
1399
|
+
|
|
1400
|
+
# Add configured channels from config
|
|
1401
|
+
if self.discord_config:
|
|
1402
|
+
# Add monitored channels
|
|
1403
|
+
for channel_id in self.discord_config.monitored_channel_ids:
|
|
1404
|
+
# Determine allowed actions based on channel type
|
|
1405
|
+
allowed_actions = ["speak", "observe", "memorize", "recall"]
|
|
1406
|
+
if channel_id == self.discord_config.home_channel_id:
|
|
1407
|
+
allowed_actions.append("wa_defer") # Home channel can defer to WA
|
|
1408
|
+
if channel_id == self.discord_config.deferral_channel_id:
|
|
1409
|
+
allowed_actions.append("wa_approve") # Deferral channel for WA approvals
|
|
1410
|
+
|
|
1411
|
+
channel_name = None
|
|
1412
|
+
# If bot is connected, get actual channel name
|
|
1413
|
+
if self._channel_manager and self._channel_manager.client and self._channel_manager.client.is_ready():
|
|
1414
|
+
try:
|
|
1415
|
+
discord_channel = self._channel_manager.client.get_channel(int(channel_id))
|
|
1416
|
+
if discord_channel and hasattr(discord_channel, "name"):
|
|
1417
|
+
channel_name = f"#{discord_channel.name}"
|
|
1418
|
+
except Exception as e:
|
|
1419
|
+
logger.debug(f"Could not get Discord channel info for {channel_id}: {e}")
|
|
1420
|
+
|
|
1421
|
+
channel = ChannelContext(
|
|
1422
|
+
channel_id=channel_id,
|
|
1423
|
+
channel_type="discord",
|
|
1424
|
+
created_at=datetime.now(), # We don't have creation time, use now
|
|
1425
|
+
channel_name=channel_name,
|
|
1426
|
+
is_private=False, # Discord channels are generally not private
|
|
1427
|
+
participants=[], # Could be populated from guild members if needed
|
|
1428
|
+
is_active=True,
|
|
1429
|
+
last_activity=None, # Could be populated from correlations
|
|
1430
|
+
message_count=0, # Could be populated from correlations
|
|
1431
|
+
allowed_actions=allowed_actions,
|
|
1432
|
+
moderation_level="standard",
|
|
1433
|
+
)
|
|
1434
|
+
channels.append(channel)
|
|
1435
|
+
|
|
1436
|
+
# Add deferral channel if not already in monitored
|
|
1437
|
+
if (
|
|
1438
|
+
self.discord_config.deferral_channel_id
|
|
1439
|
+
and self.discord_config.deferral_channel_id not in self.discord_config.monitored_channel_ids
|
|
1440
|
+
):
|
|
1441
|
+
channel_name = None
|
|
1442
|
+
if self._channel_manager and self._channel_manager.client and self._channel_manager.client.is_ready():
|
|
1443
|
+
try:
|
|
1444
|
+
discord_channel = self._channel_manager.client.get_channel(
|
|
1445
|
+
int(self.discord_config.deferral_channel_id)
|
|
1446
|
+
)
|
|
1447
|
+
if discord_channel and hasattr(discord_channel, "name"):
|
|
1448
|
+
channel_name = f"#{discord_channel.name}"
|
|
1449
|
+
except Exception as e:
|
|
1450
|
+
logger.debug(f"Could not get Discord channel info for deferral channel: {e}")
|
|
1451
|
+
|
|
1452
|
+
channels.append(
|
|
1453
|
+
ChannelContext(
|
|
1454
|
+
channel_id=self.discord_config.deferral_channel_id,
|
|
1455
|
+
channel_type="discord",
|
|
1456
|
+
created_at=datetime.now(),
|
|
1457
|
+
channel_name=channel_name,
|
|
1458
|
+
is_private=False,
|
|
1459
|
+
participants=[],
|
|
1460
|
+
is_active=True,
|
|
1461
|
+
last_activity=None,
|
|
1462
|
+
message_count=0,
|
|
1463
|
+
allowed_actions=["wa_approve", "speak", "observe"], # Deferral channel
|
|
1464
|
+
moderation_level="strict", # Higher moderation for deferral channel
|
|
1465
|
+
)
|
|
1466
|
+
)
|
|
1467
|
+
|
|
1468
|
+
return channels
|
|
1469
|
+
|
|
1470
|
+
def _setup_connection_callbacks(self) -> None:
|
|
1471
|
+
"""Set up callbacks for connection events."""
|
|
1472
|
+
|
|
1473
|
+
async def on_connected() -> None:
|
|
1474
|
+
"""Handle successful connection."""
|
|
1475
|
+
try:
|
|
1476
|
+
# Log connection event
|
|
1477
|
+
if self._connection_manager.client:
|
|
1478
|
+
guild_count = len(self._connection_manager.client.guilds)
|
|
1479
|
+
user_count = len(self._connection_manager.client.users)
|
|
1480
|
+
|
|
1481
|
+
await self._audit_logger.log_connection_event(
|
|
1482
|
+
event_type="connected", guild_count=guild_count, user_count=user_count
|
|
1483
|
+
)
|
|
1484
|
+
|
|
1485
|
+
await self._emit_telemetry(
|
|
1486
|
+
"discord.connection.established",
|
|
1487
|
+
1.0,
|
|
1488
|
+
{"adapter_type": "discord", "guilds": str(guild_count), "users": str(user_count)},
|
|
1489
|
+
)
|
|
1490
|
+
except Exception as e:
|
|
1491
|
+
logger.error(f"Error in connection callback: {e}")
|
|
1492
|
+
|
|
1493
|
+
async def on_disconnected(error: Optional[Exception]) -> None:
|
|
1494
|
+
"""Handle disconnection."""
|
|
1495
|
+
try:
|
|
1496
|
+
await self._audit_logger.log_connection_event(
|
|
1497
|
+
event_type="disconnected", guild_count=0, user_count=0, error=str(error) if error else None
|
|
1498
|
+
)
|
|
1499
|
+
|
|
1500
|
+
await self._emit_telemetry(
|
|
1501
|
+
"discord.connection.lost",
|
|
1502
|
+
1.0,
|
|
1503
|
+
{"adapter_type": "discord", "error": str(error) if error else "clean_disconnect"},
|
|
1504
|
+
)
|
|
1505
|
+
except Exception as e:
|
|
1506
|
+
logger.error(f"Error in disconnection callback: {e}")
|
|
1507
|
+
|
|
1508
|
+
async def on_reconnecting(attempt: int) -> None:
|
|
1509
|
+
"""Handle reconnection attempts."""
|
|
1510
|
+
try:
|
|
1511
|
+
await self._emit_telemetry(
|
|
1512
|
+
"discord.connection.reconnecting",
|
|
1513
|
+
1.0,
|
|
1514
|
+
{
|
|
1515
|
+
"adapter_type": "discord",
|
|
1516
|
+
"attempt": str(attempt),
|
|
1517
|
+
"max_attempts": str(self._connection_manager.max_reconnect_attempts),
|
|
1518
|
+
},
|
|
1519
|
+
)
|
|
1520
|
+
except Exception as e:
|
|
1521
|
+
logger.error(f"Error in reconnecting callback: {e}")
|
|
1522
|
+
|
|
1523
|
+
async def on_failed(reason: str) -> None:
|
|
1524
|
+
"""Handle connection failure."""
|
|
1525
|
+
try:
|
|
1526
|
+
await self._audit_logger.log_connection_event(
|
|
1527
|
+
event_type="failed", guild_count=0, user_count=0, error=reason
|
|
1528
|
+
)
|
|
1529
|
+
|
|
1530
|
+
await self._emit_telemetry(
|
|
1531
|
+
"discord.connection.failed", 1.0, {"adapter_type": "discord", "reason": reason}
|
|
1532
|
+
)
|
|
1533
|
+
except Exception as e:
|
|
1534
|
+
logger.error(f"Error in failure callback: {e}")
|
|
1535
|
+
|
|
1536
|
+
# Set callbacks
|
|
1537
|
+
self._connection_manager.on_connected = on_connected
|
|
1538
|
+
self._connection_manager.on_disconnected = on_disconnected
|
|
1539
|
+
self._connection_manager.on_reconnecting = on_reconnecting
|
|
1540
|
+
self._connection_manager.on_failed = on_failed
|
|
1541
|
+
|
|
1542
|
+
@property
|
|
1543
|
+
def _client(self) -> Optional[discord.Client]:
|
|
1544
|
+
"""Get the Discord client instance."""
|
|
1545
|
+
return self._channel_manager.client
|
|
1546
|
+
|
|
1547
|
+
def get_services_to_register(self) -> List["AdapterServiceRegistration"]:
|
|
1548
|
+
"""Register Discord services for communication, tools, and wise authority."""
|
|
1549
|
+
from ciris_engine.logic.registries.base import Priority
|
|
1550
|
+
from ciris_engine.schemas.adapters.registration import AdapterServiceRegistration
|
|
1551
|
+
from ciris_engine.schemas.runtime.enums import ServiceType
|
|
1552
|
+
|
|
1553
|
+
registrations = [
|
|
1554
|
+
AdapterServiceRegistration(
|
|
1555
|
+
service_type=ServiceType.COMMUNICATION,
|
|
1556
|
+
provider=self, # The Discord adapter itself is the provider
|
|
1557
|
+
priority=Priority.HIGH,
|
|
1558
|
+
handlers=["SpeakHandler", "ObserveHandler"], # Specific handlers
|
|
1559
|
+
capabilities=["send_message", "fetch_messages"],
|
|
1560
|
+
),
|
|
1561
|
+
AdapterServiceRegistration(
|
|
1562
|
+
service_type=ServiceType.TOOL,
|
|
1563
|
+
provider=self, # Discord adapter handles tools too
|
|
1564
|
+
priority=Priority.NORMAL, # Lower priority than CLI for tools
|
|
1565
|
+
handlers=["ToolHandler"],
|
|
1566
|
+
capabilities=["execute_tool", "get_available_tools", "get_tool_result", "validate_parameters"],
|
|
1567
|
+
),
|
|
1568
|
+
AdapterServiceRegistration(
|
|
1569
|
+
service_type=ServiceType.WISE_AUTHORITY,
|
|
1570
|
+
provider=self, # Discord adapter can handle WA
|
|
1571
|
+
priority=Priority.HIGH,
|
|
1572
|
+
handlers=["DeferralHandler", "GuidanceHandler"],
|
|
1573
|
+
capabilities=[
|
|
1574
|
+
"send_deferral",
|
|
1575
|
+
"check_deferral",
|
|
1576
|
+
"fetch_guidance",
|
|
1577
|
+
"request_permission",
|
|
1578
|
+
"check_permission",
|
|
1579
|
+
"list_permissions",
|
|
1580
|
+
],
|
|
1581
|
+
),
|
|
1582
|
+
]
|
|
1583
|
+
|
|
1584
|
+
return registrations
|