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,1256 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Consent Service - FAIL FAST, FAIL LOUD, NO FAKE DATA.
|
|
3
|
+
|
|
4
|
+
Governance Service #5: Manages user consent for the Consensual Evolution Protocol.
|
|
5
|
+
Default: TEMPORARY (14 days) unless explicitly changed.
|
|
6
|
+
This is the 22nd core CIRIS service.
|
|
7
|
+
|
|
8
|
+
REFACTORED: Now uses modular architecture with separate modules for:
|
|
9
|
+
- exceptions: Custom errors
|
|
10
|
+
- metrics: Metrics collection
|
|
11
|
+
- partnership: Partnership management
|
|
12
|
+
- decay: Decay protocol
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
from datetime import datetime, timedelta, timezone
|
|
17
|
+
from typing import Any, Dict, List, Optional
|
|
18
|
+
from uuid import uuid4
|
|
19
|
+
|
|
20
|
+
from ciris_engine.logic.buses.memory_bus import MemoryBus
|
|
21
|
+
from ciris_engine.logic.persistence import add_graph_node, get_graph_node
|
|
22
|
+
from ciris_engine.logic.services.base_service import BaseService
|
|
23
|
+
from ciris_engine.logic.utils.jsondict_helpers import get_dict, get_float, get_list, get_str, get_str_optional
|
|
24
|
+
from ciris_engine.protocols.consent import ConsentManagerProtocol
|
|
25
|
+
from ciris_engine.protocols.services import ToolService
|
|
26
|
+
from ciris_engine.protocols.services.lifecycle.time import TimeServiceProtocol
|
|
27
|
+
from ciris_engine.schemas.adapters.tools import ToolExecutionResult, ToolExecutionStatus, ToolInfo, ToolParameterSchema
|
|
28
|
+
from ciris_engine.schemas.consent.core import (
|
|
29
|
+
ConsentAuditEntry,
|
|
30
|
+
ConsentCategory,
|
|
31
|
+
ConsentDecayStatus,
|
|
32
|
+
ConsentImpactReport,
|
|
33
|
+
ConsentRequest,
|
|
34
|
+
ConsentStatus,
|
|
35
|
+
ConsentStream,
|
|
36
|
+
)
|
|
37
|
+
from ciris_engine.schemas.runtime.enums import ServiceType
|
|
38
|
+
from ciris_engine.schemas.services.core import ServiceCapabilities
|
|
39
|
+
from ciris_engine.schemas.services.graph.memory import MemorySearchFilter
|
|
40
|
+
from ciris_engine.schemas.services.graph_core import GraphNode, GraphScope, NodeType
|
|
41
|
+
from ciris_engine.schemas.types import JSONDict
|
|
42
|
+
|
|
43
|
+
# Import from new modules
|
|
44
|
+
from .air import ArtificialInteractionReminder
|
|
45
|
+
from .decay import DecayProtocolManager
|
|
46
|
+
from .exceptions import ConsentNotFoundError, ConsentValidationError
|
|
47
|
+
from .metrics import ConsentMetricsCollector
|
|
48
|
+
from .partnership import PartnershipManager
|
|
49
|
+
|
|
50
|
+
logger = logging.getLogger(__name__)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ConsentService(BaseService, ConsentManagerProtocol, ToolService):
|
|
54
|
+
"""
|
|
55
|
+
Consent Service - 22nd Core CIRIS Service (Governance #5).
|
|
56
|
+
|
|
57
|
+
Manages user consent with HARD GUARANTEES:
|
|
58
|
+
- TEMPORARY by default (14 days)
|
|
59
|
+
- No fake data or fallbacks
|
|
60
|
+
- Immutable audit trail
|
|
61
|
+
- Real decay protocol
|
|
62
|
+
- Bilateral agreement for PARTNERED
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
time_service: TimeServiceProtocol,
|
|
68
|
+
memory_bus: Optional[MemoryBus] = None,
|
|
69
|
+
db_path: Optional[str] = None,
|
|
70
|
+
):
|
|
71
|
+
super().__init__(time_service=time_service, service_name="ConsentService")
|
|
72
|
+
self._time_service = time_service
|
|
73
|
+
self._memory_bus = memory_bus
|
|
74
|
+
self._db_path = db_path
|
|
75
|
+
|
|
76
|
+
# Initialize modular managers
|
|
77
|
+
self._metrics_collector = ConsentMetricsCollector()
|
|
78
|
+
self._partnership_manager = PartnershipManager(time_service=time_service)
|
|
79
|
+
self._decay_manager = DecayProtocolManager(time_service=time_service, db_path=db_path)
|
|
80
|
+
self._air_manager = ArtificialInteractionReminder(
|
|
81
|
+
time_service=time_service,
|
|
82
|
+
time_threshold_minutes=30, # 30 minutes
|
|
83
|
+
message_threshold=20, # 20 messages
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Cache for active consents (NOT source of truth)
|
|
87
|
+
self._consent_cache: Dict[str, ConsentStatus] = {}
|
|
88
|
+
|
|
89
|
+
# Core Metrics (real, no fake data)
|
|
90
|
+
self._consent_checks = 0
|
|
91
|
+
self._consent_grants = 0
|
|
92
|
+
self._consent_revokes = 0
|
|
93
|
+
self._tool_executions = 0
|
|
94
|
+
self._tool_failures = 0
|
|
95
|
+
self._expired_cleanups = 0
|
|
96
|
+
|
|
97
|
+
# Downgrade tracking
|
|
98
|
+
self._downgrades_completed = 0
|
|
99
|
+
|
|
100
|
+
def _now(self) -> datetime:
|
|
101
|
+
"""Get current time from time service."""
|
|
102
|
+
if self._time_service is None:
|
|
103
|
+
return datetime.now(timezone.utc)
|
|
104
|
+
return self._time_service.now()
|
|
105
|
+
|
|
106
|
+
async def _extend_temporary_expiry(self, user_id: str, status: ConsentStatus) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Reset TEMPORARY expiry to 2 weeks from now on every interaction.
|
|
109
|
+
This implements the "2 weeks without seeing them before decay" rule.
|
|
110
|
+
"""
|
|
111
|
+
now = self._now()
|
|
112
|
+
new_expiry = now + timedelta(days=14)
|
|
113
|
+
|
|
114
|
+
# Only update if expiry changed significantly (avoid unnecessary writes)
|
|
115
|
+
if status.expires_at and (new_expiry - status.expires_at).total_seconds() < 3600:
|
|
116
|
+
# Less than 1 hour difference, skip update to reduce I/O
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
# Update status with new expiry
|
|
120
|
+
status.expires_at = new_expiry
|
|
121
|
+
status.last_modified = now
|
|
122
|
+
|
|
123
|
+
# Persist to graph
|
|
124
|
+
node = GraphNode(
|
|
125
|
+
id=f"consent/{user_id}",
|
|
126
|
+
type=NodeType.CONSENT,
|
|
127
|
+
scope=GraphScope.LOCAL,
|
|
128
|
+
attributes={
|
|
129
|
+
"stream": status.stream,
|
|
130
|
+
"categories": list(status.categories),
|
|
131
|
+
"granted_at": status.granted_at.isoformat(),
|
|
132
|
+
"expires_at": new_expiry.isoformat(),
|
|
133
|
+
"last_modified": now.isoformat(),
|
|
134
|
+
"impact_score": status.impact_score,
|
|
135
|
+
"attribution_count": status.attribution_count,
|
|
136
|
+
},
|
|
137
|
+
updated_by="consent_manager",
|
|
138
|
+
updated_at=now,
|
|
139
|
+
)
|
|
140
|
+
if self._time_service is None:
|
|
141
|
+
raise ValueError("TimeService required for extending expiry")
|
|
142
|
+
add_graph_node(node, self._time_service, self._db_path)
|
|
143
|
+
|
|
144
|
+
# Update cache
|
|
145
|
+
self._consent_cache[user_id] = status
|
|
146
|
+
|
|
147
|
+
logger.debug(f"Extended TEMPORARY expiry for {user_id} to {new_expiry.isoformat()}")
|
|
148
|
+
|
|
149
|
+
def _check_cached_expiry(self, user_id: str, cached: ConsentStatus) -> None:
|
|
150
|
+
"""Check if cached consent has expired and remove if so."""
|
|
151
|
+
if cached.stream == ConsentStream.TEMPORARY and cached.expires_at:
|
|
152
|
+
if self._now() > cached.expires_at:
|
|
153
|
+
del self._consent_cache[user_id]
|
|
154
|
+
raise ConsentNotFoundError(f"Consent for {user_id} has expired")
|
|
155
|
+
|
|
156
|
+
def _reconstruct_consent_from_node(self, user_id: str, node: Any) -> ConsentStatus:
|
|
157
|
+
"""Reconstruct ConsentStatus from graph node."""
|
|
158
|
+
attrs = node.attributes if isinstance(node.attributes, dict) else node.attributes.model_dump()
|
|
159
|
+
|
|
160
|
+
# Extract expires_at separately to avoid walrus operator in argument list
|
|
161
|
+
expires_str = get_str_optional(attrs, "expires_at")
|
|
162
|
+
expires_at = datetime.fromisoformat(expires_str) if expires_str else None
|
|
163
|
+
|
|
164
|
+
return ConsentStatus(
|
|
165
|
+
user_id=user_id,
|
|
166
|
+
stream=ConsentStream(get_str(attrs, "stream", "temporary")),
|
|
167
|
+
categories=[ConsentCategory(c) for c in get_list(attrs, "categories", [])],
|
|
168
|
+
granted_at=datetime.fromisoformat(get_str(attrs, "granted_at", datetime.now(timezone.utc).isoformat())),
|
|
169
|
+
expires_at=expires_at,
|
|
170
|
+
last_modified=datetime.fromisoformat(
|
|
171
|
+
get_str(attrs, "last_modified", datetime.now(timezone.utc).isoformat())
|
|
172
|
+
),
|
|
173
|
+
impact_score=get_float(attrs, "impact_score", 0.0),
|
|
174
|
+
attribution_count=int(get_float(attrs, "attribution_count", 0)),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
async def _load_consent_from_graph(self, user_id: str) -> ConsentStatus:
|
|
178
|
+
"""Load consent from graph storage."""
|
|
179
|
+
node = get_graph_node(f"consent/{user_id}", GraphScope.LOCAL, self._db_path)
|
|
180
|
+
if not node:
|
|
181
|
+
raise ConsentNotFoundError(f"No consent found for user {user_id}")
|
|
182
|
+
|
|
183
|
+
status = self._reconstruct_consent_from_node(user_id, node)
|
|
184
|
+
|
|
185
|
+
# Check expiry
|
|
186
|
+
if status.stream == ConsentStream.TEMPORARY and status.expires_at:
|
|
187
|
+
if self._now() > status.expires_at:
|
|
188
|
+
raise ConsentNotFoundError(f"Consent for {user_id} has expired")
|
|
189
|
+
|
|
190
|
+
# Update cache
|
|
191
|
+
self._consent_cache[user_id] = status
|
|
192
|
+
return status
|
|
193
|
+
|
|
194
|
+
async def get_consent(self, user_id: str, extend_expiry: bool = True) -> ConsentStatus:
|
|
195
|
+
"""
|
|
196
|
+
Get user's consent status.
|
|
197
|
+
FAILS if user doesn't exist - NO DEFAULTS.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
user_id: User ID to get consent for
|
|
201
|
+
extend_expiry: If True, reset TEMPORARY expiry to 2 weeks from now (default: True)
|
|
202
|
+
"""
|
|
203
|
+
self._consent_checks += 1
|
|
204
|
+
|
|
205
|
+
# Try cache first (but verify it's still valid)
|
|
206
|
+
if user_id in self._consent_cache:
|
|
207
|
+
cached = self._consent_cache[user_id]
|
|
208
|
+
self._check_cached_expiry(user_id, cached)
|
|
209
|
+
|
|
210
|
+
# Reset decay countdown on every interaction (2 weeks without seeing them)
|
|
211
|
+
if extend_expiry and cached.stream == ConsentStream.TEMPORARY:
|
|
212
|
+
await self._extend_temporary_expiry(user_id, cached)
|
|
213
|
+
|
|
214
|
+
return cached
|
|
215
|
+
|
|
216
|
+
# Load from graph
|
|
217
|
+
try:
|
|
218
|
+
status = await self._load_consent_from_graph(user_id)
|
|
219
|
+
|
|
220
|
+
# Reset decay countdown on every interaction (2 weeks without seeing them)
|
|
221
|
+
if extend_expiry and status.stream == ConsentStream.TEMPORARY:
|
|
222
|
+
await self._extend_temporary_expiry(user_id, status)
|
|
223
|
+
|
|
224
|
+
return status
|
|
225
|
+
|
|
226
|
+
except Exception as e:
|
|
227
|
+
if "not found" in str(e).lower():
|
|
228
|
+
raise ConsentNotFoundError(f"No consent found for user {user_id}")
|
|
229
|
+
raise
|
|
230
|
+
|
|
231
|
+
async def track_interaction(
|
|
232
|
+
self, user_id: str, channel_id: str, channel_type: Optional[str] = None, message_content: Optional[str] = None
|
|
233
|
+
) -> Optional[str]:
|
|
234
|
+
"""
|
|
235
|
+
Track user interaction for parasocial attachment prevention.
|
|
236
|
+
|
|
237
|
+
This method delegates to the AIR (Artificial Interaction Reminder) manager
|
|
238
|
+
to monitor 1:1 API interactions and provide break reminders when thresholds are exceeded.
|
|
239
|
+
|
|
240
|
+
Scope: API channels only (1:1 interactions, not community moderation)
|
|
241
|
+
Triggers:
|
|
242
|
+
- 30 minutes continuous interaction
|
|
243
|
+
- 20+ messages
|
|
244
|
+
- High valence patterns (anthropomorphism, dependency, emotional intensity)
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
user_id: User ID
|
|
248
|
+
channel_id: Channel ID
|
|
249
|
+
channel_type: Channel type (api, discord, cli, unknown) - if None, will infer from ID
|
|
250
|
+
message_content: Optional message content for valence analysis
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Reminder message if threshold exceeded, None otherwise
|
|
254
|
+
"""
|
|
255
|
+
return self._air_manager.track_interaction(user_id, channel_id, channel_type, message_content)
|
|
256
|
+
|
|
257
|
+
async def grant_consent(self, request: ConsentRequest, channel_id: Optional[str] = None) -> ConsentStatus:
|
|
258
|
+
"""
|
|
259
|
+
Grant or update consent.
|
|
260
|
+
VALIDATES everything, creates audit trail.
|
|
261
|
+
|
|
262
|
+
PARTNERED requires bilateral agreement:
|
|
263
|
+
- Creates task for agent approval
|
|
264
|
+
- Agent can REJECT, DEFER, or TASK_COMPLETE (accept)
|
|
265
|
+
- Returns pending status until agent decides
|
|
266
|
+
"""
|
|
267
|
+
self._consent_grants += 1
|
|
268
|
+
|
|
269
|
+
# Validate request
|
|
270
|
+
self._validate_consent_request(request)
|
|
271
|
+
|
|
272
|
+
# Get previous status if exists
|
|
273
|
+
previous_status = await self._get_previous_status(request.user_id)
|
|
274
|
+
|
|
275
|
+
# Check if upgrading to PARTNERED - requires bilateral agreement
|
|
276
|
+
if request.stream == ConsentStream.PARTNERED:
|
|
277
|
+
return await self._partnership_manager.create_partnership_request(request, previous_status, channel_id)
|
|
278
|
+
|
|
279
|
+
# Check for gaming behavior if switching streams
|
|
280
|
+
await self._check_gaming_behavior(request, previous_status)
|
|
281
|
+
|
|
282
|
+
# For TEMPORARY and ANONYMOUS - no bilateral agreement needed
|
|
283
|
+
new_status = self._create_consent_status(request, previous_status)
|
|
284
|
+
|
|
285
|
+
# Persist consent and audit trail
|
|
286
|
+
await self._persist_consent(new_status, previous_status, request.reason, "user")
|
|
287
|
+
|
|
288
|
+
logger.info(f"Consent granted for {request.user_id}: {new_status.stream}")
|
|
289
|
+
return new_status
|
|
290
|
+
|
|
291
|
+
def _validate_consent_request(self, request: ConsentRequest) -> None:
|
|
292
|
+
"""Validate consent request parameters."""
|
|
293
|
+
if not request.user_id:
|
|
294
|
+
raise ConsentValidationError("User ID required")
|
|
295
|
+
if not request.categories and request.stream == ConsentStream.PARTNERED:
|
|
296
|
+
raise ConsentValidationError("PARTNERED requires at least one category")
|
|
297
|
+
|
|
298
|
+
async def _get_previous_status(self, user_id: str) -> Optional[ConsentStatus]:
|
|
299
|
+
"""Get previous consent status if exists."""
|
|
300
|
+
try:
|
|
301
|
+
return await self.get_consent(user_id)
|
|
302
|
+
except ConsentNotFoundError:
|
|
303
|
+
return None
|
|
304
|
+
|
|
305
|
+
async def _check_gaming_behavior(self, request: ConsentRequest, previous_status: Optional[ConsentStatus]) -> None:
|
|
306
|
+
"""Check for gaming behavior if switching consent streams."""
|
|
307
|
+
if not previous_status or request.stream == previous_status.stream:
|
|
308
|
+
return
|
|
309
|
+
|
|
310
|
+
if not hasattr(self, "_filter_service"):
|
|
311
|
+
return
|
|
312
|
+
|
|
313
|
+
# Handle both enum and string types for stream
|
|
314
|
+
prev_stream = (
|
|
315
|
+
previous_status.stream.value if hasattr(previous_status.stream, "value") else previous_status.stream
|
|
316
|
+
)
|
|
317
|
+
req_stream = request.stream.value if hasattr(request.stream, "value") else request.stream
|
|
318
|
+
|
|
319
|
+
is_gaming = await self._filter_service.handle_consent_transition(request.user_id, prev_stream, req_stream)
|
|
320
|
+
|
|
321
|
+
if is_gaming:
|
|
322
|
+
logger.warning(
|
|
323
|
+
f"Gaming attempt detected for {request.user_id}: {previous_status.stream} -> {request.stream}"
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
def _create_consent_status(
|
|
327
|
+
self, request: ConsentRequest, previous_status: Optional[ConsentStatus]
|
|
328
|
+
) -> ConsentStatus:
|
|
329
|
+
"""Create new consent status from request."""
|
|
330
|
+
now = self._now()
|
|
331
|
+
expires_at = None
|
|
332
|
+
if request.stream == ConsentStream.TEMPORARY:
|
|
333
|
+
expires_at = now + timedelta(days=14)
|
|
334
|
+
|
|
335
|
+
return ConsentStatus(
|
|
336
|
+
user_id=request.user_id,
|
|
337
|
+
stream=request.stream,
|
|
338
|
+
categories=request.categories,
|
|
339
|
+
granted_at=previous_status.granted_at if previous_status else now,
|
|
340
|
+
expires_at=expires_at,
|
|
341
|
+
last_modified=now,
|
|
342
|
+
impact_score=previous_status.impact_score if previous_status else 0.0,
|
|
343
|
+
attribution_count=previous_status.attribution_count if previous_status else 0,
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
async def _persist_consent(
|
|
347
|
+
self,
|
|
348
|
+
new_status: ConsentStatus,
|
|
349
|
+
previous_status: Optional[ConsentStatus],
|
|
350
|
+
reason: Optional[str],
|
|
351
|
+
initiated_by: str,
|
|
352
|
+
) -> None:
|
|
353
|
+
"""Persist consent status and audit trail to graph."""
|
|
354
|
+
now = self._now()
|
|
355
|
+
|
|
356
|
+
# Store in graph
|
|
357
|
+
node = GraphNode(
|
|
358
|
+
id=f"consent/{new_status.user_id}",
|
|
359
|
+
type=NodeType.CONSENT,
|
|
360
|
+
scope=GraphScope.LOCAL,
|
|
361
|
+
attributes={
|
|
362
|
+
"stream": new_status.stream,
|
|
363
|
+
"categories": list(new_status.categories),
|
|
364
|
+
"granted_at": new_status.granted_at.isoformat(),
|
|
365
|
+
"expires_at": new_status.expires_at.isoformat() if new_status.expires_at else None,
|
|
366
|
+
"last_modified": new_status.last_modified.isoformat(),
|
|
367
|
+
"impact_score": new_status.impact_score,
|
|
368
|
+
"attribution_count": new_status.attribution_count,
|
|
369
|
+
},
|
|
370
|
+
updated_by="consent_manager",
|
|
371
|
+
updated_at=now,
|
|
372
|
+
)
|
|
373
|
+
if self._time_service is None:
|
|
374
|
+
raise ValueError("TimeService required for persisting consent")
|
|
375
|
+
add_graph_node(node, self._time_service, self._db_path)
|
|
376
|
+
|
|
377
|
+
# Create audit entry
|
|
378
|
+
audit = ConsentAuditEntry(
|
|
379
|
+
entry_id=str(uuid4()),
|
|
380
|
+
user_id=new_status.user_id,
|
|
381
|
+
timestamp=now,
|
|
382
|
+
previous_stream=previous_status.stream if previous_status else ConsentStream.TEMPORARY,
|
|
383
|
+
new_stream=new_status.stream,
|
|
384
|
+
previous_categories=previous_status.categories if previous_status else [],
|
|
385
|
+
new_categories=new_status.categories,
|
|
386
|
+
initiated_by=initiated_by,
|
|
387
|
+
reason=reason,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
# Store audit entry
|
|
391
|
+
audit_attrs = audit.model_dump(mode="json")
|
|
392
|
+
audit_attrs["service"] = "consent" # Add service identifier for querying
|
|
393
|
+
audit_node = GraphNode(
|
|
394
|
+
id=f"consent_audit/{audit.entry_id}",
|
|
395
|
+
type=NodeType.AUDIT_ENTRY,
|
|
396
|
+
scope=GraphScope.IDENTITY, # Use IDENTITY scope for user-specific audit trail
|
|
397
|
+
attributes=audit_attrs,
|
|
398
|
+
updated_by="consent_manager",
|
|
399
|
+
updated_at=now,
|
|
400
|
+
)
|
|
401
|
+
add_graph_node(audit_node, self._time_service, self._db_path)
|
|
402
|
+
|
|
403
|
+
# Update cache
|
|
404
|
+
self._consent_cache[new_status.user_id] = new_status
|
|
405
|
+
|
|
406
|
+
async def update_consent(
|
|
407
|
+
self, user_id: str, stream: ConsentStream, categories: List[ConsentCategory], reason: Optional[str] = None
|
|
408
|
+
) -> ConsentStatus:
|
|
409
|
+
"""
|
|
410
|
+
Internal method to update consent status directly.
|
|
411
|
+
Used by tool handlers for consent transitions.
|
|
412
|
+
Does NOT perform bilateral agreement checks - those should be done by caller.
|
|
413
|
+
"""
|
|
414
|
+
# Get previous status if exists
|
|
415
|
+
previous_status = None
|
|
416
|
+
try:
|
|
417
|
+
previous_status = await self.get_consent(user_id)
|
|
418
|
+
except ConsentNotFoundError:
|
|
419
|
+
pass
|
|
420
|
+
|
|
421
|
+
now = self._now()
|
|
422
|
+
expires_at = None
|
|
423
|
+
if stream == ConsentStream.TEMPORARY:
|
|
424
|
+
expires_at = now + timedelta(days=14)
|
|
425
|
+
|
|
426
|
+
new_status = ConsentStatus(
|
|
427
|
+
user_id=user_id,
|
|
428
|
+
stream=stream,
|
|
429
|
+
categories=categories,
|
|
430
|
+
granted_at=previous_status.granted_at if previous_status else now,
|
|
431
|
+
expires_at=expires_at,
|
|
432
|
+
last_modified=now,
|
|
433
|
+
impact_score=previous_status.impact_score if previous_status else 0.0,
|
|
434
|
+
attribution_count=previous_status.attribution_count if previous_status else 0,
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
# Store in graph
|
|
438
|
+
node = GraphNode(
|
|
439
|
+
id=f"consent/{user_id}",
|
|
440
|
+
type=NodeType.CONSENT,
|
|
441
|
+
scope=GraphScope.LOCAL,
|
|
442
|
+
attributes={
|
|
443
|
+
"stream": new_status.stream,
|
|
444
|
+
"categories": list(new_status.categories),
|
|
445
|
+
"granted_at": new_status.granted_at.isoformat(),
|
|
446
|
+
"expires_at": new_status.expires_at.isoformat() if new_status.expires_at else None,
|
|
447
|
+
"last_modified": new_status.last_modified.isoformat(),
|
|
448
|
+
"impact_score": new_status.impact_score,
|
|
449
|
+
"attribution_count": new_status.attribution_count,
|
|
450
|
+
},
|
|
451
|
+
updated_by="consent_manager",
|
|
452
|
+
updated_at=now,
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
if self._time_service is None:
|
|
456
|
+
raise ValueError("TimeService required for updating consent")
|
|
457
|
+
add_graph_node(node, self._time_service, self._db_path)
|
|
458
|
+
|
|
459
|
+
# Create audit entry
|
|
460
|
+
audit = ConsentAuditEntry(
|
|
461
|
+
entry_id=str(uuid4()),
|
|
462
|
+
user_id=user_id,
|
|
463
|
+
timestamp=now,
|
|
464
|
+
previous_stream=previous_status.stream if previous_status else ConsentStream.TEMPORARY,
|
|
465
|
+
new_stream=new_status.stream,
|
|
466
|
+
previous_categories=previous_status.categories if previous_status else [],
|
|
467
|
+
new_categories=new_status.categories,
|
|
468
|
+
initiated_by="tool",
|
|
469
|
+
reason=reason or "Tool-initiated update",
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
# Store audit entry
|
|
473
|
+
audit_attrs = audit.model_dump(mode="json")
|
|
474
|
+
audit_attrs["service"] = "consent" # Add service identifier for querying
|
|
475
|
+
audit_node = GraphNode(
|
|
476
|
+
id=f"consent_audit/{audit.entry_id}",
|
|
477
|
+
type=NodeType.AUDIT_ENTRY,
|
|
478
|
+
scope=GraphScope.IDENTITY, # Use IDENTITY scope for user-specific audit trail
|
|
479
|
+
attributes=audit_attrs,
|
|
480
|
+
updated_by="consent_manager",
|
|
481
|
+
updated_at=now,
|
|
482
|
+
)
|
|
483
|
+
add_graph_node(audit_node, self._time_service, self._db_path)
|
|
484
|
+
|
|
485
|
+
# Update cache
|
|
486
|
+
self._consent_cache[user_id] = new_status
|
|
487
|
+
|
|
488
|
+
logger.info(f"Consent updated for {user_id}: {new_status.stream}")
|
|
489
|
+
return new_status
|
|
490
|
+
|
|
491
|
+
async def revoke_consent(self, user_id: str, reason: Optional[str] = None) -> ConsentDecayStatus:
|
|
492
|
+
"""
|
|
493
|
+
Start decay protocol - IMMEDIATE identity severance.
|
|
494
|
+
Delegates to DecayProtocolManager for actual decay handling.
|
|
495
|
+
"""
|
|
496
|
+
self._consent_revokes += 1
|
|
497
|
+
|
|
498
|
+
# Verify user exists
|
|
499
|
+
status = await self.get_consent(user_id)
|
|
500
|
+
|
|
501
|
+
# Get filter service if available
|
|
502
|
+
filter_service = self._filter_service if hasattr(self, "_filter_service") else None
|
|
503
|
+
|
|
504
|
+
# Delegate to decay manager
|
|
505
|
+
decay = await self._decay_manager.initiate_decay(
|
|
506
|
+
user_id=user_id,
|
|
507
|
+
reason=reason,
|
|
508
|
+
initiated_by="user",
|
|
509
|
+
current_status=status,
|
|
510
|
+
filter_service=filter_service,
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
# Remove from cache (decay manager handles persistence)
|
|
514
|
+
if user_id in self._consent_cache:
|
|
515
|
+
del self._consent_cache[user_id]
|
|
516
|
+
|
|
517
|
+
return decay
|
|
518
|
+
|
|
519
|
+
async def check_expiry(self, user_id: str) -> bool:
|
|
520
|
+
"""
|
|
521
|
+
Check if TEMPORARY consent has expired.
|
|
522
|
+
NO GRACE PERIOD - expired means expired.
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
False: Consent exists and is valid
|
|
526
|
+
True: Consent exists but is expired
|
|
527
|
+
|
|
528
|
+
Raises:
|
|
529
|
+
ConsentNotFoundError: User has no consent record (FAIL FAST)
|
|
530
|
+
"""
|
|
531
|
+
status = await self.get_consent(user_id) # Let it raise - fail fast!
|
|
532
|
+
if status.stream == ConsentStream.TEMPORARY and status.expires_at:
|
|
533
|
+
return self._now() > status.expires_at
|
|
534
|
+
return False
|
|
535
|
+
|
|
536
|
+
async def get_impact_report(self, user_id: str) -> ConsentImpactReport:
|
|
537
|
+
"""
|
|
538
|
+
Generate impact report - REAL DATA ONLY.
|
|
539
|
+
"""
|
|
540
|
+
# Get consent status first
|
|
541
|
+
status = await self.get_consent(user_id)
|
|
542
|
+
|
|
543
|
+
# Query REAL data from TSDB summaries - NO FALLBACKS
|
|
544
|
+
if not self._memory_bus:
|
|
545
|
+
raise ValueError("Memory bus required for impact reporting - no fake data allowed")
|
|
546
|
+
|
|
547
|
+
# Get real interaction data from TSDB conversation summaries
|
|
548
|
+
conversation_summaries = await self._memory_bus.search(
|
|
549
|
+
query="", # Empty query to get all matching nodes
|
|
550
|
+
filters=MemorySearchFilter(node_type=NodeType.CONVERSATION_SUMMARY.value, scope=GraphScope.COMMUNITY.value),
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
# Count interactions where this user participated
|
|
554
|
+
total_interactions = 0
|
|
555
|
+
for summary in conversation_summaries:
|
|
556
|
+
if summary.attributes:
|
|
557
|
+
attrs = summary.attributes if isinstance(summary.attributes, dict) else summary.attributes.model_dump()
|
|
558
|
+
if "participants" in attrs:
|
|
559
|
+
participants = attrs["participants"]
|
|
560
|
+
# Check if user_id is in any participant data
|
|
561
|
+
if isinstance(participants, dict):
|
|
562
|
+
for participant_data in participants.values():
|
|
563
|
+
if isinstance(participant_data, dict) and participant_data.get("user_id") == user_id:
|
|
564
|
+
total_interactions += participant_data.get("message_count", 0)
|
|
565
|
+
|
|
566
|
+
# Get real contribution data from task summaries
|
|
567
|
+
task_summaries = await self._memory_bus.search(
|
|
568
|
+
query="", filters=MemorySearchFilter(node_type=NodeType.TASK_SUMMARY.value, scope=GraphScope.IDENTITY.value)
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
patterns_contributed = 0
|
|
572
|
+
for task_summary in task_summaries:
|
|
573
|
+
if task_summary.attributes:
|
|
574
|
+
attrs = (
|
|
575
|
+
task_summary.attributes
|
|
576
|
+
if isinstance(task_summary.attributes, dict)
|
|
577
|
+
else task_summary.attributes.model_dump()
|
|
578
|
+
)
|
|
579
|
+
if attrs.get("author_id") == user_id:
|
|
580
|
+
patterns_contributed += 1
|
|
581
|
+
|
|
582
|
+
# Calculate users helped from actual conversation engagement
|
|
583
|
+
users_helped_set = set()
|
|
584
|
+
for summary in conversation_summaries:
|
|
585
|
+
if summary.attributes:
|
|
586
|
+
attrs = summary.attributes if isinstance(summary.attributes, dict) else summary.attributes.model_dump()
|
|
587
|
+
if "participants" in attrs and isinstance(attrs["participants"], dict):
|
|
588
|
+
for participant_id, participant_data in attrs["participants"].items():
|
|
589
|
+
if participant_id != user_id and isinstance(participant_data, dict):
|
|
590
|
+
users_helped_set.add(participant_id)
|
|
591
|
+
users_helped = len(users_helped_set)
|
|
592
|
+
|
|
593
|
+
logger.info(
|
|
594
|
+
f"Real impact metrics for {user_id}: {total_interactions} interactions, {patterns_contributed} contributions, {users_helped} users helped"
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
report = ConsentImpactReport(
|
|
598
|
+
user_id=user_id,
|
|
599
|
+
total_interactions=total_interactions,
|
|
600
|
+
patterns_contributed=patterns_contributed,
|
|
601
|
+
users_helped=users_helped,
|
|
602
|
+
categories_active=status.categories,
|
|
603
|
+
impact_score=status.impact_score,
|
|
604
|
+
example_contributions=await self._get_example_contributions(user_id),
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
return report
|
|
608
|
+
|
|
609
|
+
async def get_audit_trail(self, user_id: str, limit: int = 100) -> List[ConsentAuditEntry]:
|
|
610
|
+
"""
|
|
611
|
+
Get consent change history - IMMUTABLE AUDIT TRAIL.
|
|
612
|
+
"""
|
|
613
|
+
# Query consent audit entries from graph
|
|
614
|
+
audit_entries = []
|
|
615
|
+
|
|
616
|
+
if self._memory_bus:
|
|
617
|
+
try:
|
|
618
|
+
# Query audit nodes for this user
|
|
619
|
+
audit_nodes = await self._memory_bus.search(
|
|
620
|
+
query="",
|
|
621
|
+
filters=MemorySearchFilter(
|
|
622
|
+
node_type=NodeType.AUDIT_ENTRY.value,
|
|
623
|
+
scope=GraphScope.IDENTITY.value,
|
|
624
|
+
attribute_values={"user_id": user_id, "service": "consent"},
|
|
625
|
+
),
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
# Convert nodes to audit entries (limit by parameter)
|
|
629
|
+
for node in audit_nodes[:limit]:
|
|
630
|
+
if node.attributes:
|
|
631
|
+
attrs = node.attributes if isinstance(node.attributes, dict) else node.attributes.model_dump()
|
|
632
|
+
entry = ConsentAuditEntry(
|
|
633
|
+
entry_id=get_str(attrs, "entry_id", "unknown"),
|
|
634
|
+
user_id=user_id,
|
|
635
|
+
timestamp=node.updated_at,
|
|
636
|
+
previous_stream=ConsentStream(get_str(attrs, "previous_stream", "temporary")),
|
|
637
|
+
new_stream=ConsentStream(get_str(attrs, "new_stream", "temporary")),
|
|
638
|
+
previous_categories=[
|
|
639
|
+
ConsentCategory(c) for c in get_list(attrs, "previous_categories", [])
|
|
640
|
+
],
|
|
641
|
+
new_categories=[ConsentCategory(c) for c in get_list(attrs, "new_categories", [])],
|
|
642
|
+
initiated_by=get_str(attrs, "initiated_by", "unknown"),
|
|
643
|
+
reason=get_str_optional(attrs, "reason"),
|
|
644
|
+
)
|
|
645
|
+
audit_entries.append(entry)
|
|
646
|
+
|
|
647
|
+
logger.debug(f"Found {len(audit_entries)} audit entries for {user_id}")
|
|
648
|
+
except Exception as e:
|
|
649
|
+
logger.warning(f"Failed to query audit trail for {user_id}: {e}")
|
|
650
|
+
|
|
651
|
+
return audit_entries
|
|
652
|
+
|
|
653
|
+
async def check_pending_partnership(self, user_id: str) -> Optional[str]:
|
|
654
|
+
"""
|
|
655
|
+
Check status of pending partnership request.
|
|
656
|
+
Delegates to PartnershipManager for checking and finalization.
|
|
657
|
+
|
|
658
|
+
Returns:
|
|
659
|
+
- "accepted": Partnership approved by agent
|
|
660
|
+
- "rejected": Partnership declined by agent
|
|
661
|
+
- "deferred": Agent needs more information
|
|
662
|
+
- "pending": Still processing
|
|
663
|
+
- None: No pending request
|
|
664
|
+
"""
|
|
665
|
+
# Check status via partnership manager
|
|
666
|
+
status = await self._partnership_manager.check_partnership_status(user_id)
|
|
667
|
+
|
|
668
|
+
# If not accepted, return the status as-is
|
|
669
|
+
if status != "accepted":
|
|
670
|
+
return status
|
|
671
|
+
|
|
672
|
+
# Partnership was accepted - finalize it
|
|
673
|
+
# Get task_id from pending partnerships
|
|
674
|
+
pending_list = self._partnership_manager.list_pending_partnerships()
|
|
675
|
+
task_id = None
|
|
676
|
+
for pending in pending_list:
|
|
677
|
+
if pending.get("user_id") == user_id:
|
|
678
|
+
task_id = pending.get("task_id")
|
|
679
|
+
break
|
|
680
|
+
|
|
681
|
+
if not task_id:
|
|
682
|
+
logger.warning(f"Partnership accepted for {user_id} but no task_id found")
|
|
683
|
+
return status
|
|
684
|
+
|
|
685
|
+
# Finalize via partnership manager
|
|
686
|
+
partnership_data = self._partnership_manager.finalize_partnership_approval(user_id, str(task_id))
|
|
687
|
+
|
|
688
|
+
if not partnership_data:
|
|
689
|
+
logger.warning(f"Partnership finalization failed for {user_id}")
|
|
690
|
+
return status
|
|
691
|
+
|
|
692
|
+
# Create and persist PARTNERED consent status
|
|
693
|
+
now = self._now()
|
|
694
|
+
categories = partnership_data.get("categories", [])
|
|
695
|
+
|
|
696
|
+
partnered_status = ConsentStatus(
|
|
697
|
+
user_id=user_id,
|
|
698
|
+
stream=ConsentStream.PARTNERED,
|
|
699
|
+
categories=categories,
|
|
700
|
+
granted_at=now,
|
|
701
|
+
expires_at=None, # PARTNERED doesn't expire
|
|
702
|
+
last_modified=now,
|
|
703
|
+
impact_score=0.0,
|
|
704
|
+
attribution_count=0,
|
|
705
|
+
)
|
|
706
|
+
|
|
707
|
+
# Store in graph
|
|
708
|
+
node = GraphNode(
|
|
709
|
+
id=f"consent/{user_id}",
|
|
710
|
+
type=NodeType.CONSENT,
|
|
711
|
+
scope=GraphScope.LOCAL,
|
|
712
|
+
attributes={
|
|
713
|
+
"stream": partnered_status.stream,
|
|
714
|
+
"categories": list(partnered_status.categories),
|
|
715
|
+
"granted_at": partnered_status.granted_at.isoformat(),
|
|
716
|
+
"expires_at": None,
|
|
717
|
+
"last_modified": partnered_status.last_modified.isoformat(),
|
|
718
|
+
"impact_score": partnered_status.impact_score,
|
|
719
|
+
"attribution_count": partnered_status.attribution_count,
|
|
720
|
+
"partnership_approved": True,
|
|
721
|
+
"approval_task_id": task_id,
|
|
722
|
+
},
|
|
723
|
+
updated_by="consent_manager",
|
|
724
|
+
updated_at=now,
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
if self._time_service is None:
|
|
728
|
+
raise ValueError("TimeService required for finalizing partnership")
|
|
729
|
+
add_graph_node(node, self._time_service, self._db_path)
|
|
730
|
+
|
|
731
|
+
# Update cache
|
|
732
|
+
self._consent_cache[user_id] = partnered_status
|
|
733
|
+
|
|
734
|
+
# Return "partnered" to indicate successful finalization (different from "accepted")
|
|
735
|
+
return "partnered"
|
|
736
|
+
|
|
737
|
+
async def _get_example_contributions(self, user_id: str) -> List[str]:
|
|
738
|
+
"""Get example contributions from the graph."""
|
|
739
|
+
examples: List[str] = []
|
|
740
|
+
|
|
741
|
+
if self._memory_bus:
|
|
742
|
+
try:
|
|
743
|
+
# Query recent contribution nodes from this user
|
|
744
|
+
contribution_nodes = await self._memory_bus.search(
|
|
745
|
+
query="",
|
|
746
|
+
filters=MemorySearchFilter(
|
|
747
|
+
node_type=NodeType.CONCEPT.value,
|
|
748
|
+
scope=GraphScope.COMMUNITY.value,
|
|
749
|
+
attribute_values={"contributor_id": user_id},
|
|
750
|
+
),
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
# Extract meaningful examples (limit to 3-5)
|
|
754
|
+
for node in contribution_nodes[:5]:
|
|
755
|
+
if node.attributes:
|
|
756
|
+
attrs = node.attributes if isinstance(node.attributes, dict) else node.attributes.model_dump()
|
|
757
|
+
# Use get_str to extract string values safely
|
|
758
|
+
description = get_str_optional(attrs, "description")
|
|
759
|
+
if description:
|
|
760
|
+
examples.append(description)
|
|
761
|
+
else:
|
|
762
|
+
content = get_str_optional(attrs, "content")
|
|
763
|
+
if content:
|
|
764
|
+
examples.append(content)
|
|
765
|
+
else:
|
|
766
|
+
# Fallback to node ID if no meaningful description
|
|
767
|
+
examples.append(f"Contribution: {node.id}")
|
|
768
|
+
|
|
769
|
+
logger.debug(f"Found {len(examples)} example contributions for {user_id}")
|
|
770
|
+
except Exception as e:
|
|
771
|
+
logger.warning(f"Failed to query example contributions for {user_id}: {e}")
|
|
772
|
+
|
|
773
|
+
# Fallback examples if no graph data
|
|
774
|
+
if not examples:
|
|
775
|
+
examples = [
|
|
776
|
+
"Provided feedback on system behavior",
|
|
777
|
+
"Contributed to pattern recognition",
|
|
778
|
+
"Participated in conversation threads",
|
|
779
|
+
]
|
|
780
|
+
|
|
781
|
+
return examples
|
|
782
|
+
|
|
783
|
+
async def cleanup_expired(self) -> int:
|
|
784
|
+
"""
|
|
785
|
+
Clean up all expired TEMPORARY consents.
|
|
786
|
+
HARD DELETE after 14 days.
|
|
787
|
+
"""
|
|
788
|
+
self._expired_cleanups += 1
|
|
789
|
+
current_time = self._now()
|
|
790
|
+
|
|
791
|
+
# Get expired user IDs from graph or cache
|
|
792
|
+
expired_user_ids = await self._find_expired_user_ids(current_time)
|
|
793
|
+
|
|
794
|
+
# Perform cleanup operations
|
|
795
|
+
cleanup_count = self._perform_cleanup(expired_user_ids)
|
|
796
|
+
|
|
797
|
+
return cleanup_count
|
|
798
|
+
|
|
799
|
+
async def _find_expired_user_ids(self, current_time: datetime) -> List[str]:
|
|
800
|
+
"""Find all user IDs with expired temporary consents."""
|
|
801
|
+
if self._memory_bus:
|
|
802
|
+
return await self._find_expired_from_graph(current_time)
|
|
803
|
+
else:
|
|
804
|
+
return self._find_expired_from_cache(current_time)
|
|
805
|
+
|
|
806
|
+
async def _find_expired_from_graph(self, current_time: datetime) -> List[str]:
|
|
807
|
+
"""Find expired consents from memory graph nodes."""
|
|
808
|
+
expired = []
|
|
809
|
+
|
|
810
|
+
if self._memory_bus is None:
|
|
811
|
+
return []
|
|
812
|
+
|
|
813
|
+
try:
|
|
814
|
+
consent_nodes = await self._memory_bus.search(
|
|
815
|
+
query="",
|
|
816
|
+
filters=MemorySearchFilter(
|
|
817
|
+
node_type=NodeType.CONCEPT.value,
|
|
818
|
+
scope=GraphScope.IDENTITY.value,
|
|
819
|
+
attribute_values={"service": "consent"},
|
|
820
|
+
),
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
for node in consent_nodes:
|
|
824
|
+
user_id = self._extract_expired_user_from_node(node, current_time)
|
|
825
|
+
if user_id:
|
|
826
|
+
expired.append(user_id)
|
|
827
|
+
|
|
828
|
+
logger.debug(f"Found {len(expired)} expired consent nodes in graph")
|
|
829
|
+
|
|
830
|
+
except Exception as e:
|
|
831
|
+
logger.warning(f"Failed to query consent nodes for expiry cleanup: {e}")
|
|
832
|
+
# Fall back to cache-based cleanup on graph query failure
|
|
833
|
+
expired = self._find_expired_from_cache(current_time)
|
|
834
|
+
|
|
835
|
+
return expired
|
|
836
|
+
|
|
837
|
+
def _extract_expired_user_from_node(self, node: Any, current_time: datetime) -> Optional[str]:
|
|
838
|
+
"""Extract user ID if the consent node represents an expired temporary consent."""
|
|
839
|
+
if not node.attributes:
|
|
840
|
+
return None
|
|
841
|
+
|
|
842
|
+
attrs = node.attributes if isinstance(node.attributes, dict) else node.attributes.model_dump()
|
|
843
|
+
stream = attrs.get("stream")
|
|
844
|
+
expires_at_str = attrs.get("expires_at")
|
|
845
|
+
user_id = attrs.get("user_id")
|
|
846
|
+
|
|
847
|
+
# Check if this is a temporary consent with expiry data
|
|
848
|
+
if not (stream == ConsentStream.TEMPORARY.value and expires_at_str and user_id):
|
|
849
|
+
return None
|
|
850
|
+
|
|
851
|
+
# Parse and check expiry
|
|
852
|
+
try:
|
|
853
|
+
expires_at = datetime.fromisoformat(expires_at_str)
|
|
854
|
+
if current_time > expires_at:
|
|
855
|
+
return str(user_id)
|
|
856
|
+
except Exception as e:
|
|
857
|
+
logger.warning(f"Failed to parse expiry date for {user_id}: {e}")
|
|
858
|
+
|
|
859
|
+
return None
|
|
860
|
+
|
|
861
|
+
def _find_expired_from_cache(self, current_time: datetime) -> List[str]:
|
|
862
|
+
"""Find expired consents from local cache as fallback."""
|
|
863
|
+
expired = []
|
|
864
|
+
|
|
865
|
+
for user_id, status in self._consent_cache.items():
|
|
866
|
+
if self._is_cache_entry_expired(status, current_time):
|
|
867
|
+
expired.append(user_id)
|
|
868
|
+
|
|
869
|
+
return expired
|
|
870
|
+
|
|
871
|
+
def _is_cache_entry_expired(self, status: ConsentStatus, current_time: datetime) -> bool:
|
|
872
|
+
"""Check if a cached consent status is expired."""
|
|
873
|
+
return (
|
|
874
|
+
status.stream == ConsentStream.TEMPORARY
|
|
875
|
+
and status.expires_at is not None
|
|
876
|
+
and current_time > status.expires_at
|
|
877
|
+
)
|
|
878
|
+
|
|
879
|
+
def _perform_cleanup(self, expired_user_ids: List[str]) -> int:
|
|
880
|
+
"""Remove expired consents from cache and return count."""
|
|
881
|
+
count = 0
|
|
882
|
+
|
|
883
|
+
for user_id in expired_user_ids:
|
|
884
|
+
if user_id in self._consent_cache:
|
|
885
|
+
del self._consent_cache[user_id]
|
|
886
|
+
count += 1
|
|
887
|
+
logger.info(f"Cleaned up expired consent for {user_id}")
|
|
888
|
+
|
|
889
|
+
return count
|
|
890
|
+
|
|
891
|
+
def get_service_type(self) -> ServiceType:
|
|
892
|
+
"""Get service type."""
|
|
893
|
+
return ServiceType.TOOL # Changed to TOOL so it can be discovered by ToolBus
|
|
894
|
+
|
|
895
|
+
def get_capabilities(self) -> ServiceCapabilities:
|
|
896
|
+
"""Get service capabilities."""
|
|
897
|
+
return ServiceCapabilities(
|
|
898
|
+
service_name="ConsentService",
|
|
899
|
+
actions=[], # Non-bussed services don't have actions
|
|
900
|
+
version="0.2.0",
|
|
901
|
+
dependencies=["TimeService"],
|
|
902
|
+
metadata=None,
|
|
903
|
+
)
|
|
904
|
+
|
|
905
|
+
def _extract_air_metrics(self, air_metrics: Dict[str, Any]) -> Dict[str, float]:
|
|
906
|
+
"""Extract and safely cast AIR metrics."""
|
|
907
|
+
return {
|
|
908
|
+
"consent_air_total_interactions": (
|
|
909
|
+
float(val) if isinstance((val := air_metrics.get("total_interactions", 0)), (int, float)) else 0.0
|
|
910
|
+
),
|
|
911
|
+
"consent_air_reminders_sent": (
|
|
912
|
+
float(val) if isinstance((val := air_metrics.get("reminders_sent", 0)), (int, float)) else 0.0
|
|
913
|
+
),
|
|
914
|
+
"consent_air_reminder_rate_percent": (
|
|
915
|
+
float(val) if isinstance((val := air_metrics.get("reminder_rate_percent", 0.0)), (int, float)) else 0.0
|
|
916
|
+
),
|
|
917
|
+
"consent_air_active_sessions": (
|
|
918
|
+
float(val) if isinstance((val := air_metrics.get("active_sessions", 0)), (int, float)) else 0.0
|
|
919
|
+
),
|
|
920
|
+
"consent_air_time_triggered": (
|
|
921
|
+
float(val) if isinstance((val := air_metrics.get("time_triggered_reminders", 0)), (int, float)) else 0.0
|
|
922
|
+
),
|
|
923
|
+
"consent_air_message_triggered": (
|
|
924
|
+
float(val)
|
|
925
|
+
if isinstance((val := air_metrics.get("message_triggered_reminders", 0)), (int, float))
|
|
926
|
+
else 0.0
|
|
927
|
+
),
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
def _collect_custom_metrics(self) -> Dict[str, float]:
|
|
931
|
+
"""
|
|
932
|
+
Collect consent-specific metrics - REAL DATA ONLY.
|
|
933
|
+
|
|
934
|
+
Top 5 Most Important Metrics:
|
|
935
|
+
1. consent_active_users - Number of users with active consent
|
|
936
|
+
2. consent_stream_distribution - Breakdown by stream type (TEMPORARY/PARTNERED/ANONYMOUS)
|
|
937
|
+
3. consent_partnership_success_rate - Approval rate for partnership requests
|
|
938
|
+
4. consent_average_age_days - Average age of active consents
|
|
939
|
+
5. consent_decay_completion_rate - Percentage of decays completed vs initiated
|
|
940
|
+
"""
|
|
941
|
+
# Get base metrics from parent
|
|
942
|
+
metrics = super()._collect_custom_metrics()
|
|
943
|
+
|
|
944
|
+
now = self._time_service.now() if self._time_service else datetime.now(timezone.utc)
|
|
945
|
+
|
|
946
|
+
# Collect stream distribution metrics using metrics collector
|
|
947
|
+
stream_metrics = self._metrics_collector.collect_stream_distribution(self._consent_cache, now)
|
|
948
|
+
metrics.update(stream_metrics)
|
|
949
|
+
|
|
950
|
+
# Get partnership metrics from manager
|
|
951
|
+
partnership_requests, partnership_approvals, _ = self._partnership_manager.get_request_counts()
|
|
952
|
+
partnership_metrics = self._metrics_collector.collect_partnership_metrics(
|
|
953
|
+
partnership_requests, partnership_approvals, 0, self._partnership_manager.get_pending_count()
|
|
954
|
+
)
|
|
955
|
+
metrics.update(partnership_metrics)
|
|
956
|
+
|
|
957
|
+
# Get decay metrics from manager
|
|
958
|
+
active_decays = self._decay_manager.get_active_decays()
|
|
959
|
+
decay_metrics = self._metrics_collector.collect_decay_metrics(len(active_decays), 0, len(active_decays))
|
|
960
|
+
metrics.update(decay_metrics)
|
|
961
|
+
|
|
962
|
+
# Operational metrics
|
|
963
|
+
operational_metrics = self._metrics_collector.collect_operational_metrics(
|
|
964
|
+
self._consent_checks,
|
|
965
|
+
self._consent_grants,
|
|
966
|
+
self._consent_revokes,
|
|
967
|
+
self._expired_cleanups,
|
|
968
|
+
self._tool_executions,
|
|
969
|
+
self._tool_failures,
|
|
970
|
+
)
|
|
971
|
+
metrics.update(operational_metrics)
|
|
972
|
+
|
|
973
|
+
# AIR metrics
|
|
974
|
+
air_metrics_dict = self._air_manager.get_metrics()
|
|
975
|
+
air_metrics = self._extract_air_metrics(air_metrics_dict)
|
|
976
|
+
metrics.update(air_metrics)
|
|
977
|
+
|
|
978
|
+
# Service health
|
|
979
|
+
metrics["consent_service_uptime_seconds"] = (
|
|
980
|
+
self._calculate_uptime() if hasattr(self, "_calculate_uptime") else 0.0
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
return metrics
|
|
984
|
+
|
|
985
|
+
def _check_dependencies(self) -> bool:
|
|
986
|
+
"""Check service dependencies."""
|
|
987
|
+
return self._time_service is not None
|
|
988
|
+
|
|
989
|
+
def _get_actions(self) -> List[str]:
|
|
990
|
+
"""Get available actions - not used for non-bussed services."""
|
|
991
|
+
return ["upgrade_relationship", "degrade_relationship"]
|
|
992
|
+
|
|
993
|
+
# ToolService Protocol Implementation
|
|
994
|
+
|
|
995
|
+
async def execute_tool(self, tool_name: str, parameters: JSONDict) -> ToolExecutionResult:
|
|
996
|
+
"""Execute a tool and return the result."""
|
|
997
|
+
self._track_request() # Track the tool execution
|
|
998
|
+
self._tool_executions += 1
|
|
999
|
+
|
|
1000
|
+
if tool_name == "upgrade_relationship":
|
|
1001
|
+
result = await self._upgrade_relationship_tool(parameters)
|
|
1002
|
+
elif tool_name == "degrade_relationship":
|
|
1003
|
+
result = await self._degrade_relationship_tool(parameters)
|
|
1004
|
+
else:
|
|
1005
|
+
self._tool_failures += 1 # Unknown tool is a failure!
|
|
1006
|
+
return ToolExecutionResult(
|
|
1007
|
+
tool_name=tool_name,
|
|
1008
|
+
status=ToolExecutionStatus.NOT_FOUND,
|
|
1009
|
+
success=False,
|
|
1010
|
+
data=None,
|
|
1011
|
+
error=f"Unknown tool: {tool_name}",
|
|
1012
|
+
correlation_id=f"consent_{tool_name}_{self._now().timestamp()}",
|
|
1013
|
+
)
|
|
1014
|
+
|
|
1015
|
+
# Track failures
|
|
1016
|
+
if not result.get("success", False):
|
|
1017
|
+
self._tool_failures += 1
|
|
1018
|
+
|
|
1019
|
+
return ToolExecutionResult(
|
|
1020
|
+
tool_name=tool_name,
|
|
1021
|
+
status=ToolExecutionStatus.COMPLETED if result.get("success") else ToolExecutionStatus.FAILED,
|
|
1022
|
+
success=result.get("success", False),
|
|
1023
|
+
data=result,
|
|
1024
|
+
error=result.get("error"),
|
|
1025
|
+
correlation_id=f"consent_{tool_name}_{self._now().timestamp()}",
|
|
1026
|
+
)
|
|
1027
|
+
|
|
1028
|
+
async def get_available_tools(self) -> List[str]:
|
|
1029
|
+
"""Get list of available tool names."""
|
|
1030
|
+
return ["upgrade_relationship", "degrade_relationship"]
|
|
1031
|
+
|
|
1032
|
+
async def list_tools(self) -> List[str]:
|
|
1033
|
+
"""List available tools - required by ToolServiceProtocol."""
|
|
1034
|
+
return ["upgrade_relationship", "degrade_relationship"]
|
|
1035
|
+
|
|
1036
|
+
async def get_tool_schema(self, tool_name: str) -> Optional[ToolParameterSchema]:
|
|
1037
|
+
"""Get parameter schema for a specific tool."""
|
|
1038
|
+
schemas = {
|
|
1039
|
+
"upgrade_relationship": ToolParameterSchema(
|
|
1040
|
+
type="object",
|
|
1041
|
+
properties={
|
|
1042
|
+
"user_id": {"type": "string", "description": "User ID requesting the upgrade"},
|
|
1043
|
+
"reason": {
|
|
1044
|
+
"type": "string",
|
|
1045
|
+
"description": "Reason for upgrade request",
|
|
1046
|
+
"default": "User requested partnership",
|
|
1047
|
+
},
|
|
1048
|
+
},
|
|
1049
|
+
required=["user_id"],
|
|
1050
|
+
),
|
|
1051
|
+
"degrade_relationship": ToolParameterSchema(
|
|
1052
|
+
type="object",
|
|
1053
|
+
properties={
|
|
1054
|
+
"user_id": {"type": "string", "description": "User ID requesting the downgrade"},
|
|
1055
|
+
"target_stream": {
|
|
1056
|
+
"type": "string",
|
|
1057
|
+
"description": "Target stream: TEMPORARY or ANONYMOUS",
|
|
1058
|
+
"default": "TEMPORARY",
|
|
1059
|
+
},
|
|
1060
|
+
"reason": {
|
|
1061
|
+
"type": "string",
|
|
1062
|
+
"description": "Reason for downgrade",
|
|
1063
|
+
"default": "User requested downgrade",
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
required=["user_id"],
|
|
1067
|
+
),
|
|
1068
|
+
}
|
|
1069
|
+
return schemas.get(tool_name)
|
|
1070
|
+
|
|
1071
|
+
async def get_tool_info(self, tool_name: str) -> Optional[ToolInfo]:
|
|
1072
|
+
"""Get detailed information about a specific tool."""
|
|
1073
|
+
tools_info = {
|
|
1074
|
+
"upgrade_relationship": ToolInfo(
|
|
1075
|
+
name="upgrade_relationship",
|
|
1076
|
+
description="Request to upgrade user relationship to PARTNERED (requires bilateral consent)",
|
|
1077
|
+
parameters=ToolParameterSchema(
|
|
1078
|
+
type="object",
|
|
1079
|
+
properties={
|
|
1080
|
+
"user_id": {"type": "string", "description": "User ID requesting the upgrade"},
|
|
1081
|
+
"reason": {
|
|
1082
|
+
"type": "string",
|
|
1083
|
+
"description": "Reason for upgrade request",
|
|
1084
|
+
"default": "User requested partnership",
|
|
1085
|
+
},
|
|
1086
|
+
},
|
|
1087
|
+
required=["user_id"],
|
|
1088
|
+
),
|
|
1089
|
+
),
|
|
1090
|
+
"degrade_relationship": ToolInfo(
|
|
1091
|
+
name="degrade_relationship",
|
|
1092
|
+
description="Request to downgrade user relationship to TEMPORARY or ANONYMOUS",
|
|
1093
|
+
parameters=ToolParameterSchema(
|
|
1094
|
+
type="object",
|
|
1095
|
+
properties={
|
|
1096
|
+
"user_id": {"type": "string", "description": "User ID requesting the downgrade"},
|
|
1097
|
+
"target_stream": {
|
|
1098
|
+
"type": "string",
|
|
1099
|
+
"description": "Target stream: TEMPORARY or ANONYMOUS",
|
|
1100
|
+
"default": "TEMPORARY",
|
|
1101
|
+
},
|
|
1102
|
+
"reason": {
|
|
1103
|
+
"type": "string",
|
|
1104
|
+
"description": "Reason for downgrade",
|
|
1105
|
+
"default": "User requested downgrade",
|
|
1106
|
+
},
|
|
1107
|
+
},
|
|
1108
|
+
required=["user_id"],
|
|
1109
|
+
),
|
|
1110
|
+
),
|
|
1111
|
+
}
|
|
1112
|
+
return tools_info.get(tool_name)
|
|
1113
|
+
|
|
1114
|
+
async def get_all_tool_info(self) -> List[ToolInfo]:
|
|
1115
|
+
"""Get info for all available tools."""
|
|
1116
|
+
tools = []
|
|
1117
|
+
tool1 = await self.get_tool_info("upgrade_relationship")
|
|
1118
|
+
if tool1:
|
|
1119
|
+
tools.append(tool1)
|
|
1120
|
+
tool2 = await self.get_tool_info("degrade_relationship")
|
|
1121
|
+
if tool2:
|
|
1122
|
+
tools.append(tool2)
|
|
1123
|
+
return tools
|
|
1124
|
+
|
|
1125
|
+
async def get_tool_result(self, correlation_id: str, timeout: float = 30.0) -> Optional[ToolExecutionResult]:
|
|
1126
|
+
"""Get result of an async tool execution by correlation ID."""
|
|
1127
|
+
# ConsentService tools are synchronous, so results are immediate
|
|
1128
|
+
return None
|
|
1129
|
+
|
|
1130
|
+
async def validate_parameters(self, tool_name: str, parameters: JSONDict) -> bool:
|
|
1131
|
+
"""Validate parameters for a tool."""
|
|
1132
|
+
if tool_name == "upgrade_relationship":
|
|
1133
|
+
return "user_id" in parameters
|
|
1134
|
+
elif tool_name == "degrade_relationship":
|
|
1135
|
+
return "user_id" in parameters
|
|
1136
|
+
return False
|
|
1137
|
+
|
|
1138
|
+
async def _upgrade_relationship_tool(self, parameters: JSONDict) -> JSONDict:
|
|
1139
|
+
"""Tool implementation for upgrading relationship to PARTNERED."""
|
|
1140
|
+
try:
|
|
1141
|
+
user_id = get_str(parameters, "user_id", "")
|
|
1142
|
+
reason = get_str(parameters, "reason", "User requested partnership")
|
|
1143
|
+
|
|
1144
|
+
if not user_id:
|
|
1145
|
+
return {"success": False, "error": "user_id is required"}
|
|
1146
|
+
|
|
1147
|
+
# Get current consent status
|
|
1148
|
+
try:
|
|
1149
|
+
current = await self.get_consent(user_id)
|
|
1150
|
+
except ConsentNotFoundError:
|
|
1151
|
+
# Create default TEMPORARY consent if none exists
|
|
1152
|
+
request = ConsentRequest(
|
|
1153
|
+
user_id=user_id,
|
|
1154
|
+
stream=ConsentStream.TEMPORARY,
|
|
1155
|
+
categories=[], # TEMPORARY doesn't need categories
|
|
1156
|
+
reason="Default consent for impact calculation",
|
|
1157
|
+
)
|
|
1158
|
+
current = await self.grant_consent(request)
|
|
1159
|
+
|
|
1160
|
+
if current.stream == ConsentStream.PARTNERED:
|
|
1161
|
+
return {
|
|
1162
|
+
"success": True,
|
|
1163
|
+
"message": "Already in PARTNERED relationship",
|
|
1164
|
+
"current_stream": "PARTNERED",
|
|
1165
|
+
"user_id": user_id,
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
# Create upgrade request - this will need agent approval
|
|
1169
|
+
request = ConsentRequest(
|
|
1170
|
+
user_id=user_id,
|
|
1171
|
+
stream=ConsentStream.PARTNERED,
|
|
1172
|
+
categories=[ConsentCategory.INTERACTION, ConsentCategory.PREFERENCE],
|
|
1173
|
+
reason=reason,
|
|
1174
|
+
)
|
|
1175
|
+
|
|
1176
|
+
# Update to PARTNERED (pending agent approval via thought/task system)
|
|
1177
|
+
await self.update_consent(user_id, ConsentStream.PARTNERED, request.categories)
|
|
1178
|
+
|
|
1179
|
+
# Note: Partnership requests are now tracked by PartnershipManager
|
|
1180
|
+
return {
|
|
1181
|
+
"success": True,
|
|
1182
|
+
"message": "Partnership upgrade requested - requires agent approval",
|
|
1183
|
+
"current_stream": current.stream.value if hasattr(current.stream, "value") else current.stream,
|
|
1184
|
+
"requested_stream": "PARTNERED",
|
|
1185
|
+
"user_id": user_id,
|
|
1186
|
+
"status": "PENDING_APPROVAL",
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
except Exception as e:
|
|
1190
|
+
logger.error(f"Failed to upgrade relationship: {e}")
|
|
1191
|
+
return {"success": False, "error": str(e)}
|
|
1192
|
+
|
|
1193
|
+
async def _degrade_relationship_tool(self, parameters: JSONDict) -> JSONDict:
|
|
1194
|
+
"""Tool implementation for downgrading relationship."""
|
|
1195
|
+
try:
|
|
1196
|
+
user_id = get_str(parameters, "user_id", "")
|
|
1197
|
+
target_stream = get_str(parameters, "target_stream", "TEMPORARY")
|
|
1198
|
+
reason = get_str(parameters, "reason", "User requested downgrade")
|
|
1199
|
+
|
|
1200
|
+
if not user_id:
|
|
1201
|
+
return {"success": False, "error": "user_id is required"}
|
|
1202
|
+
|
|
1203
|
+
if target_stream not in ["TEMPORARY", "ANONYMOUS"]:
|
|
1204
|
+
return {"success": False, "error": "target_stream must be TEMPORARY or ANONYMOUS"}
|
|
1205
|
+
|
|
1206
|
+
# Convert uppercase to lowercase for enum
|
|
1207
|
+
target_stream_lower = target_stream.lower()
|
|
1208
|
+
|
|
1209
|
+
# Get current consent status
|
|
1210
|
+
try:
|
|
1211
|
+
current = await self.get_consent(user_id)
|
|
1212
|
+
except ConsentNotFoundError:
|
|
1213
|
+
# If no consent exists and target is ANONYMOUS, create it
|
|
1214
|
+
if target_stream == "ANONYMOUS":
|
|
1215
|
+
await self.update_consent(user_id, ConsentStream.ANONYMOUS, [ConsentCategory.RESEARCH])
|
|
1216
|
+
self._downgrades_completed += 1
|
|
1217
|
+
return {
|
|
1218
|
+
"success": True,
|
|
1219
|
+
"message": "Created ANONYMOUS consent for proactive opt-out",
|
|
1220
|
+
"current_stream": "ANONYMOUS",
|
|
1221
|
+
"user_id": user_id,
|
|
1222
|
+
}
|
|
1223
|
+
else:
|
|
1224
|
+
return {"success": False, "error": f"No consent found for user {user_id}"}
|
|
1225
|
+
|
|
1226
|
+
target = ConsentStream(target_stream_lower)
|
|
1227
|
+
|
|
1228
|
+
if current.stream == target:
|
|
1229
|
+
return {
|
|
1230
|
+
"success": True,
|
|
1231
|
+
"message": f"Already in {target_stream} relationship",
|
|
1232
|
+
"current_stream": target_stream,
|
|
1233
|
+
"user_id": user_id,
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
# Downgrades are immediate - no approval needed
|
|
1237
|
+
if target == ConsentStream.TEMPORARY:
|
|
1238
|
+
categories = [ConsentCategory.INTERACTION]
|
|
1239
|
+
else: # ANONYMOUS
|
|
1240
|
+
categories = [ConsentCategory.RESEARCH]
|
|
1241
|
+
|
|
1242
|
+
await self.update_consent(user_id, target, categories)
|
|
1243
|
+
|
|
1244
|
+
self._downgrades_completed += 1
|
|
1245
|
+
|
|
1246
|
+
return {
|
|
1247
|
+
"success": True,
|
|
1248
|
+
"message": f"Relationship downgraded to {target_stream}",
|
|
1249
|
+
"previous_stream": current.stream.value if hasattr(current.stream, "value") else current.stream,
|
|
1250
|
+
"current_stream": target_stream,
|
|
1251
|
+
"user_id": user_id,
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
except Exception as e:
|
|
1255
|
+
logger.error(f"Failed to degrade relationship: {e}")
|
|
1256
|
+
return {"success": False, "error": str(e)}
|