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,2342 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ciris_engine/runtime/ciris_runtime.py
|
|
3
|
+
|
|
4
|
+
New simplified runtime that properly orchestrates all components.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import time
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
from ciris_engine.config.ciris_services import get_billing_url
|
|
15
|
+
from ciris_engine.schemas.types import JSONDict
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from ciris_engine.schemas.runtime.bootstrap import RuntimeBootstrapConfig
|
|
19
|
+
|
|
20
|
+
from ciris_engine.logic import persistence
|
|
21
|
+
from ciris_engine.logic.adapters import load_adapter
|
|
22
|
+
from ciris_engine.logic.infrastructure.handlers.action_dispatcher import ActionDispatcher
|
|
23
|
+
from ciris_engine.logic.infrastructure.handlers.handler_registry import build_action_dispatcher
|
|
24
|
+
from ciris_engine.logic.processors import AgentProcessor
|
|
25
|
+
from ciris_engine.logic.registries.base import ServiceRegistry
|
|
26
|
+
from ciris_engine.logic.utils.constants import DEFAULT_NUM_ROUNDS
|
|
27
|
+
from ciris_engine.logic.utils.shutdown_manager import (
|
|
28
|
+
get_shutdown_manager,
|
|
29
|
+
is_global_shutdown_requested,
|
|
30
|
+
wait_for_global_shutdown_async,
|
|
31
|
+
)
|
|
32
|
+
from ciris_engine.protocols.infrastructure.base import BusManagerProtocol
|
|
33
|
+
from ciris_engine.protocols.runtime.base import BaseAdapterProtocol
|
|
34
|
+
from ciris_engine.protocols.services.adaptation.self_observation import SelfObservationServiceProtocol
|
|
35
|
+
|
|
36
|
+
# Governance service protocols
|
|
37
|
+
# Note: WiseAuthorityService doesn't have a unified protocol - it's a complex system with multiple protocols
|
|
38
|
+
from ciris_engine.protocols.services.governance.filter import AdaptiveFilterServiceProtocol
|
|
39
|
+
from ciris_engine.protocols.services.governance.visibility import VisibilityServiceProtocol
|
|
40
|
+
from ciris_engine.protocols.services.graph.audit import AuditServiceProtocol
|
|
41
|
+
from ciris_engine.protocols.services.graph.config import GraphConfigServiceProtocol
|
|
42
|
+
from ciris_engine.protocols.services.graph.incident_management import IncidentManagementServiceProtocol
|
|
43
|
+
|
|
44
|
+
# Graph service protocols
|
|
45
|
+
from ciris_engine.protocols.services.graph.memory import MemoryServiceProtocol
|
|
46
|
+
from ciris_engine.protocols.services.graph.telemetry import TelemetryServiceProtocol
|
|
47
|
+
from ciris_engine.protocols.services.graph.tsdb_consolidation import TSDBConsolidationServiceProtocol
|
|
48
|
+
|
|
49
|
+
# Infrastructure service protocols
|
|
50
|
+
from ciris_engine.protocols.services.infrastructure.authentication import AuthenticationServiceProtocol
|
|
51
|
+
from ciris_engine.protocols.services.infrastructure.database_maintenance import DatabaseMaintenanceServiceProtocol
|
|
52
|
+
from ciris_engine.protocols.services.infrastructure.resource_monitor import ResourceMonitorServiceProtocol
|
|
53
|
+
from ciris_engine.protocols.services.lifecycle.initialization import InitializationServiceProtocol
|
|
54
|
+
|
|
55
|
+
# Lifecycle service protocols
|
|
56
|
+
from ciris_engine.protocols.services.lifecycle.scheduler import TaskSchedulerServiceProtocol
|
|
57
|
+
from ciris_engine.protocols.services.lifecycle.shutdown import ShutdownServiceProtocol
|
|
58
|
+
from ciris_engine.protocols.services.lifecycle.time import TimeServiceProtocol
|
|
59
|
+
|
|
60
|
+
# Runtime service protocols
|
|
61
|
+
from ciris_engine.protocols.services.runtime.llm import LLMServiceProtocol
|
|
62
|
+
from ciris_engine.protocols.services.runtime.runtime_control import RuntimeControlServiceProtocol
|
|
63
|
+
from ciris_engine.protocols.services.runtime.secrets import SecretsServiceProtocol
|
|
64
|
+
from ciris_engine.protocols.services.runtime.tool import ToolServiceProtocol
|
|
65
|
+
from ciris_engine.schemas.adapters import AdapterServiceRegistration
|
|
66
|
+
from ciris_engine.schemas.config.essential import EssentialConfig
|
|
67
|
+
from ciris_engine.schemas.processors.states import AgentState
|
|
68
|
+
from ciris_engine.schemas.runtime.adapter_management import AdapterConfig
|
|
69
|
+
from ciris_engine.schemas.runtime.core import AgentIdentityRoot
|
|
70
|
+
from ciris_engine.schemas.runtime.enums import ServiceType
|
|
71
|
+
from ciris_engine.schemas.services.operations import InitializationPhase
|
|
72
|
+
|
|
73
|
+
from .component_builder import ComponentBuilder
|
|
74
|
+
from .identity_manager import IdentityManager
|
|
75
|
+
from .service_initializer import ServiceInitializer
|
|
76
|
+
|
|
77
|
+
logger = logging.getLogger(__name__)
|
|
78
|
+
|
|
79
|
+
# Domain identifiers for CIRIS proxy services (LLM, billing)
|
|
80
|
+
# Includes legacy ciris.ai and new ciris-services infrastructure
|
|
81
|
+
CIRIS_PROXY_DOMAINS = ("ciris.ai", "ciris-services")
|
|
82
|
+
# Keep single domain for backwards compatibility (tests)
|
|
83
|
+
CIRIS_PROXY_DOMAIN = "ciris.ai"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class CIRISRuntime:
|
|
87
|
+
"""
|
|
88
|
+
Main runtime orchestrator for CIRIS Agent.
|
|
89
|
+
Handles initialization of all components and services.
|
|
90
|
+
Implements the RuntimeInterface protocol.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
def __new__(cls, *args: Any, **kwargs: Any) -> "CIRISRuntime":
|
|
94
|
+
"""Custom __new__ to handle CI environment issues."""
|
|
95
|
+
# This fixes a pytest/CI issue where object.__new__ gets called incorrectly
|
|
96
|
+
instance = object.__new__(cls)
|
|
97
|
+
return instance
|
|
98
|
+
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
adapter_types: List[str],
|
|
102
|
+
essential_config: Optional[EssentialConfig] = None,
|
|
103
|
+
bootstrap: Optional["RuntimeBootstrapConfig"] = None,
|
|
104
|
+
startup_channel_id: Optional[str] = None,
|
|
105
|
+
adapter_configs: Optional[Dict[str, AdapterConfig]] = None,
|
|
106
|
+
**kwargs: Any,
|
|
107
|
+
) -> None:
|
|
108
|
+
# CRITICAL: Prevent runtime creation during module imports
|
|
109
|
+
import os
|
|
110
|
+
|
|
111
|
+
if os.environ.get("CIRIS_IMPORT_MODE", "").lower() == "true":
|
|
112
|
+
logger.error("CRITICAL: Attempted to create CIRISRuntime during import mode!")
|
|
113
|
+
raise RuntimeError(
|
|
114
|
+
"Cannot create CIRISRuntime during module imports. "
|
|
115
|
+
"This prevents side effects and unwanted process creation. "
|
|
116
|
+
"Call prevent_sideeffects.allow_runtime_creation() before creating runtime."
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Import RuntimeBootstrapConfig here to avoid circular imports
|
|
120
|
+
from ciris_engine.schemas.runtime.bootstrap import RuntimeBootstrapConfig
|
|
121
|
+
|
|
122
|
+
# Declare attributes that will be set in _parse_bootstrap_config
|
|
123
|
+
self.essential_config: EssentialConfig
|
|
124
|
+
self.startup_channel_id: str
|
|
125
|
+
self.adapter_configs: Dict[str, AdapterConfig]
|
|
126
|
+
self.modules_to_load: List[str]
|
|
127
|
+
self.debug: bool
|
|
128
|
+
self._preload_tasks: List[Any]
|
|
129
|
+
self.bootstrap: RuntimeBootstrapConfig
|
|
130
|
+
|
|
131
|
+
# Use bootstrap config if provided, otherwise construct from legacy parameters
|
|
132
|
+
self._parse_bootstrap_config(
|
|
133
|
+
bootstrap, essential_config, startup_channel_id, adapter_types, adapter_configs, kwargs
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
self.adapters: List[BaseAdapterProtocol] = []
|
|
137
|
+
|
|
138
|
+
# CRITICAL: Check for mock LLM environment variable
|
|
139
|
+
self._check_mock_llm()
|
|
140
|
+
|
|
141
|
+
# Initialize managers
|
|
142
|
+
self.identity_manager: Optional[IdentityManager] = None
|
|
143
|
+
self.service_initializer = ServiceInitializer(essential_config=essential_config)
|
|
144
|
+
self.component_builder: Optional[ComponentBuilder] = None
|
|
145
|
+
self.agent_processor: Optional["AgentProcessor"] = None
|
|
146
|
+
self._adapter_tasks: List[asyncio.Task[Any]] = []
|
|
147
|
+
|
|
148
|
+
# Load adapters from bootstrap config
|
|
149
|
+
self._load_adapters_from_bootstrap()
|
|
150
|
+
|
|
151
|
+
if not self.adapters:
|
|
152
|
+
raise RuntimeError("No valid adapters specified, shutting down")
|
|
153
|
+
|
|
154
|
+
# Runtime state
|
|
155
|
+
self._initialized = False
|
|
156
|
+
self._shutdown_manager = get_shutdown_manager()
|
|
157
|
+
self._shutdown_event: Optional[asyncio.Event] = None
|
|
158
|
+
self._shutdown_reason: Optional[str] = None
|
|
159
|
+
self._agent_task: Optional[asyncio.Task[Any]] = None
|
|
160
|
+
self._shutdown_complete = False
|
|
161
|
+
self._shutdown_in_progress = False # Set when shutdown has been initiated
|
|
162
|
+
|
|
163
|
+
# Resume protection - prevents SmartStartup from killing server mid-initialization
|
|
164
|
+
self._resume_in_progress = False # Set during resume_from_first_run
|
|
165
|
+
self._resume_started_at: Optional[float] = None # Timestamp for timeout detection
|
|
166
|
+
self._startup_time: float = time.time() # For uptime calculation
|
|
167
|
+
|
|
168
|
+
# Identity - will be loaded during initialization
|
|
169
|
+
self.agent_identity: Optional[AgentIdentityRoot] = None
|
|
170
|
+
|
|
171
|
+
# Properties to access services from the service initializer
|
|
172
|
+
@property
|
|
173
|
+
def service_registry(self) -> Optional[ServiceRegistry]:
|
|
174
|
+
return self.service_initializer.service_registry if self.service_initializer else None
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def bus_manager(self) -> Optional[BusManagerProtocol]:
|
|
178
|
+
return self.service_initializer.bus_manager if self.service_initializer else None
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def memory_service(self) -> Optional[MemoryServiceProtocol]:
|
|
182
|
+
return self.service_initializer.memory_service if self.service_initializer else None
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def resource_monitor(self) -> Optional[ResourceMonitorServiceProtocol]:
|
|
186
|
+
"""Access to resource monitor service - CRITICAL for mission-critical systems."""
|
|
187
|
+
return self.service_initializer.resource_monitor_service if self.service_initializer else None
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def secrets_service(self) -> Optional[SecretsServiceProtocol]:
|
|
191
|
+
return self.service_initializer.secrets_service if self.service_initializer else None
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def wa_auth_system(self) -> Optional[Any]:
|
|
195
|
+
"""WiseAuthorityService - complex system without unified protocol."""
|
|
196
|
+
return self.service_initializer.wa_auth_system if self.service_initializer else None
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def telemetry_service(self) -> Optional[TelemetryServiceProtocol]:
|
|
200
|
+
return self.service_initializer.telemetry_service if self.service_initializer else None
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def llm_service(self) -> Optional[LLMServiceProtocol]:
|
|
204
|
+
return self.service_initializer.llm_service if self.service_initializer else None
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def audit_service(self) -> Optional[AuditServiceProtocol]:
|
|
208
|
+
return self.service_initializer.audit_service if self.service_initializer else None
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def adaptive_filter_service(self) -> Optional[AdaptiveFilterServiceProtocol]:
|
|
212
|
+
return self.service_initializer.adaptive_filter_service if self.service_initializer else None
|
|
213
|
+
|
|
214
|
+
@property
|
|
215
|
+
def config_manager(self) -> Optional[GraphConfigServiceProtocol]:
|
|
216
|
+
"""Return GraphConfigService for RuntimeControlService compatibility."""
|
|
217
|
+
return self.service_initializer.config_service if self.service_initializer else None
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def secrets_tool_service(self) -> Optional[ToolServiceProtocol]:
|
|
221
|
+
return self.service_initializer.secrets_tool_service if self.service_initializer else None
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def time_service(self) -> Optional[TimeServiceProtocol]:
|
|
225
|
+
return self.service_initializer.time_service if self.service_initializer else None
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def config_service(self) -> Optional[GraphConfigServiceProtocol]:
|
|
229
|
+
"""Access to configuration service."""
|
|
230
|
+
return self.service_initializer.config_service if self.service_initializer else None
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def task_scheduler(self) -> Optional[TaskSchedulerServiceProtocol]:
|
|
234
|
+
"""Access to task scheduler service."""
|
|
235
|
+
return self.service_initializer.task_scheduler_service if self.service_initializer else None
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def authentication_service(self) -> Optional[AuthenticationServiceProtocol]:
|
|
239
|
+
"""Access to authentication service."""
|
|
240
|
+
return self.service_initializer.auth_service if self.service_initializer else None
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def incident_management_service(self) -> Optional[IncidentManagementServiceProtocol]:
|
|
244
|
+
"""Access to incident management service."""
|
|
245
|
+
return self.service_initializer.incident_management_service if self.service_initializer else None
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def runtime_control_service(self) -> Optional[RuntimeControlServiceProtocol]:
|
|
249
|
+
"""Access to runtime control service."""
|
|
250
|
+
return self.service_initializer.runtime_control_service if self.service_initializer else None
|
|
251
|
+
|
|
252
|
+
@property
|
|
253
|
+
def profile(self) -> Optional[Any]:
|
|
254
|
+
"""Convert agent identity to profile format for compatibility."""
|
|
255
|
+
if not self.agent_identity:
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
# Create AgentTemplate from identity
|
|
259
|
+
from ciris_engine.schemas.config.agent import AgentTemplate, DSDMAConfiguration
|
|
260
|
+
|
|
261
|
+
# Create DSDMAConfiguration object if needed
|
|
262
|
+
dsdma_config = None
|
|
263
|
+
if (
|
|
264
|
+
self.agent_identity.core_profile.domain_specific_knowledge
|
|
265
|
+
or self.agent_identity.core_profile.dsdma_prompt_template
|
|
266
|
+
):
|
|
267
|
+
dsdma_config = DSDMAConfiguration(
|
|
268
|
+
domain_specific_knowledge=self.agent_identity.core_profile.domain_specific_knowledge,
|
|
269
|
+
prompt_template=self.agent_identity.core_profile.dsdma_prompt_template,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
return AgentTemplate(
|
|
273
|
+
name=self.agent_identity.agent_id,
|
|
274
|
+
description=self.agent_identity.core_profile.description,
|
|
275
|
+
role_description=self.agent_identity.core_profile.role_description,
|
|
276
|
+
permitted_actions=self.agent_identity.permitted_actions,
|
|
277
|
+
dsdma_kwargs=dsdma_config,
|
|
278
|
+
csdma_overrides=self.agent_identity.core_profile.csdma_overrides,
|
|
279
|
+
action_selection_pdma_overrides=self.agent_identity.core_profile.action_selection_pdma_overrides,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def maintenance_service(self) -> Optional[DatabaseMaintenanceServiceProtocol]:
|
|
284
|
+
return self.service_initializer.maintenance_service if self.service_initializer else None
|
|
285
|
+
|
|
286
|
+
@property
|
|
287
|
+
def database_maintenance_service(self) -> Optional[DatabaseMaintenanceServiceProtocol]:
|
|
288
|
+
"""Alias for maintenance_service - used by API adapter service injection.
|
|
289
|
+
|
|
290
|
+
The API adapter's service configuration expects 'database_maintenance_service'
|
|
291
|
+
while the internal runtime property is named 'maintenance_service'. This alias
|
|
292
|
+
maintains backward compatibility and ensures all 22 core services are accessible.
|
|
293
|
+
"""
|
|
294
|
+
return self.maintenance_service
|
|
295
|
+
|
|
296
|
+
@property
|
|
297
|
+
def shutdown_service(self) -> Optional[ShutdownServiceProtocol]:
|
|
298
|
+
"""Access to shutdown service."""
|
|
299
|
+
return self.service_initializer.shutdown_service if self.service_initializer else None
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def initialization_service(self) -> Optional[InitializationServiceProtocol]:
|
|
303
|
+
"""Access to initialization service."""
|
|
304
|
+
return self.service_initializer.initialization_service if self.service_initializer else None
|
|
305
|
+
|
|
306
|
+
@property
|
|
307
|
+
def tsdb_consolidation_service(self) -> Optional[TSDBConsolidationServiceProtocol]:
|
|
308
|
+
"""Access to TSDB consolidation service."""
|
|
309
|
+
return self.service_initializer.tsdb_consolidation_service if self.service_initializer else None
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def self_observation_service(self) -> Optional[SelfObservationServiceProtocol]:
|
|
313
|
+
"""Access to self observation service."""
|
|
314
|
+
return self.service_initializer.self_observation_service if self.service_initializer else None
|
|
315
|
+
|
|
316
|
+
@property
|
|
317
|
+
def visibility_service(self) -> Optional[VisibilityServiceProtocol]:
|
|
318
|
+
"""Access to visibility service."""
|
|
319
|
+
return self.service_initializer.visibility_service if self.service_initializer else None
|
|
320
|
+
|
|
321
|
+
@property
|
|
322
|
+
def consent_service(self) -> Optional[Any]:
|
|
323
|
+
"""Access to consent service - manages user consent, data retention, and DSAR automation."""
|
|
324
|
+
return self.service_initializer.consent_service if self.service_initializer else None
|
|
325
|
+
|
|
326
|
+
@property
|
|
327
|
+
def agent_template(self) -> Optional[Any]:
|
|
328
|
+
"""Access to full agent template - includes tickets config and all template data."""
|
|
329
|
+
return self.identity_manager.agent_template if self.identity_manager else None
|
|
330
|
+
|
|
331
|
+
def _ensure_shutdown_event(self) -> None:
|
|
332
|
+
"""Ensure shutdown event is created when needed in async context."""
|
|
333
|
+
if self._shutdown_event is None:
|
|
334
|
+
try:
|
|
335
|
+
self._shutdown_event = asyncio.Event()
|
|
336
|
+
except RuntimeError:
|
|
337
|
+
logger.warning("Cannot create shutdown event outside of async context")
|
|
338
|
+
|
|
339
|
+
def _ensure_config(self) -> EssentialConfig:
|
|
340
|
+
"""Ensure essential_config is available, raise if not."""
|
|
341
|
+
if not self.essential_config:
|
|
342
|
+
raise RuntimeError("Essential config not initialized")
|
|
343
|
+
return self.essential_config
|
|
344
|
+
|
|
345
|
+
def request_shutdown(self, reason: str = "Shutdown requested") -> None:
|
|
346
|
+
"""Request a graceful shutdown of the runtime."""
|
|
347
|
+
self._ensure_shutdown_event()
|
|
348
|
+
|
|
349
|
+
if self._shutdown_event and self._shutdown_event.is_set():
|
|
350
|
+
logger.debug(f"Shutdown already requested, ignoring duplicate request: {reason}")
|
|
351
|
+
return
|
|
352
|
+
|
|
353
|
+
logger.critical(f"RUNTIME SHUTDOWN REQUESTED: {reason}")
|
|
354
|
+
self._shutdown_reason = reason
|
|
355
|
+
|
|
356
|
+
if self._shutdown_event:
|
|
357
|
+
self._shutdown_event.set()
|
|
358
|
+
|
|
359
|
+
# Use the sync version from shutdown_manager utils to avoid async/await issues
|
|
360
|
+
from ciris_engine.logic.utils.shutdown_manager import request_global_shutdown
|
|
361
|
+
|
|
362
|
+
request_global_shutdown(f"Runtime: {reason}")
|
|
363
|
+
|
|
364
|
+
def _request_shutdown(self, reason: str = "Shutdown requested") -> None:
|
|
365
|
+
"""Wrapper used during initialization failures."""
|
|
366
|
+
self.request_shutdown(reason)
|
|
367
|
+
|
|
368
|
+
def set_preload_tasks(self, tasks: List[str]) -> None:
|
|
369
|
+
"""Set tasks to be loaded after successful WORK state transition."""
|
|
370
|
+
self._preload_tasks = tasks.copy()
|
|
371
|
+
|
|
372
|
+
async def request_state_transition(self, target_state: str, reason: str) -> bool:
|
|
373
|
+
"""Request a cognitive state transition.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
target_state: Target state name (e.g., "DREAM", "PLAY", "SOLITUDE", "WORK")
|
|
377
|
+
reason: Reason for the transition request
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
True if transition was successful, False otherwise
|
|
381
|
+
"""
|
|
382
|
+
if not self.agent_processor:
|
|
383
|
+
logger.error("Cannot transition state: agent processor not initialized")
|
|
384
|
+
return False
|
|
385
|
+
|
|
386
|
+
# Convert string to AgentState enum (values are lowercase)
|
|
387
|
+
try:
|
|
388
|
+
target = AgentState(target_state.lower())
|
|
389
|
+
except ValueError:
|
|
390
|
+
logger.error(f"Invalid target state: {target_state}")
|
|
391
|
+
return False
|
|
392
|
+
|
|
393
|
+
current_state = self.agent_processor.state_manager.get_state()
|
|
394
|
+
logger.info(f"State transition requested: {current_state.value} -> {target.value} (reason: {reason})")
|
|
395
|
+
|
|
396
|
+
# Use the state manager's transition_to method
|
|
397
|
+
success = await self.agent_processor.state_manager.transition_to(target)
|
|
398
|
+
|
|
399
|
+
if success:
|
|
400
|
+
logger.info(f"State transition successful: {current_state.value} -> {target.value}")
|
|
401
|
+
else:
|
|
402
|
+
logger.warning(f"State transition failed: {current_state.value} -> {target.value}")
|
|
403
|
+
|
|
404
|
+
return success
|
|
405
|
+
|
|
406
|
+
def get_preload_tasks(self) -> List[str]:
|
|
407
|
+
"""Get the list of preload tasks."""
|
|
408
|
+
return self._preload_tasks.copy()
|
|
409
|
+
|
|
410
|
+
async def initialize(self) -> None:
|
|
411
|
+
"""Initialize all components and services."""
|
|
412
|
+
if self._initialized:
|
|
413
|
+
return
|
|
414
|
+
|
|
415
|
+
logger.info("Initializing CIRIS Runtime...")
|
|
416
|
+
|
|
417
|
+
try:
|
|
418
|
+
# CRITICAL: Ensure all directories exist with correct permissions BEFORE anything else
|
|
419
|
+
from ciris_engine.logic.utils.directory_setup import (
|
|
420
|
+
DirectorySetupError,
|
|
421
|
+
setup_application_directories,
|
|
422
|
+
validate_directories,
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
try:
|
|
426
|
+
# In production (when running in container), validate only
|
|
427
|
+
# In development, create directories if needed
|
|
428
|
+
import os
|
|
429
|
+
|
|
430
|
+
is_production = os.environ.get("CIRIS_ENV", "dev").lower() == "prod"
|
|
431
|
+
|
|
432
|
+
if is_production:
|
|
433
|
+
logger.info("Production environment detected - validating directories...")
|
|
434
|
+
validate_directories()
|
|
435
|
+
else:
|
|
436
|
+
logger.info("Development environment - setting up directories...")
|
|
437
|
+
setup_application_directories(essential_config=self.essential_config)
|
|
438
|
+
|
|
439
|
+
except DirectorySetupError as e:
|
|
440
|
+
logger.critical(f"DIRECTORY SETUP FAILED: {e}")
|
|
441
|
+
# This will already have printed clear error messages to stderr
|
|
442
|
+
# and potentially exited the process
|
|
443
|
+
raise RuntimeError(f"Cannot start: Directory setup failed - {e}")
|
|
444
|
+
|
|
445
|
+
# First initialize infrastructure services to get the InitializationService instance
|
|
446
|
+
logger.info("[initialize] Initializing infrastructure services...")
|
|
447
|
+
await self.service_initializer.initialize_infrastructure_services()
|
|
448
|
+
logger.info("[initialize] Infrastructure services initialized")
|
|
449
|
+
|
|
450
|
+
# Get the initialization service from service_initializer
|
|
451
|
+
init_manager = self.service_initializer.initialization_service
|
|
452
|
+
if not init_manager:
|
|
453
|
+
raise RuntimeError("InitializationService not available from ServiceInitializer")
|
|
454
|
+
logger.info(f"[initialize] Got initialization service: {init_manager}")
|
|
455
|
+
|
|
456
|
+
# Register all initialization steps with proper phases
|
|
457
|
+
logger.info("[initialize] Registering initialization steps...")
|
|
458
|
+
self._register_initialization_steps(init_manager)
|
|
459
|
+
logger.info("[initialize] Steps registered")
|
|
460
|
+
|
|
461
|
+
# Run the initialization sequence
|
|
462
|
+
logger.info("[initialize] Running initialization sequence...")
|
|
463
|
+
init_result = await init_manager.initialize()
|
|
464
|
+
logger.info(f"[initialize] Initialization sequence result: {init_result}")
|
|
465
|
+
|
|
466
|
+
if not init_result:
|
|
467
|
+
raise RuntimeError("Initialization sequence failed - check logs for details")
|
|
468
|
+
|
|
469
|
+
self._initialized = True
|
|
470
|
+
agent_name = self.agent_identity.agent_id if self.agent_identity else "NO_IDENTITY"
|
|
471
|
+
logger.info(f"CIRIS Runtime initialized successfully with identity '{agent_name}'")
|
|
472
|
+
|
|
473
|
+
except asyncio.TimeoutError as e:
|
|
474
|
+
logger.critical(f"Runtime initialization TIMED OUT: {e}", exc_info=True)
|
|
475
|
+
self._initialized = False
|
|
476
|
+
raise
|
|
477
|
+
except Exception as e:
|
|
478
|
+
logger.critical(f"Runtime initialization failed: {e}", exc_info=True)
|
|
479
|
+
if "maintenance" in str(e).lower():
|
|
480
|
+
logger.critical("Database maintenance failure during initialization - system cannot start safely")
|
|
481
|
+
self._initialized = False
|
|
482
|
+
raise
|
|
483
|
+
|
|
484
|
+
async def _initialize_identity(self) -> None:
|
|
485
|
+
"""Initialize agent identity - create from template on first run, load from graph thereafter.
|
|
486
|
+
|
|
487
|
+
In first-run mode, this only creates the IdentityManager but does NOT seed the graph.
|
|
488
|
+
The actual identity seeding happens in resume_from_first_run() AFTER the user selects
|
|
489
|
+
their template in the setup wizard.
|
|
490
|
+
"""
|
|
491
|
+
from ciris_engine.logic.setup.first_run import is_first_run
|
|
492
|
+
|
|
493
|
+
config = self._ensure_config()
|
|
494
|
+
if not self.time_service:
|
|
495
|
+
raise RuntimeError("TimeService not available for IdentityManager")
|
|
496
|
+
self.identity_manager = IdentityManager(config, self.time_service)
|
|
497
|
+
|
|
498
|
+
# In first-run mode, skip identity seeding - user hasn't selected template yet
|
|
499
|
+
# Identity will be seeded in resume_from_first_run() after setup completes
|
|
500
|
+
if is_first_run():
|
|
501
|
+
logger.info("First-run mode: Skipping identity seeding (will seed after setup wizard)")
|
|
502
|
+
return
|
|
503
|
+
|
|
504
|
+
self.agent_identity = await self.identity_manager.initialize_identity()
|
|
505
|
+
|
|
506
|
+
# Create startup node for continuity tracking
|
|
507
|
+
await self._create_startup_node()
|
|
508
|
+
|
|
509
|
+
def _register_initialization_steps(self, init_manager: Any) -> None:
|
|
510
|
+
"""Register all initialization steps with the initialization manager."""
|
|
511
|
+
|
|
512
|
+
# Phase 0: INFRASTRUCTURE (NEW - must be first)
|
|
513
|
+
init_manager.register_step(
|
|
514
|
+
phase=InitializationPhase.INFRASTRUCTURE,
|
|
515
|
+
name="Initialize Infrastructure Services",
|
|
516
|
+
handler=self._initialize_infrastructure,
|
|
517
|
+
verifier=self._verify_infrastructure,
|
|
518
|
+
critical=True,
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
# Phase 1: DATABASE
|
|
522
|
+
init_manager.register_step(
|
|
523
|
+
phase=InitializationPhase.DATABASE,
|
|
524
|
+
name="Initialize Database",
|
|
525
|
+
handler=self._init_database,
|
|
526
|
+
verifier=self._verify_database_integrity,
|
|
527
|
+
critical=True,
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
# Phase 2: MEMORY
|
|
531
|
+
init_manager.register_step(
|
|
532
|
+
phase=InitializationPhase.MEMORY,
|
|
533
|
+
name="Memory Service",
|
|
534
|
+
handler=self._initialize_memory_service,
|
|
535
|
+
verifier=self._verify_memory_service,
|
|
536
|
+
critical=True,
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
# Phase 3: IDENTITY
|
|
540
|
+
init_manager.register_step(
|
|
541
|
+
phase=InitializationPhase.IDENTITY,
|
|
542
|
+
name="Agent Identity",
|
|
543
|
+
handler=self._initialize_identity,
|
|
544
|
+
verifier=self._verify_identity_integrity,
|
|
545
|
+
critical=True,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
# Phase 4: SECURITY
|
|
549
|
+
init_manager.register_step(
|
|
550
|
+
phase=InitializationPhase.SECURITY,
|
|
551
|
+
name="Security Services",
|
|
552
|
+
handler=self._initialize_security_services,
|
|
553
|
+
verifier=self._verify_security_services,
|
|
554
|
+
critical=True,
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
# Phase 5: SERVICES
|
|
558
|
+
init_manager.register_step(
|
|
559
|
+
phase=InitializationPhase.SERVICES,
|
|
560
|
+
name="Core Services",
|
|
561
|
+
handler=self._initialize_services,
|
|
562
|
+
verifier=self._verify_core_services,
|
|
563
|
+
critical=True,
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
# Start adapters and wait for critical services
|
|
567
|
+
init_manager.register_step(
|
|
568
|
+
phase=InitializationPhase.SERVICES, name="Start Adapters", handler=self._start_adapters, critical=True
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
# Register adapter services immediately after adapters start
|
|
572
|
+
# This ensures communication and other adapter services are available before components build
|
|
573
|
+
init_manager.register_step(
|
|
574
|
+
phase=InitializationPhase.SERVICES,
|
|
575
|
+
name="Register Adapter Services",
|
|
576
|
+
handler=self._register_adapter_services,
|
|
577
|
+
critical=True,
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
# Initialize maintenance service and perform cleanup BEFORE components
|
|
581
|
+
init_manager.register_step(
|
|
582
|
+
phase=InitializationPhase.SERVICES,
|
|
583
|
+
name="Initialize Maintenance Service",
|
|
584
|
+
handler=self._initialize_maintenance_service,
|
|
585
|
+
critical=True,
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
# Adapter connections will be started in COMPONENTS phase after services are ready
|
|
589
|
+
|
|
590
|
+
# Phase 6: COMPONENTS
|
|
591
|
+
init_manager.register_step(
|
|
592
|
+
phase=InitializationPhase.COMPONENTS, name="Build Components", handler=self._build_components, critical=True
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
# Start adapter connections FIRST to establish Discord connection
|
|
596
|
+
init_manager.register_step(
|
|
597
|
+
phase=InitializationPhase.COMPONENTS,
|
|
598
|
+
name="Start Adapter Connections",
|
|
599
|
+
handler=self._start_adapter_connections,
|
|
600
|
+
critical=True,
|
|
601
|
+
timeout=45.0,
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
# Adapter services are now registered inside _start_adapter_connections
|
|
605
|
+
# after waiting for adapters to be healthy
|
|
606
|
+
|
|
607
|
+
# Phase 7: VERIFICATION
|
|
608
|
+
init_manager.register_step(
|
|
609
|
+
phase=InitializationPhase.VERIFICATION,
|
|
610
|
+
name="Final System Verification",
|
|
611
|
+
handler=self._final_verification,
|
|
612
|
+
critical=True,
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
async def _initialize_infrastructure(self) -> None: # NOSONAR: Part of async initialization chain
|
|
616
|
+
"""Initialize infrastructure services that all other services depend on."""
|
|
617
|
+
# Infrastructure services already initialized in initialize() method
|
|
618
|
+
|
|
619
|
+
# CRITICAL: File logging is REQUIRED for production
|
|
620
|
+
# FAIL FAST AND LOUD if we can't set it up
|
|
621
|
+
import os
|
|
622
|
+
|
|
623
|
+
if not os.environ.get("PYTEST_CURRENT_TEST"):
|
|
624
|
+
from ciris_engine.logic.utils.logging_config import setup_basic_logging
|
|
625
|
+
|
|
626
|
+
# Get TimeService from service initializer
|
|
627
|
+
time_service = self.service_initializer.time_service
|
|
628
|
+
if not time_service:
|
|
629
|
+
error_msg = "CRITICAL: TimeService not available - CANNOT INITIALIZE FILE LOGGING"
|
|
630
|
+
logger.critical(error_msg)
|
|
631
|
+
raise RuntimeError(error_msg)
|
|
632
|
+
|
|
633
|
+
try:
|
|
634
|
+
setup_basic_logging(
|
|
635
|
+
level=logging.DEBUG if self.debug else logging.INFO,
|
|
636
|
+
log_to_file=True,
|
|
637
|
+
console_output=False, # Already logging to console from main.py
|
|
638
|
+
enable_incident_capture=True,
|
|
639
|
+
time_service=time_service,
|
|
640
|
+
# log_dir defaults to None, which uses path_resolution.get_logs_dir()
|
|
641
|
+
)
|
|
642
|
+
logger.info("[_initialize_infrastructure] File logging initialized successfully")
|
|
643
|
+
except Exception as e:
|
|
644
|
+
error_msg = f"CRITICAL: Failed to setup file logging: {e}"
|
|
645
|
+
logger.critical(error_msg)
|
|
646
|
+
raise RuntimeError(error_msg)
|
|
647
|
+
else:
|
|
648
|
+
logger.debug("[_initialize_infrastructure] Test mode detected, skipping file logging setup")
|
|
649
|
+
|
|
650
|
+
async def _verify_infrastructure(self) -> bool:
|
|
651
|
+
"""Verify infrastructure services are operational."""
|
|
652
|
+
# Check that all infrastructure services are running
|
|
653
|
+
if not self.service_initializer.time_service:
|
|
654
|
+
logger.error("TimeService not initialized")
|
|
655
|
+
return False
|
|
656
|
+
if not self.service_initializer.shutdown_service:
|
|
657
|
+
logger.error("ShutdownService not initialized")
|
|
658
|
+
return False
|
|
659
|
+
if not self.service_initializer.initialization_service:
|
|
660
|
+
logger.error("InitializationService not initialized")
|
|
661
|
+
return False
|
|
662
|
+
return True
|
|
663
|
+
|
|
664
|
+
async def _init_database(self) -> None:
|
|
665
|
+
"""Initialize database and run migrations."""
|
|
666
|
+
# Use environment-based database URL if set, otherwise use SQLite path from config
|
|
667
|
+
# This allows PostgreSQL support via CIRIS_DB_URL environment variable
|
|
668
|
+
from ciris_engine.logic.persistence.db.dialect import get_adapter
|
|
669
|
+
|
|
670
|
+
adapter = get_adapter()
|
|
671
|
+
if adapter.is_postgresql():
|
|
672
|
+
# PostgreSQL: Use None to trigger environment-based connection
|
|
673
|
+
db_path = None
|
|
674
|
+
logger.info("Using PostgreSQL database from environment (CIRIS_DB_URL)")
|
|
675
|
+
else:
|
|
676
|
+
# SQLite: Use direct path from essential_config to avoid config service dependency
|
|
677
|
+
db_path = str(self.essential_config.database.main_db)
|
|
678
|
+
logger.info(f"Using SQLite database: {db_path}")
|
|
679
|
+
|
|
680
|
+
persistence.initialize_database(db_path)
|
|
681
|
+
persistence.run_migrations(db_path)
|
|
682
|
+
|
|
683
|
+
if not self.essential_config:
|
|
684
|
+
# Use default essential config if none provided
|
|
685
|
+
self.essential_config = EssentialConfig()
|
|
686
|
+
self.essential_config.load_env_vars() # Load CIRIS_DB_URL and other env vars
|
|
687
|
+
logger.warning("No config provided, using defaults")
|
|
688
|
+
|
|
689
|
+
async def _verify_database_integrity(self) -> bool:
|
|
690
|
+
"""Verify database integrity before proceeding."""
|
|
691
|
+
try:
|
|
692
|
+
from ciris_engine.logic.persistence.db.dialect import get_adapter
|
|
693
|
+
|
|
694
|
+
adapter = get_adapter()
|
|
695
|
+
# Use environment-based connection for PostgreSQL, direct path for SQLite
|
|
696
|
+
db_path = None if adapter.is_postgresql() else str(self.essential_config.database.main_db)
|
|
697
|
+
conn = persistence.get_db_connection(db_path)
|
|
698
|
+
cursor = conn.cursor()
|
|
699
|
+
|
|
700
|
+
adapter = get_adapter()
|
|
701
|
+
required_tables = ["tasks", "thoughts", "graph_nodes", "graph_edges"]
|
|
702
|
+
|
|
703
|
+
for table in required_tables:
|
|
704
|
+
# Use database-specific query
|
|
705
|
+
if adapter.is_postgresql():
|
|
706
|
+
cursor.execute(
|
|
707
|
+
"SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_name=%s",
|
|
708
|
+
(table,),
|
|
709
|
+
)
|
|
710
|
+
else:
|
|
711
|
+
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table,))
|
|
712
|
+
|
|
713
|
+
if not cursor.fetchone():
|
|
714
|
+
raise RuntimeError(f"Required table '{table}' missing from database")
|
|
715
|
+
|
|
716
|
+
conn.close()
|
|
717
|
+
logger.info("✓ Database integrity verified")
|
|
718
|
+
return True
|
|
719
|
+
except Exception as e:
|
|
720
|
+
logger.error(f"Database integrity check failed: {e}")
|
|
721
|
+
return False
|
|
722
|
+
|
|
723
|
+
async def _initialize_memory_service(self) -> None:
|
|
724
|
+
"""Initialize memory service early for identity storage."""
|
|
725
|
+
config = self._ensure_config()
|
|
726
|
+
await self.service_initializer.initialize_memory_service(config)
|
|
727
|
+
|
|
728
|
+
async def _verify_memory_service(self) -> bool:
|
|
729
|
+
"""Verify memory service is operational."""
|
|
730
|
+
return await self.service_initializer.verify_memory_service()
|
|
731
|
+
|
|
732
|
+
async def _verify_identity_integrity(self) -> bool:
|
|
733
|
+
"""Verify identity was properly established.
|
|
734
|
+
|
|
735
|
+
In first-run mode, identity is not seeded yet (waiting for user to select template),
|
|
736
|
+
so we only verify that the identity manager was created.
|
|
737
|
+
"""
|
|
738
|
+
from ciris_engine.logic.setup.first_run import is_first_run
|
|
739
|
+
|
|
740
|
+
if not self.identity_manager:
|
|
741
|
+
logger.error("Identity manager not initialized")
|
|
742
|
+
return False
|
|
743
|
+
|
|
744
|
+
# In first-run mode, identity isn't seeded yet - just verify manager exists
|
|
745
|
+
if is_first_run():
|
|
746
|
+
logger.info("First-run mode: Identity manager created (identity will be seeded after setup)")
|
|
747
|
+
return True
|
|
748
|
+
|
|
749
|
+
return await self.identity_manager.verify_identity_integrity()
|
|
750
|
+
|
|
751
|
+
async def _initialize_security_services(self) -> None:
|
|
752
|
+
"""Initialize security-critical services first."""
|
|
753
|
+
config = self._ensure_config()
|
|
754
|
+
await self.service_initializer.initialize_security_services(config, self.essential_config)
|
|
755
|
+
|
|
756
|
+
async def _verify_security_services(self) -> bool:
|
|
757
|
+
"""Verify security services are operational."""
|
|
758
|
+
return await self.service_initializer.verify_security_services()
|
|
759
|
+
|
|
760
|
+
async def _initialize_services(self) -> None:
|
|
761
|
+
"""Initialize all remaining core services.
|
|
762
|
+
|
|
763
|
+
In first-run mode, identity is not yet established (user selects template in setup wizard).
|
|
764
|
+
We skip full service initialization - only the API adapter runs for the setup wizard.
|
|
765
|
+
"""
|
|
766
|
+
from ciris_engine.logic.setup.first_run import is_first_run
|
|
767
|
+
|
|
768
|
+
config = self._ensure_config()
|
|
769
|
+
|
|
770
|
+
# In first-run mode, skip service initialization - we only need the API server
|
|
771
|
+
if is_first_run():
|
|
772
|
+
logger.info("First-run mode: Skipping core service initialization (setup wizard only)")
|
|
773
|
+
return
|
|
774
|
+
|
|
775
|
+
# Identity MUST be established before services can be initialized
|
|
776
|
+
if not self.agent_identity:
|
|
777
|
+
raise RuntimeError("CRITICAL: Cannot initialize services without agent identity")
|
|
778
|
+
await self.service_initializer.initialize_all_services(
|
|
779
|
+
config, self.essential_config, self.agent_identity.agent_id, self.startup_channel_id, self.modules_to_load
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
# Load any external modules (e.g. mockllm)
|
|
783
|
+
if self.modules_to_load:
|
|
784
|
+
logger.info(f"Loading {len(self.modules_to_load)} external modules: {self.modules_to_load}")
|
|
785
|
+
await self.service_initializer.load_modules(self.modules_to_load)
|
|
786
|
+
|
|
787
|
+
# Set runtime on audit service so it can create trace correlations
|
|
788
|
+
if self.audit_service:
|
|
789
|
+
self.audit_service._runtime = self # type: ignore[attr-defined]
|
|
790
|
+
logger.debug("Set runtime reference on audit service for trace correlations")
|
|
791
|
+
|
|
792
|
+
# Set runtime on visibility service so it can access telemetry for traces
|
|
793
|
+
if self.visibility_service:
|
|
794
|
+
self.visibility_service._runtime = self # type: ignore[attr-defined]
|
|
795
|
+
logger.debug("Set runtime reference on visibility service for trace retrieval")
|
|
796
|
+
|
|
797
|
+
# Update runtime control service with runtime reference
|
|
798
|
+
if self.runtime_control_service:
|
|
799
|
+
if hasattr(self.runtime_control_service, "_set_runtime"):
|
|
800
|
+
self.runtime_control_service._set_runtime(self)
|
|
801
|
+
else:
|
|
802
|
+
self.runtime_control_service.runtime = self # type: ignore[attr-defined]
|
|
803
|
+
logger.info("Updated runtime control service with runtime reference")
|
|
804
|
+
|
|
805
|
+
# Update telemetry service with runtime reference for aggregator
|
|
806
|
+
if self.telemetry_service:
|
|
807
|
+
if hasattr(self.telemetry_service, "_set_runtime"):
|
|
808
|
+
self.telemetry_service._set_runtime(self)
|
|
809
|
+
logger.info("Updated telemetry service with runtime reference for aggregator")
|
|
810
|
+
|
|
811
|
+
async def _verify_core_services(self) -> bool:
|
|
812
|
+
"""Verify all core services are operational.
|
|
813
|
+
|
|
814
|
+
In first-run mode, services aren't initialized yet - just return True.
|
|
815
|
+
"""
|
|
816
|
+
from ciris_engine.logic.setup.first_run import is_first_run
|
|
817
|
+
|
|
818
|
+
if is_first_run():
|
|
819
|
+
logger.info("First-run mode: Core services verification skipped")
|
|
820
|
+
return True
|
|
821
|
+
|
|
822
|
+
return self.service_initializer.verify_core_services()
|
|
823
|
+
|
|
824
|
+
async def _initialize_maintenance_service(self) -> None:
|
|
825
|
+
"""Initialize the maintenance service and perform startup cleanup.
|
|
826
|
+
|
|
827
|
+
In first-run mode, services aren't initialized - skip maintenance.
|
|
828
|
+
"""
|
|
829
|
+
from ciris_engine.logic.setup.first_run import is_first_run
|
|
830
|
+
|
|
831
|
+
if is_first_run():
|
|
832
|
+
logger.info("First-run mode: Skipping maintenance service initialization")
|
|
833
|
+
return
|
|
834
|
+
|
|
835
|
+
# Verify maintenance service is available
|
|
836
|
+
if not self.maintenance_service:
|
|
837
|
+
raise RuntimeError("Maintenance service was not initialized properly")
|
|
838
|
+
logger.info("Maintenance service verified available")
|
|
839
|
+
|
|
840
|
+
# Perform startup maintenance to clean stale tasks
|
|
841
|
+
await self._perform_startup_maintenance()
|
|
842
|
+
|
|
843
|
+
async def _start_adapters(self) -> None:
|
|
844
|
+
"""Start all adapters."""
|
|
845
|
+
await asyncio.gather(*(adapter.start() for adapter in self.adapters))
|
|
846
|
+
logger.info(f"All {len(self.adapters)} adapters started")
|
|
847
|
+
|
|
848
|
+
# Migrate adapter configurations to graph config
|
|
849
|
+
await self._migrate_adapter_configs_to_graph()
|
|
850
|
+
|
|
851
|
+
async def _wait_for_critical_services(self, timeout: float) -> None:
|
|
852
|
+
"""Wait for services required for agent operation."""
|
|
853
|
+
from ciris_engine.schemas.runtime.enums import ServiceType
|
|
854
|
+
|
|
855
|
+
start_time = asyncio.get_event_loop().time()
|
|
856
|
+
last_report_time = start_time
|
|
857
|
+
|
|
858
|
+
required_services = [
|
|
859
|
+
(ServiceType.COMMUNICATION, ["send_message"], "Communication (Discord/API/CLI)"),
|
|
860
|
+
]
|
|
861
|
+
|
|
862
|
+
while (asyncio.get_event_loop().time() - start_time) < timeout:
|
|
863
|
+
all_ready = True
|
|
864
|
+
missing_services = []
|
|
865
|
+
|
|
866
|
+
for service_type, capabilities, name in required_services:
|
|
867
|
+
if self.service_registry:
|
|
868
|
+
service = await self.service_registry.get_service(
|
|
869
|
+
handler="SpeakHandler", # Use a handler that requires communication
|
|
870
|
+
service_type=service_type,
|
|
871
|
+
required_capabilities=capabilities,
|
|
872
|
+
)
|
|
873
|
+
if not service:
|
|
874
|
+
all_ready = False
|
|
875
|
+
missing_services.append(name)
|
|
876
|
+
else:
|
|
877
|
+
# Check if service is actually healthy (connected)
|
|
878
|
+
if hasattr(service, "is_healthy"):
|
|
879
|
+
is_healthy = await service.is_healthy()
|
|
880
|
+
if not is_healthy:
|
|
881
|
+
all_ready = False
|
|
882
|
+
missing_services.append(f"{name} (registered but not connected)")
|
|
883
|
+
else:
|
|
884
|
+
# Report Discord connection details
|
|
885
|
+
service_name = service.__class__.__name__ if service else "Unknown"
|
|
886
|
+
if "Discord" in service_name and not hasattr(self, "_discord_connected_reported"):
|
|
887
|
+
# Get Discord client info
|
|
888
|
+
if (
|
|
889
|
+
hasattr(service, "client")
|
|
890
|
+
and service.client
|
|
891
|
+
and hasattr(service.client, "user")
|
|
892
|
+
):
|
|
893
|
+
user = service.client.user
|
|
894
|
+
guilds = service.client.guilds if hasattr(service.client, "guilds") else []
|
|
895
|
+
logger.info(f" ✓ Discord connected as {user} to {len(guilds)} guild(s)")
|
|
896
|
+
for guild in guilds[:3]: # Show first 3 guilds
|
|
897
|
+
logger.info(f" - {guild.name} (ID: {guild.id})")
|
|
898
|
+
if len(guilds) > 3:
|
|
899
|
+
logger.info(f" ... and {len(guilds) - 3} more guild(s)")
|
|
900
|
+
self._discord_connected_reported = True
|
|
901
|
+
|
|
902
|
+
if all_ready:
|
|
903
|
+
return
|
|
904
|
+
|
|
905
|
+
# Report progress every 3 seconds
|
|
906
|
+
current_time = asyncio.get_event_loop().time()
|
|
907
|
+
if current_time - last_report_time >= 3.0:
|
|
908
|
+
elapsed = current_time - start_time
|
|
909
|
+
logger.info(f" ⏳ Still waiting for: {', '.join(missing_services)} ({elapsed:.1f}s elapsed)")
|
|
910
|
+
last_report_time = current_time
|
|
911
|
+
|
|
912
|
+
await asyncio.sleep(0.5)
|
|
913
|
+
|
|
914
|
+
# Timeout reached
|
|
915
|
+
raise TimeoutError(f"Critical services not available after {timeout}s. Missing: {', '.join(missing_services)}")
|
|
916
|
+
|
|
917
|
+
async def _migrate_adapter_configs_to_graph(self) -> None:
|
|
918
|
+
"""Migrate adapter configurations to graph config service."""
|
|
919
|
+
if not self.service_initializer or not self.service_initializer.config_service:
|
|
920
|
+
logger.warning("Cannot migrate adapter configs - GraphConfigService not available")
|
|
921
|
+
return
|
|
922
|
+
|
|
923
|
+
config_service = self.service_initializer.config_service
|
|
924
|
+
|
|
925
|
+
# Migrate bootstrap adapter configs
|
|
926
|
+
for adapter_type, adapter_config in self.adapter_configs.items():
|
|
927
|
+
try:
|
|
928
|
+
# Determine adapter ID (handle instance-specific types like "api:8081")
|
|
929
|
+
if ":" in adapter_type:
|
|
930
|
+
base_type, instance_id = adapter_type.split(":", 1)
|
|
931
|
+
adapter_id = f"{base_type}_{instance_id}"
|
|
932
|
+
else:
|
|
933
|
+
# For bootstrap adapters without instance ID, use a standard naming
|
|
934
|
+
adapter_id = f"{adapter_type}_bootstrap"
|
|
935
|
+
|
|
936
|
+
# Store the full config object
|
|
937
|
+
await config_service.set_config(
|
|
938
|
+
key=f"adapter.{adapter_id}.config",
|
|
939
|
+
value=adapter_config.model_dump() if hasattr(adapter_config, "model_dump") else adapter_config,
|
|
940
|
+
updated_by="system_bootstrap",
|
|
941
|
+
)
|
|
942
|
+
|
|
943
|
+
# Also store individual config values for easy access
|
|
944
|
+
config_dict = adapter_config.model_dump() if hasattr(adapter_config, "model_dump") else adapter_config
|
|
945
|
+
if isinstance(config_dict, dict):
|
|
946
|
+
for key, value in config_dict.items():
|
|
947
|
+
await config_service.set_config(
|
|
948
|
+
key=f"adapter.{adapter_id}.{key}", value=value, updated_by="system_bootstrap"
|
|
949
|
+
)
|
|
950
|
+
|
|
951
|
+
logger.info(f"Migrated adapter config for {adapter_id} to graph")
|
|
952
|
+
|
|
953
|
+
except Exception as e:
|
|
954
|
+
logger.error(f"Failed to migrate adapter config for {adapter_type}: {e}")
|
|
955
|
+
|
|
956
|
+
# Migrate tickets config from template (first-run only)
|
|
957
|
+
await self._migrate_tickets_config_to_graph()
|
|
958
|
+
|
|
959
|
+
# Migrate cognitive state behaviors (pre-1.7 compatibility)
|
|
960
|
+
await self._migrate_cognitive_state_behaviors_to_graph()
|
|
961
|
+
|
|
962
|
+
async def _migrate_tickets_config_to_graph(self) -> None:
|
|
963
|
+
"""Migrate tickets config to graph.
|
|
964
|
+
|
|
965
|
+
This handles two scenarios:
|
|
966
|
+
1. First-run: Seeds tickets config from template to graph
|
|
967
|
+
2. Pre-1.7.0 upgrade: Adds default DSAR SOPs for existing agents without tickets config
|
|
968
|
+
|
|
969
|
+
After migration, tickets.py retrieves config from graph, not template.
|
|
970
|
+
"""
|
|
971
|
+
if not self.service_initializer or not self.service_initializer.config_service:
|
|
972
|
+
logger.warning("Cannot migrate tickets config - GraphConfigService not available")
|
|
973
|
+
return
|
|
974
|
+
|
|
975
|
+
config_service = self.service_initializer.config_service
|
|
976
|
+
|
|
977
|
+
# Check if tickets config already exists in graph
|
|
978
|
+
try:
|
|
979
|
+
existing_config = await config_service.get_config("tickets")
|
|
980
|
+
if existing_config and existing_config.value and existing_config.value.dict_value:
|
|
981
|
+
logger.debug("Tickets config already exists in graph - skipping migration")
|
|
982
|
+
return
|
|
983
|
+
except Exception:
|
|
984
|
+
pass # Config doesn't exist, proceed with migration
|
|
985
|
+
|
|
986
|
+
# Try to get tickets config from template (first-run scenario)
|
|
987
|
+
tickets_config = None
|
|
988
|
+
if self.identity_manager and self.identity_manager.agent_template:
|
|
989
|
+
tickets_config = self.identity_manager.agent_template.tickets
|
|
990
|
+
|
|
991
|
+
# If no template available (pre-1.7.0 agent upgrade), create default DSAR SOPs
|
|
992
|
+
if not tickets_config:
|
|
993
|
+
logger.info("No tickets config found - creating default DSAR SOPs for pre-1.7.0 compatibility")
|
|
994
|
+
from ciris_engine.schemas.config.default_dsar_sops import DEFAULT_DSAR_SOPS
|
|
995
|
+
from ciris_engine.schemas.config.tickets import TicketsConfig
|
|
996
|
+
|
|
997
|
+
tickets_config = TicketsConfig(enabled=True, sops=DEFAULT_DSAR_SOPS)
|
|
998
|
+
|
|
999
|
+
try:
|
|
1000
|
+
# Store tickets config as a dict in the graph with IDENTITY scope (WA-protected)
|
|
1001
|
+
from ciris_engine.schemas.services.graph_core import GraphScope
|
|
1002
|
+
|
|
1003
|
+
await config_service.set_config(
|
|
1004
|
+
key="tickets",
|
|
1005
|
+
value=tickets_config.model_dump(),
|
|
1006
|
+
updated_by="system_bootstrap",
|
|
1007
|
+
scope=GraphScope.IDENTITY, # Protected - agent cannot modify
|
|
1008
|
+
)
|
|
1009
|
+
logger.info("Migrated tickets config to graph (IDENTITY scope - WA-protected)")
|
|
1010
|
+
except Exception as e:
|
|
1011
|
+
logger.error(f"Failed to migrate tickets config to graph: {e}")
|
|
1012
|
+
|
|
1013
|
+
def _should_skip_cognitive_migration(self, force_from_template: bool) -> bool:
|
|
1014
|
+
"""Check if cognitive migration should be skipped (first-run mode without force)."""
|
|
1015
|
+
from ciris_engine.logic.setup.first_run import is_first_run
|
|
1016
|
+
|
|
1017
|
+
if is_first_run() and not force_from_template:
|
|
1018
|
+
logger.info("[COGNITIVE_MIGRATION] First-run mode: Skipping migration (will seed after setup wizard)")
|
|
1019
|
+
return True
|
|
1020
|
+
return False
|
|
1021
|
+
|
|
1022
|
+
async def _check_existing_cognitive_config(self, config_service: Any) -> bool:
|
|
1023
|
+
"""Check if cognitive config already exists in graph.
|
|
1024
|
+
|
|
1025
|
+
Returns True if config exists and should skip migration.
|
|
1026
|
+
"""
|
|
1027
|
+
try:
|
|
1028
|
+
existing_config = await config_service.get_config("cognitive_state_behaviors")
|
|
1029
|
+
if existing_config and existing_config.value and existing_config.value.dict_value:
|
|
1030
|
+
existing_wakeup = existing_config.value.dict_value.get("wakeup", {})
|
|
1031
|
+
logger.info(
|
|
1032
|
+
f"[COGNITIVE_MIGRATION] Config already exists in graph - wakeup.enabled={existing_wakeup.get('enabled', 'MISSING')}"
|
|
1033
|
+
)
|
|
1034
|
+
logger.info("[COGNITIVE_MIGRATION] Skipping migration (existing config preserved)")
|
|
1035
|
+
return True
|
|
1036
|
+
except Exception as e:
|
|
1037
|
+
logger.info(f"[COGNITIVE_MIGRATION] No existing config in graph (will migrate): {e}")
|
|
1038
|
+
return False
|
|
1039
|
+
|
|
1040
|
+
def _get_cognitive_behaviors_from_template(self) -> Optional[Any]:
|
|
1041
|
+
"""Get cognitive behaviors from the agent template if available."""
|
|
1042
|
+
logger.info(f"[COGNITIVE_MIGRATION] identity_manager={self.identity_manager is not None}")
|
|
1043
|
+
if not self.identity_manager or not self.identity_manager.agent_template:
|
|
1044
|
+
logger.info("[COGNITIVE_MIGRATION] No template available (identity_manager or agent_template is None)")
|
|
1045
|
+
return None
|
|
1046
|
+
|
|
1047
|
+
template = self.identity_manager.agent_template
|
|
1048
|
+
logger.info(f"[COGNITIVE_MIGRATION] Template loaded: name={getattr(template, 'name', 'UNKNOWN')}")
|
|
1049
|
+
cognitive_behaviors = getattr(template, "cognitive_state_behaviors", None)
|
|
1050
|
+
if cognitive_behaviors:
|
|
1051
|
+
logger.info(
|
|
1052
|
+
f"[COGNITIVE_MIGRATION] Template has cognitive_state_behaviors: wakeup.enabled={cognitive_behaviors.wakeup.enabled}"
|
|
1053
|
+
)
|
|
1054
|
+
else:
|
|
1055
|
+
logger.info("[COGNITIVE_MIGRATION] Template has NO cognitive_state_behaviors attribute")
|
|
1056
|
+
return cognitive_behaviors
|
|
1057
|
+
|
|
1058
|
+
def _create_legacy_cognitive_behaviors(self) -> Any:
|
|
1059
|
+
"""Create pre-1.7 compatible cognitive behaviors config."""
|
|
1060
|
+
from ciris_engine.schemas.config.cognitive_state_behaviors import (
|
|
1061
|
+
CognitiveStateBehaviors,
|
|
1062
|
+
DreamBehavior,
|
|
1063
|
+
StateBehavior,
|
|
1064
|
+
StatePreservationBehavior,
|
|
1065
|
+
)
|
|
1066
|
+
|
|
1067
|
+
logger.info("No cognitive state behaviors found - creating pre-1.7 compatible config")
|
|
1068
|
+
return CognitiveStateBehaviors(
|
|
1069
|
+
play=StateBehavior(
|
|
1070
|
+
enabled=False,
|
|
1071
|
+
rationale="Pre-1.7 agent: PLAY state not available in legacy version",
|
|
1072
|
+
),
|
|
1073
|
+
dream=DreamBehavior(
|
|
1074
|
+
enabled=False,
|
|
1075
|
+
auto_schedule=False,
|
|
1076
|
+
rationale="Pre-1.7 agent: DREAM state not available in legacy version",
|
|
1077
|
+
),
|
|
1078
|
+
solitude=StateBehavior(
|
|
1079
|
+
enabled=False,
|
|
1080
|
+
rationale="Pre-1.7 agent: SOLITUDE state not available in legacy version",
|
|
1081
|
+
),
|
|
1082
|
+
state_preservation=StatePreservationBehavior(
|
|
1083
|
+
enabled=True,
|
|
1084
|
+
resume_silently=False,
|
|
1085
|
+
rationale="Pre-1.7 agent: preserve state across restarts",
|
|
1086
|
+
),
|
|
1087
|
+
)
|
|
1088
|
+
|
|
1089
|
+
async def _save_cognitive_behaviors_to_graph(self, config_service: Any, cognitive_behaviors: Any) -> None:
|
|
1090
|
+
"""Save cognitive behaviors to the graph with IDENTITY scope."""
|
|
1091
|
+
from ciris_engine.schemas.services.graph_core import GraphScope
|
|
1092
|
+
|
|
1093
|
+
config_dict = cognitive_behaviors.model_dump()
|
|
1094
|
+
logger.info(
|
|
1095
|
+
f"[COGNITIVE_MIGRATION] Saving to graph: wakeup.enabled={config_dict.get('wakeup', {}).get('enabled', 'MISSING')}"
|
|
1096
|
+
)
|
|
1097
|
+
await config_service.set_config(
|
|
1098
|
+
key="cognitive_state_behaviors",
|
|
1099
|
+
value=config_dict,
|
|
1100
|
+
updated_by="system_bootstrap",
|
|
1101
|
+
scope=GraphScope.IDENTITY,
|
|
1102
|
+
)
|
|
1103
|
+
logger.info("[COGNITIVE_MIGRATION] SUCCESS - Migrated cognitive state behaviors to graph (IDENTITY scope)")
|
|
1104
|
+
|
|
1105
|
+
async def _migrate_cognitive_state_behaviors_to_graph(self, force_from_template: bool = False) -> None:
|
|
1106
|
+
"""Migrate cognitive state behaviors to graph.
|
|
1107
|
+
|
|
1108
|
+
This handles two scenarios:
|
|
1109
|
+
1. First-run: Seeds cognitive behaviors from template to graph
|
|
1110
|
+
2. Pre-1.7.0 upgrade: Adds legacy-compatible behaviors (PLAY/DREAM/SOLITUDE disabled)
|
|
1111
|
+
|
|
1112
|
+
Pre-1.7 agents get:
|
|
1113
|
+
- Wakeup: enabled (full identity ceremony)
|
|
1114
|
+
- Shutdown: always_consent (Covenant compliance)
|
|
1115
|
+
- Play/Dream/Solitude: DISABLED (these states didn't exist pre-1.7)
|
|
1116
|
+
|
|
1117
|
+
After migration, StateManager retrieves config from graph, not template.
|
|
1118
|
+
|
|
1119
|
+
Args:
|
|
1120
|
+
force_from_template: If True, always seed from template (used during resume_from_first_run
|
|
1121
|
+
when template is now available). This overwrites any pre-existing config.
|
|
1122
|
+
"""
|
|
1123
|
+
if self._should_skip_cognitive_migration(force_from_template):
|
|
1124
|
+
return
|
|
1125
|
+
|
|
1126
|
+
if not self.service_initializer or not self.service_initializer.config_service:
|
|
1127
|
+
logger.warning("[COGNITIVE_MIGRATION] Cannot migrate - GraphConfigService not available")
|
|
1128
|
+
return
|
|
1129
|
+
|
|
1130
|
+
config_service = self.service_initializer.config_service
|
|
1131
|
+
|
|
1132
|
+
logger.info("[COGNITIVE_MIGRATION] Starting cognitive state behaviors migration check...")
|
|
1133
|
+
logger.info(f"[COGNITIVE_MIGRATION] force_from_template={force_from_template}")
|
|
1134
|
+
|
|
1135
|
+
if not force_from_template:
|
|
1136
|
+
if await self._check_existing_cognitive_config(config_service):
|
|
1137
|
+
return
|
|
1138
|
+
else:
|
|
1139
|
+
logger.info("[COGNITIVE_MIGRATION] Force mode: Will overwrite existing config with template values")
|
|
1140
|
+
|
|
1141
|
+
# Try to get cognitive behaviors from template
|
|
1142
|
+
cognitive_behaviors = self._get_cognitive_behaviors_from_template()
|
|
1143
|
+
|
|
1144
|
+
# If no template available, use Covenant-compliant defaults (all states enabled)
|
|
1145
|
+
# This applies to fresh installs without templates (e.g., QA testing, API-only mode)
|
|
1146
|
+
# Note: The old pre-1.7 upgrade logic disabled PLAY/DREAM/SOLITUDE, but this was
|
|
1147
|
+
# overly conservative. Default behavior should enable all states - users can disable
|
|
1148
|
+
# specific states via template configuration if needed.
|
|
1149
|
+
if not cognitive_behaviors:
|
|
1150
|
+
from ciris_engine.schemas.config.cognitive_state_behaviors import CognitiveStateBehaviors
|
|
1151
|
+
|
|
1152
|
+
logger.info("[COGNITIVE_MIGRATION] No template - using Covenant-compliant defaults (all states enabled)")
|
|
1153
|
+
cognitive_behaviors = CognitiveStateBehaviors()
|
|
1154
|
+
|
|
1155
|
+
try:
|
|
1156
|
+
await self._save_cognitive_behaviors_to_graph(config_service, cognitive_behaviors)
|
|
1157
|
+
except Exception as e:
|
|
1158
|
+
logger.error(f"[COGNITIVE_MIGRATION] FAILED to migrate cognitive state behaviors to graph: {e}")
|
|
1159
|
+
|
|
1160
|
+
async def _final_verification(self) -> None:
|
|
1161
|
+
"""Perform final system verification.
|
|
1162
|
+
|
|
1163
|
+
In first-run mode, identity isn't established yet - skip full verification.
|
|
1164
|
+
"""
|
|
1165
|
+
from ciris_engine.logic.setup.first_run import is_first_run
|
|
1166
|
+
|
|
1167
|
+
# In first-run mode, identity isn't established yet
|
|
1168
|
+
if is_first_run():
|
|
1169
|
+
logger.info("First-run mode: Skipping final verification (waiting for setup wizard)")
|
|
1170
|
+
logger.info("=" * 60)
|
|
1171
|
+
logger.info("CIRIS Agent First-Run Mode Active")
|
|
1172
|
+
logger.info("Setup wizard is ready at http://127.0.0.1:8080/setup")
|
|
1173
|
+
logger.info("=" * 60)
|
|
1174
|
+
return
|
|
1175
|
+
|
|
1176
|
+
# Don't check initialization status here - we're still IN the initialization process
|
|
1177
|
+
# Just verify the critical components are ready
|
|
1178
|
+
|
|
1179
|
+
# Verify identity loaded
|
|
1180
|
+
if not self.agent_identity:
|
|
1181
|
+
raise RuntimeError("No agent identity established")
|
|
1182
|
+
|
|
1183
|
+
# Log final status
|
|
1184
|
+
logger.info("=" * 60)
|
|
1185
|
+
logger.info("CIRIS Agent Pre-Wakeup Verification Complete")
|
|
1186
|
+
logger.info(f"Identity: {self.agent_identity.agent_id}")
|
|
1187
|
+
logger.info(f"Purpose: {self.agent_identity.core_profile.description}")
|
|
1188
|
+
logger.info(f"Capabilities: {len(self.agent_identity.permitted_actions)} allowed")
|
|
1189
|
+
# Count all registered services
|
|
1190
|
+
service_count = 0
|
|
1191
|
+
if self.service_registry:
|
|
1192
|
+
registry_info = self.service_registry.get_provider_info()
|
|
1193
|
+
# Count services from the 'services' key (new structure)
|
|
1194
|
+
for service_list in registry_info.get("services", {}).values():
|
|
1195
|
+
service_count += len(service_list)
|
|
1196
|
+
|
|
1197
|
+
logger.info(f"Services: {service_count} registered")
|
|
1198
|
+
logger.info("=" * 60)
|
|
1199
|
+
|
|
1200
|
+
async def _perform_startup_maintenance(self) -> None:
|
|
1201
|
+
"""Perform database cleanup at startup."""
|
|
1202
|
+
if self.maintenance_service:
|
|
1203
|
+
try:
|
|
1204
|
+
logger.info("Starting critical database maintenance...")
|
|
1205
|
+
await self.maintenance_service.perform_startup_cleanup()
|
|
1206
|
+
logger.info("Database maintenance completed successfully")
|
|
1207
|
+
except Exception as e:
|
|
1208
|
+
logger.critical(f"CRITICAL ERROR: Database maintenance failed during startup: {e}")
|
|
1209
|
+
logger.critical("Database integrity cannot be guaranteed - initiating graceful shutdown")
|
|
1210
|
+
self._request_shutdown(f"Critical database maintenance failure: {e}")
|
|
1211
|
+
raise RuntimeError(f"Database maintenance failure: {e}") from e
|
|
1212
|
+
else:
|
|
1213
|
+
logger.critical("CRITICAL ERROR: No maintenance service available during startup")
|
|
1214
|
+
logger.critical("Database integrity cannot be guaranteed - initiating graceful shutdown")
|
|
1215
|
+
self._request_shutdown("No maintenance service available")
|
|
1216
|
+
raise RuntimeError("No maintenance service available")
|
|
1217
|
+
|
|
1218
|
+
async def _clean_runtime_configs(self) -> None:
|
|
1219
|
+
"""Clean up runtime-specific configuration from previous runs."""
|
|
1220
|
+
if not self.config_service:
|
|
1221
|
+
logger.warning("Config service not available - skipping runtime config cleanup")
|
|
1222
|
+
return
|
|
1223
|
+
|
|
1224
|
+
try:
|
|
1225
|
+
logger.info("Cleaning up runtime-specific configurations...")
|
|
1226
|
+
|
|
1227
|
+
# Get all config entries
|
|
1228
|
+
all_configs = await self.config_service.list_configs()
|
|
1229
|
+
|
|
1230
|
+
runtime_config_patterns = [
|
|
1231
|
+
"adapter.", # Adapter configurations
|
|
1232
|
+
"runtime.", # Runtime-specific settings
|
|
1233
|
+
"session.", # Session-specific data
|
|
1234
|
+
"temp.", # Temporary configurations
|
|
1235
|
+
]
|
|
1236
|
+
|
|
1237
|
+
deleted_count = 0
|
|
1238
|
+
|
|
1239
|
+
for key, value in all_configs.items():
|
|
1240
|
+
# Check if this is a runtime-specific config
|
|
1241
|
+
is_runtime_config = any(key.startswith(pattern) for pattern in runtime_config_patterns)
|
|
1242
|
+
|
|
1243
|
+
if is_runtime_config:
|
|
1244
|
+
# Get the actual config node to check if it should be deleted
|
|
1245
|
+
config_node = await self.config_service.get_config(key)
|
|
1246
|
+
if config_node:
|
|
1247
|
+
# Skip configs created by system_bootstrap (essential configs)
|
|
1248
|
+
if config_node.updated_by == "system_bootstrap":
|
|
1249
|
+
logger.debug(f"Preserving bootstrap config: {key}")
|
|
1250
|
+
continue
|
|
1251
|
+
|
|
1252
|
+
# Convert to GraphNode and use memory service to forget it
|
|
1253
|
+
graph_node = config_node.to_graph_node()
|
|
1254
|
+
await self.config_service.graph.forget(graph_node) # type: ignore[attr-defined]
|
|
1255
|
+
deleted_count += 1
|
|
1256
|
+
logger.debug(f"Deleted runtime config node: {key}")
|
|
1257
|
+
|
|
1258
|
+
if deleted_count > 0:
|
|
1259
|
+
logger.info(f"Cleaned up {deleted_count} runtime-specific configuration entries from previous runs")
|
|
1260
|
+
else:
|
|
1261
|
+
logger.info("No runtime-specific configuration entries to clean up")
|
|
1262
|
+
|
|
1263
|
+
except Exception as e:
|
|
1264
|
+
logger.error(f"Failed to clean up runtime config: {e}", exc_info=True)
|
|
1265
|
+
# Non-critical - don't fail initialization
|
|
1266
|
+
|
|
1267
|
+
async def _register_adapter_services(self) -> None:
|
|
1268
|
+
"""Register services provided by the loaded adapters.
|
|
1269
|
+
|
|
1270
|
+
In first-run mode, skip registration since services aren't initialized.
|
|
1271
|
+
"""
|
|
1272
|
+
from ciris_engine.logic.setup.first_run import is_first_run
|
|
1273
|
+
|
|
1274
|
+
if is_first_run():
|
|
1275
|
+
logger.info("First-run mode: Skipping adapter service registration")
|
|
1276
|
+
return
|
|
1277
|
+
|
|
1278
|
+
if not self.service_registry:
|
|
1279
|
+
logger.error("ServiceRegistry not initialized. Cannot register adapter services.")
|
|
1280
|
+
return
|
|
1281
|
+
|
|
1282
|
+
for adapter in self.adapters:
|
|
1283
|
+
try:
|
|
1284
|
+
# Generate authentication token for adapter - REQUIRED for security
|
|
1285
|
+
adapter_type = adapter.__class__.__name__.lower().replace("adapter", "")
|
|
1286
|
+
# Explicitly type as JSONDict for authentication service compatibility
|
|
1287
|
+
adapter_info: JSONDict = {
|
|
1288
|
+
"instance_id": str(id(adapter)),
|
|
1289
|
+
"startup_time": (
|
|
1290
|
+
self.time_service.now().isoformat()
|
|
1291
|
+
if self.time_service
|
|
1292
|
+
else datetime.now(timezone.utc).isoformat()
|
|
1293
|
+
),
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
# Get channel-specific info if available
|
|
1297
|
+
if hasattr(adapter, "get_channel_info"):
|
|
1298
|
+
adapter_info.update(adapter.get_channel_info())
|
|
1299
|
+
|
|
1300
|
+
# Get authentication service from service initializer
|
|
1301
|
+
auth_service = self.service_initializer.auth_service if self.service_initializer else None
|
|
1302
|
+
|
|
1303
|
+
# Create adapter token using the proper authentication service
|
|
1304
|
+
auth_token = (
|
|
1305
|
+
await auth_service._create_channel_token_for_adapter(adapter_type, adapter_info)
|
|
1306
|
+
if auth_service
|
|
1307
|
+
else None
|
|
1308
|
+
)
|
|
1309
|
+
|
|
1310
|
+
# Set token on adapter if it has the method
|
|
1311
|
+
if hasattr(adapter, "set_auth_token") and auth_token:
|
|
1312
|
+
adapter.set_auth_token(auth_token)
|
|
1313
|
+
|
|
1314
|
+
if auth_token:
|
|
1315
|
+
logger.info(f"Generated authentication token for {adapter_type} adapter")
|
|
1316
|
+
|
|
1317
|
+
registrations = adapter.get_services_to_register()
|
|
1318
|
+
for reg in registrations:
|
|
1319
|
+
if not isinstance(reg, AdapterServiceRegistration):
|
|
1320
|
+
logger.error(
|
|
1321
|
+
f"Adapter {adapter.__class__.__name__} provided an invalid AdapterServiceRegistration object: {reg}"
|
|
1322
|
+
)
|
|
1323
|
+
continue
|
|
1324
|
+
|
|
1325
|
+
# No need to check Service base class - adapters implement protocol interfaces
|
|
1326
|
+
|
|
1327
|
+
# All services are global now
|
|
1328
|
+
self.service_registry.register_service(
|
|
1329
|
+
service_type=reg.service_type, # Use the enum directly
|
|
1330
|
+
provider=reg.provider,
|
|
1331
|
+
priority=reg.priority,
|
|
1332
|
+
capabilities=reg.capabilities,
|
|
1333
|
+
)
|
|
1334
|
+
logger.info(f"Registered {reg.service_type.value} from {adapter.__class__.__name__}")
|
|
1335
|
+
except Exception as e:
|
|
1336
|
+
logger.error(f"Error registering services for adapter {adapter.__class__.__name__}: {e}", exc_info=True)
|
|
1337
|
+
|
|
1338
|
+
def _build_adapter_info(self, adapter: Any) -> JSONDict:
|
|
1339
|
+
"""Build adapter info dictionary for authentication token creation."""
|
|
1340
|
+
adapter_info: JSONDict = {
|
|
1341
|
+
"instance_id": str(id(adapter)),
|
|
1342
|
+
"startup_time": (
|
|
1343
|
+
self.time_service.now().isoformat() if self.time_service else datetime.now(timezone.utc).isoformat()
|
|
1344
|
+
),
|
|
1345
|
+
}
|
|
1346
|
+
# Get channel-specific info if available
|
|
1347
|
+
if hasattr(adapter, "get_channel_info"):
|
|
1348
|
+
adapter_info.update(adapter.get_channel_info())
|
|
1349
|
+
return adapter_info
|
|
1350
|
+
|
|
1351
|
+
async def _create_adapter_auth_token(
|
|
1352
|
+
self, adapter: Any, adapter_type: str, adapter_info: JSONDict
|
|
1353
|
+
) -> Optional[str]:
|
|
1354
|
+
"""Create and set authentication token for an adapter."""
|
|
1355
|
+
auth_service = self.service_initializer.auth_service if self.service_initializer else None
|
|
1356
|
+
if not auth_service:
|
|
1357
|
+
return None
|
|
1358
|
+
|
|
1359
|
+
auth_token = await auth_service._create_channel_token_for_adapter(adapter_type, adapter_info)
|
|
1360
|
+
|
|
1361
|
+
if hasattr(adapter, "set_auth_token") and auth_token:
|
|
1362
|
+
adapter.set_auth_token(auth_token)
|
|
1363
|
+
|
|
1364
|
+
if auth_token:
|
|
1365
|
+
logger.info(f"Generated authentication token for {adapter_type} adapter")
|
|
1366
|
+
|
|
1367
|
+
return auth_token
|
|
1368
|
+
|
|
1369
|
+
def _register_adapter_service(self, reg: AdapterServiceRegistration, adapter: Any) -> bool:
|
|
1370
|
+
"""Register a single adapter service. Returns True if successful."""
|
|
1371
|
+
if not isinstance(reg, AdapterServiceRegistration):
|
|
1372
|
+
logger.error(
|
|
1373
|
+
f"Adapter {adapter.__class__.__name__} provided an invalid AdapterServiceRegistration object: {reg}"
|
|
1374
|
+
)
|
|
1375
|
+
return False
|
|
1376
|
+
|
|
1377
|
+
if self.service_registry is None:
|
|
1378
|
+
logger.error("Cannot register adapter service: service_registry is None")
|
|
1379
|
+
return False
|
|
1380
|
+
|
|
1381
|
+
self.service_registry.register_service(
|
|
1382
|
+
service_type=reg.service_type,
|
|
1383
|
+
provider=reg.provider,
|
|
1384
|
+
priority=reg.priority,
|
|
1385
|
+
capabilities=reg.capabilities,
|
|
1386
|
+
)
|
|
1387
|
+
logger.info(f"Registered {reg.service_type.value} from {adapter.__class__.__name__}")
|
|
1388
|
+
return True
|
|
1389
|
+
|
|
1390
|
+
async def _register_adapter_services_for_resume(self) -> None:
|
|
1391
|
+
"""Register adapter services during resume_from_first_run.
|
|
1392
|
+
|
|
1393
|
+
This is identical to _register_adapter_services but without the is_first_run check,
|
|
1394
|
+
since we explicitly want to register during resume.
|
|
1395
|
+
"""
|
|
1396
|
+
if not self.service_registry:
|
|
1397
|
+
logger.error("ServiceRegistry not initialized. Cannot register adapter services.")
|
|
1398
|
+
return
|
|
1399
|
+
|
|
1400
|
+
for adapter in self.adapters:
|
|
1401
|
+
try:
|
|
1402
|
+
adapter_type = adapter.__class__.__name__.lower().replace("adapter", "")
|
|
1403
|
+
adapter_info = self._build_adapter_info(adapter)
|
|
1404
|
+
await self._create_adapter_auth_token(adapter, adapter_type, adapter_info)
|
|
1405
|
+
|
|
1406
|
+
for reg in adapter.get_services_to_register():
|
|
1407
|
+
self._register_adapter_service(reg, adapter)
|
|
1408
|
+
except Exception as e:
|
|
1409
|
+
logger.error(f"Error registering services for adapter {adapter.__class__.__name__}: {e}", exc_info=True)
|
|
1410
|
+
|
|
1411
|
+
async def _build_components(self) -> None:
|
|
1412
|
+
"""Build all processing components."""
|
|
1413
|
+
logger.info("[_build_components] Starting component building...")
|
|
1414
|
+
logger.info(f"[_build_components] llm_service: {self.llm_service}")
|
|
1415
|
+
logger.info(f"[_build_components] service_registry: {self.service_registry}")
|
|
1416
|
+
logger.info(f"[_build_components] service_initializer: {self.service_initializer}")
|
|
1417
|
+
|
|
1418
|
+
if self.service_initializer:
|
|
1419
|
+
logger.info(f"[_build_components] service_initializer.llm_service: {self.service_initializer.llm_service}")
|
|
1420
|
+
logger.info(
|
|
1421
|
+
f"[_build_components] service_initializer.service_registry: {self.service_initializer.service_registry}"
|
|
1422
|
+
)
|
|
1423
|
+
|
|
1424
|
+
# Check if LLM service is available - if not, check if this is first-run setup mode
|
|
1425
|
+
if not self.llm_service:
|
|
1426
|
+
from ciris_engine.logic.setup.first_run import is_first_run
|
|
1427
|
+
|
|
1428
|
+
if is_first_run():
|
|
1429
|
+
logger.info("[_build_components] First-run setup mode - LLM not yet configured")
|
|
1430
|
+
logger.info("[_build_components] Setup wizard will guide LLM configuration")
|
|
1431
|
+
else:
|
|
1432
|
+
logger.error("[_build_components] LLM service not available but setup was completed!")
|
|
1433
|
+
logger.error(
|
|
1434
|
+
"[_build_components] Check your LLM configuration - the agent cannot operate without an LLM"
|
|
1435
|
+
)
|
|
1436
|
+
return
|
|
1437
|
+
|
|
1438
|
+
try:
|
|
1439
|
+
self.component_builder = ComponentBuilder(self)
|
|
1440
|
+
logger.info("[_build_components] ComponentBuilder created successfully")
|
|
1441
|
+
|
|
1442
|
+
self.agent_processor = await self.component_builder.build_all_components()
|
|
1443
|
+
logger.info(f"[_build_components] agent_processor created: {self.agent_processor}")
|
|
1444
|
+
|
|
1445
|
+
# Set up thought tracking callback now that agent_processor exists
|
|
1446
|
+
# This avoids the race condition where RuntimeControlService tried to access
|
|
1447
|
+
# agent_processor during Phase 5 (SERVICES) before it was created in Phase 6 (COMPONENTS)
|
|
1448
|
+
if self.runtime_control_service:
|
|
1449
|
+
self.runtime_control_service.setup_thought_tracking() # type: ignore[attr-defined]
|
|
1450
|
+
logger.debug("Thought tracking callback set up after agent_processor creation")
|
|
1451
|
+
|
|
1452
|
+
except Exception as e:
|
|
1453
|
+
logger.error(f"[_build_components] Failed to build components: {e}", exc_info=True)
|
|
1454
|
+
raise
|
|
1455
|
+
|
|
1456
|
+
# Register core services after components are built
|
|
1457
|
+
self._register_core_services()
|
|
1458
|
+
logger.info("[_build_components] Component building completed")
|
|
1459
|
+
|
|
1460
|
+
async def _start_adapter_connections(self) -> None:
|
|
1461
|
+
"""Start adapter connections and wait for them to be ready."""
|
|
1462
|
+
from ciris_engine.logic.setup.first_run import is_first_run
|
|
1463
|
+
|
|
1464
|
+
from .ciris_runtime_helpers import (
|
|
1465
|
+
create_adapter_lifecycle_tasks,
|
|
1466
|
+
log_adapter_configuration_details,
|
|
1467
|
+
verify_adapter_service_registration,
|
|
1468
|
+
wait_for_adapter_readiness,
|
|
1469
|
+
)
|
|
1470
|
+
|
|
1471
|
+
# Log adapter configuration details
|
|
1472
|
+
log_adapter_configuration_details(self.adapters)
|
|
1473
|
+
|
|
1474
|
+
# Check if this is first-run - skip agent processor if so
|
|
1475
|
+
first_run = is_first_run()
|
|
1476
|
+
if first_run:
|
|
1477
|
+
logger.info("")
|
|
1478
|
+
logger.info("=" * 70)
|
|
1479
|
+
logger.info("🔧 FIRST RUN DETECTED - Setup Wizard Mode")
|
|
1480
|
+
logger.info("=" * 70)
|
|
1481
|
+
logger.info("")
|
|
1482
|
+
logger.info("The agent processor will NOT start in first-run mode.")
|
|
1483
|
+
logger.info("Only the API server is running to provide the setup wizard.")
|
|
1484
|
+
logger.info("")
|
|
1485
|
+
logger.info("📋 Next Steps:")
|
|
1486
|
+
logger.info(" 1. Open your browser to: http://localhost:8080")
|
|
1487
|
+
logger.info(" 2. Complete the setup wizard")
|
|
1488
|
+
logger.info(" 3. Restart the agent with: ciris-agent")
|
|
1489
|
+
logger.info("")
|
|
1490
|
+
logger.info("After restart, the full agent will start normally.")
|
|
1491
|
+
logger.info("=" * 70)
|
|
1492
|
+
logger.info("")
|
|
1493
|
+
|
|
1494
|
+
# Only wait for adapters to be ready, but don't start agent processor
|
|
1495
|
+
adapters_ready = await wait_for_adapter_readiness(self.adapters)
|
|
1496
|
+
if not adapters_ready:
|
|
1497
|
+
raise RuntimeError("Adapters failed to become ready within timeout")
|
|
1498
|
+
|
|
1499
|
+
# No agent processor task in first-run mode
|
|
1500
|
+
self._adapter_tasks = create_adapter_lifecycle_tasks(self.adapters, agent_task=None)
|
|
1501
|
+
|
|
1502
|
+
# Skip service registration and processor - API will handle setup
|
|
1503
|
+
logger.info("✅ Setup wizard ready at http://localhost:8080")
|
|
1504
|
+
logger.info("Waiting for setup completion... (Press CTRL+C to exit)")
|
|
1505
|
+
return
|
|
1506
|
+
|
|
1507
|
+
# Normal mode - create agent processor task and adapter lifecycle tasks
|
|
1508
|
+
agent_task = asyncio.create_task(self._create_agent_processor_when_ready(), name="AgentProcessorTask")
|
|
1509
|
+
self._adapter_tasks = create_adapter_lifecycle_tasks(self.adapters, agent_task)
|
|
1510
|
+
|
|
1511
|
+
# Wait for adapters to be ready
|
|
1512
|
+
adapters_ready = await wait_for_adapter_readiness(self.adapters)
|
|
1513
|
+
if not adapters_ready:
|
|
1514
|
+
raise RuntimeError("Adapters failed to become ready within timeout")
|
|
1515
|
+
|
|
1516
|
+
# Register services and verify availability
|
|
1517
|
+
services_available = await verify_adapter_service_registration(self)
|
|
1518
|
+
if not services_available:
|
|
1519
|
+
raise RuntimeError("Failed to establish adapter connections within timeout")
|
|
1520
|
+
|
|
1521
|
+
# Final verification with the existing wait method
|
|
1522
|
+
await self._wait_for_critical_services(timeout=5.0)
|
|
1523
|
+
|
|
1524
|
+
def _is_using_ciris_proxy(self) -> bool:
|
|
1525
|
+
"""Check if runtime is configured to use CIRIS proxy."""
|
|
1526
|
+
llm_base_url = os.getenv("OPENAI_API_BASE", "")
|
|
1527
|
+
return any(domain in llm_base_url for domain in CIRIS_PROXY_DOMAINS)
|
|
1528
|
+
|
|
1529
|
+
def _create_billing_token_handler(self, credit_provider: Any) -> Callable[..., Any]:
|
|
1530
|
+
"""Create handler for billing token refresh signals."""
|
|
1531
|
+
|
|
1532
|
+
async def handle_billing_token_refreshed(signal: str, resource: str) -> None:
|
|
1533
|
+
new_token = os.getenv("CIRIS_BILLING_GOOGLE_ID_TOKEN", "")
|
|
1534
|
+
if new_token and credit_provider:
|
|
1535
|
+
credit_provider.update_google_id_token(new_token)
|
|
1536
|
+
logger.info("✓ Updated billing provider with refreshed Google ID token")
|
|
1537
|
+
|
|
1538
|
+
return handle_billing_token_refreshed
|
|
1539
|
+
|
|
1540
|
+
def _create_llm_token_handler(self) -> Callable[..., Any]:
|
|
1541
|
+
"""Create handler for LLM service token refresh signals."""
|
|
1542
|
+
|
|
1543
|
+
async def handle_llm_token_refreshed(signal: str, resource: str) -> None:
|
|
1544
|
+
new_token = os.getenv("OPENAI_API_KEY", "")
|
|
1545
|
+
if not new_token:
|
|
1546
|
+
logger.warning("[LLM_TOKEN] No OPENAI_API_KEY in env after token refresh")
|
|
1547
|
+
return
|
|
1548
|
+
|
|
1549
|
+
self._update_llm_services_token(new_token)
|
|
1550
|
+
|
|
1551
|
+
return handle_llm_token_refreshed
|
|
1552
|
+
|
|
1553
|
+
def _update_llm_services_token(self, new_token: str) -> None:
|
|
1554
|
+
"""Update all LLM services that use CIRIS proxy with new token."""
|
|
1555
|
+
if self.service_registry:
|
|
1556
|
+
llm_services = self.service_registry.get_services_by_type(ServiceType.LLM)
|
|
1557
|
+
for service in llm_services:
|
|
1558
|
+
self._update_service_token_if_ciris_proxy(service, new_token)
|
|
1559
|
+
|
|
1560
|
+
if self.llm_service:
|
|
1561
|
+
self._update_service_token_if_ciris_proxy(self.llm_service, new_token, is_primary=True)
|
|
1562
|
+
|
|
1563
|
+
def _update_service_token_if_ciris_proxy(self, service: Any, new_token: str, is_primary: bool = False) -> None:
|
|
1564
|
+
"""Update a service's API key if it uses CIRIS proxy."""
|
|
1565
|
+
if not hasattr(service, "openai_config") or not service.openai_config:
|
|
1566
|
+
return
|
|
1567
|
+
if not hasattr(service, "update_api_key"):
|
|
1568
|
+
return
|
|
1569
|
+
|
|
1570
|
+
base_url = getattr(service.openai_config, "base_url", "") or ""
|
|
1571
|
+
if not any(domain in base_url for domain in CIRIS_PROXY_DOMAINS):
|
|
1572
|
+
return
|
|
1573
|
+
|
|
1574
|
+
service.update_api_key(new_token)
|
|
1575
|
+
label = "primary LLM service" if is_primary else type(service).__name__
|
|
1576
|
+
logger.info(f"✓ Updated {label} with refreshed token")
|
|
1577
|
+
|
|
1578
|
+
async def _reinitialize_billing_provider(self) -> None:
|
|
1579
|
+
"""Reinitialize billing provider after setup completes.
|
|
1580
|
+
|
|
1581
|
+
Called during resume_from_first_run to set up billing now that
|
|
1582
|
+
environment variables (OPENAI_API_BASE, CIRIS_BILLING_GOOGLE_ID_TOKEN)
|
|
1583
|
+
are available from the newly created .env file.
|
|
1584
|
+
"""
|
|
1585
|
+
resource_monitor = self._get_resource_monitor_for_billing()
|
|
1586
|
+
if not resource_monitor:
|
|
1587
|
+
return
|
|
1588
|
+
|
|
1589
|
+
is_android = "ANDROID_DATA" in os.environ
|
|
1590
|
+
using_ciris_proxy = self._is_using_ciris_proxy()
|
|
1591
|
+
|
|
1592
|
+
logger.info(f"Billing provider check: is_android={is_android}, using_ciris_proxy={using_ciris_proxy}")
|
|
1593
|
+
logger.info(f" OPENAI_API_BASE={os.getenv('OPENAI_API_BASE', '')}")
|
|
1594
|
+
|
|
1595
|
+
if not (is_android and using_ciris_proxy):
|
|
1596
|
+
logger.info("Billing provider not needed (not using CIRIS proxy or not Android)")
|
|
1597
|
+
return
|
|
1598
|
+
|
|
1599
|
+
google_id_token = os.getenv("CIRIS_BILLING_GOOGLE_ID_TOKEN", "")
|
|
1600
|
+
if not google_id_token:
|
|
1601
|
+
logger.warning("Android using CIRIS LLM proxy without Google ID token - billing provider not configured")
|
|
1602
|
+
return
|
|
1603
|
+
|
|
1604
|
+
credit_provider = self._create_billing_provider(google_id_token)
|
|
1605
|
+
resource_monitor.credit_provider = credit_provider
|
|
1606
|
+
|
|
1607
|
+
# Register token refresh handlers
|
|
1608
|
+
resource_monitor.signal_bus.register("token_refreshed", self._create_billing_token_handler(credit_provider))
|
|
1609
|
+
logger.info("✓ Reinitialized CIRISBillingProvider with JWT auth (CIRIS LLM proxy)")
|
|
1610
|
+
logger.info("✓ Registered token_refreshed handler for billing provider")
|
|
1611
|
+
|
|
1612
|
+
resource_monitor.signal_bus.register("token_refreshed", self._create_llm_token_handler())
|
|
1613
|
+
logger.info("✓ Registered token_refreshed handler for LLM service")
|
|
1614
|
+
|
|
1615
|
+
def _get_resource_monitor_for_billing(self) -> Any:
|
|
1616
|
+
"""Get resource monitor service for billing initialization.
|
|
1617
|
+
|
|
1618
|
+
Returns the resource monitor service or None if not available.
|
|
1619
|
+
Uses Any type since we access implementation-specific attributes
|
|
1620
|
+
(credit_provider, signal_bus) not in the protocol.
|
|
1621
|
+
"""
|
|
1622
|
+
if not self.service_initializer:
|
|
1623
|
+
logger.warning("Cannot reinitialize billing - service_initializer not available")
|
|
1624
|
+
return None
|
|
1625
|
+
|
|
1626
|
+
resource_monitor = self.service_initializer.resource_monitor_service
|
|
1627
|
+
if not resource_monitor:
|
|
1628
|
+
logger.warning("Cannot reinitialize billing - resource_monitor_service not available")
|
|
1629
|
+
return None
|
|
1630
|
+
|
|
1631
|
+
return resource_monitor
|
|
1632
|
+
|
|
1633
|
+
def _create_billing_provider(self, google_id_token: str) -> Any:
|
|
1634
|
+
"""Create and configure the CIRIS billing provider."""
|
|
1635
|
+
from ciris_engine.logic.services.infrastructure.resource_monitor import CIRISBillingProvider
|
|
1636
|
+
|
|
1637
|
+
base_url = get_billing_url() # Checks env var first, then falls back to central config
|
|
1638
|
+
timeout = float(os.getenv("CIRIS_BILLING_TIMEOUT_SECONDS", "5.0"))
|
|
1639
|
+
cache_ttl = int(os.getenv("CIRIS_BILLING_CACHE_TTL_SECONDS", "15"))
|
|
1640
|
+
fail_open = os.getenv("CIRIS_BILLING_FAIL_OPEN", "false").lower() == "true"
|
|
1641
|
+
|
|
1642
|
+
def get_fresh_token() -> str:
|
|
1643
|
+
return os.getenv("CIRIS_BILLING_GOOGLE_ID_TOKEN", "")
|
|
1644
|
+
|
|
1645
|
+
return CIRISBillingProvider(
|
|
1646
|
+
google_id_token=google_id_token,
|
|
1647
|
+
token_refresh_callback=get_fresh_token,
|
|
1648
|
+
base_url=base_url,
|
|
1649
|
+
timeout_seconds=timeout,
|
|
1650
|
+
cache_ttl_seconds=cache_ttl,
|
|
1651
|
+
fail_open=fail_open,
|
|
1652
|
+
)
|
|
1653
|
+
|
|
1654
|
+
def _resume_reload_environment(
|
|
1655
|
+
self, log_step: Callable[[int, int, str], None], total_steps: int
|
|
1656
|
+
) -> "EssentialConfig":
|
|
1657
|
+
"""Reload environment and config during resume from first-run."""
|
|
1658
|
+
from dotenv import load_dotenv
|
|
1659
|
+
|
|
1660
|
+
from ciris_engine.logic.setup.first_run import get_default_config_path
|
|
1661
|
+
|
|
1662
|
+
config_path = get_default_config_path()
|
|
1663
|
+
log_step(2, total_steps, f"Config path: {config_path}, exists: {config_path.exists()}")
|
|
1664
|
+
if config_path.exists():
|
|
1665
|
+
load_dotenv(config_path, override=True)
|
|
1666
|
+
log_step(2, total_steps, f"✓ Reloaded environment from {config_path}")
|
|
1667
|
+
else:
|
|
1668
|
+
log_step(2, total_steps, f"⚠️ Config path does not exist: {config_path}")
|
|
1669
|
+
|
|
1670
|
+
config = self._ensure_config()
|
|
1671
|
+
config.load_env_vars()
|
|
1672
|
+
log_step(3, total_steps, f"✓ Config reloaded - default_template: {config.default_template}")
|
|
1673
|
+
return config
|
|
1674
|
+
|
|
1675
|
+
async def _resume_initialize_identity(
|
|
1676
|
+
self, config: "EssentialConfig", log_step: Callable[[int, int, str], None], total_steps: int
|
|
1677
|
+
) -> None:
|
|
1678
|
+
"""Initialize identity with user-selected template during resume."""
|
|
1679
|
+
log_step(
|
|
1680
|
+
4,
|
|
1681
|
+
total_steps,
|
|
1682
|
+
f"Initializing identity... identity_manager={self.identity_manager is not None}, "
|
|
1683
|
+
f"time_service={self.time_service is not None}",
|
|
1684
|
+
)
|
|
1685
|
+
if self.identity_manager and self.time_service:
|
|
1686
|
+
self.identity_manager = IdentityManager(config, self.time_service)
|
|
1687
|
+
self.agent_identity = await self.identity_manager.initialize_identity()
|
|
1688
|
+
await self._create_startup_node()
|
|
1689
|
+
log_step(
|
|
1690
|
+
4,
|
|
1691
|
+
total_steps,
|
|
1692
|
+
f"✓ Agent identity initialized: {self.agent_identity.agent_id if self.agent_identity else 'None'}",
|
|
1693
|
+
)
|
|
1694
|
+
else:
|
|
1695
|
+
log_step(4, total_steps, "⚠️ Skipped identity init - missing identity_manager or time_service")
|
|
1696
|
+
|
|
1697
|
+
async def _resume_migrate_cognitive_behaviors(
|
|
1698
|
+
self, log_step: Callable[[int, int, str], None], total_steps: int
|
|
1699
|
+
) -> None:
|
|
1700
|
+
"""Migrate cognitive state behaviors from template during resume."""
|
|
1701
|
+
log_step(5, total_steps, "Migrating cognitive state behaviors from template...")
|
|
1702
|
+
if self.identity_manager and self.identity_manager.agent_template:
|
|
1703
|
+
template_name = getattr(self.identity_manager.agent_template, "name", "UNKNOWN")
|
|
1704
|
+
cognitive_behaviors = getattr(self.identity_manager.agent_template, "cognitive_state_behaviors", None)
|
|
1705
|
+
if cognitive_behaviors:
|
|
1706
|
+
log_step(
|
|
1707
|
+
5,
|
|
1708
|
+
total_steps,
|
|
1709
|
+
f"Template '{template_name}' has cognitive_state_behaviors: "
|
|
1710
|
+
f"wakeup.enabled={cognitive_behaviors.wakeup.enabled}",
|
|
1711
|
+
)
|
|
1712
|
+
else:
|
|
1713
|
+
log_step(
|
|
1714
|
+
5, total_steps, f"Template '{template_name}' has no cognitive_state_behaviors (will use defaults)"
|
|
1715
|
+
)
|
|
1716
|
+
await self._migrate_cognitive_state_behaviors_to_graph(force_from_template=True)
|
|
1717
|
+
log_step(5, total_steps, "✓ Cognitive state behaviors migrated from template")
|
|
1718
|
+
else:
|
|
1719
|
+
log_step(5, total_steps, "⚠️ No template available - using default cognitive behaviors")
|
|
1720
|
+
await self._migrate_cognitive_state_behaviors_to_graph(force_from_template=False)
|
|
1721
|
+
|
|
1722
|
+
def _set_service_runtime_references(self) -> None:
|
|
1723
|
+
"""Set runtime references on services that need them."""
|
|
1724
|
+
if self.audit_service:
|
|
1725
|
+
self.audit_service._runtime = self # type: ignore[attr-defined]
|
|
1726
|
+
logger.debug("Set runtime reference on audit service for trace correlations")
|
|
1727
|
+
|
|
1728
|
+
if self.visibility_service:
|
|
1729
|
+
self.visibility_service._runtime = self # type: ignore[attr-defined]
|
|
1730
|
+
logger.debug("Set runtime reference on visibility service for trace retrieval")
|
|
1731
|
+
|
|
1732
|
+
if self.runtime_control_service:
|
|
1733
|
+
if hasattr(self.runtime_control_service, "_set_runtime"):
|
|
1734
|
+
self.runtime_control_service._set_runtime(self)
|
|
1735
|
+
else:
|
|
1736
|
+
self.runtime_control_service.runtime = self # type: ignore[attr-defined]
|
|
1737
|
+
logger.info("Updated runtime control service with runtime reference")
|
|
1738
|
+
|
|
1739
|
+
if self.telemetry_service and hasattr(self.telemetry_service, "_set_runtime"):
|
|
1740
|
+
self.telemetry_service._set_runtime(self)
|
|
1741
|
+
logger.info("Updated telemetry service with runtime reference for aggregator")
|
|
1742
|
+
|
|
1743
|
+
async def _resume_initialize_core_services(
|
|
1744
|
+
self, config: "EssentialConfig", log_step: Callable[[int, int, str], None], total_steps: int
|
|
1745
|
+
) -> None:
|
|
1746
|
+
"""Initialize core services during resume."""
|
|
1747
|
+
log_step(
|
|
1748
|
+
6,
|
|
1749
|
+
total_steps,
|
|
1750
|
+
f"Initializing core services... service_initializer={self.service_initializer is not None}, "
|
|
1751
|
+
f"agent_identity={self.agent_identity is not None}",
|
|
1752
|
+
)
|
|
1753
|
+
if not (self.service_initializer and self.agent_identity):
|
|
1754
|
+
log_step(6, total_steps, "⚠️ Skipped core services - missing service_initializer or agent_identity")
|
|
1755
|
+
return
|
|
1756
|
+
|
|
1757
|
+
await self.service_initializer.initialize_all_services(
|
|
1758
|
+
config,
|
|
1759
|
+
self.essential_config,
|
|
1760
|
+
self.agent_identity.agent_id,
|
|
1761
|
+
self.startup_channel_id,
|
|
1762
|
+
self.modules_to_load,
|
|
1763
|
+
)
|
|
1764
|
+
log_step(6, total_steps, "✓ Core services initialized")
|
|
1765
|
+
|
|
1766
|
+
self._set_service_runtime_references()
|
|
1767
|
+
|
|
1768
|
+
if self.modules_to_load:
|
|
1769
|
+
log_step(6, total_steps, f"Loading {len(self.modules_to_load)} external modules: {self.modules_to_load}")
|
|
1770
|
+
await self.service_initializer.load_modules(self.modules_to_load)
|
|
1771
|
+
|
|
1772
|
+
async def _resume_initialize_llm(self, log_step: Callable[[int, int, str], None], total_steps: int) -> None:
|
|
1773
|
+
"""Initialize LLM service during resume."""
|
|
1774
|
+
log_step(
|
|
1775
|
+
10, total_steps, f"Initializing LLM service... service_initializer={self.service_initializer is not None}"
|
|
1776
|
+
)
|
|
1777
|
+
if self.service_initializer:
|
|
1778
|
+
config = self._ensure_config()
|
|
1779
|
+
await self.service_initializer._initialize_llm_services(config, self.modules_to_load)
|
|
1780
|
+
log_step(10, total_steps, "✓ LLM service initialized")
|
|
1781
|
+
else:
|
|
1782
|
+
log_step(10, total_steps, "⚠️ Skipped LLM init - no service_initializer")
|
|
1783
|
+
|
|
1784
|
+
def _resume_reinject_adapters(self, log_step: Callable[[int, int, str], None], total_steps: int) -> None:
|
|
1785
|
+
"""Re-inject services into running adapters during resume."""
|
|
1786
|
+
log_step(11, total_steps, f"Re-injecting services into {len(self.adapters)} adapters...")
|
|
1787
|
+
for adapter in self.adapters:
|
|
1788
|
+
if hasattr(adapter, "reinject_services"):
|
|
1789
|
+
adapter.reinject_services()
|
|
1790
|
+
log_step(11, total_steps, f"✓ Re-injected services into {adapter.__class__.__name__}")
|
|
1791
|
+
|
|
1792
|
+
async def _resume_auto_enable_android_adapters(self) -> None:
|
|
1793
|
+
"""Auto-enable Android-specific adapters after resume.
|
|
1794
|
+
|
|
1795
|
+
Calls _auto_enable_android_adapters on any adapters that have it,
|
|
1796
|
+
which enables ciris_hosted_tools (web_search) when:
|
|
1797
|
+
- Running on Android with Google auth
|
|
1798
|
+
- The adapter is not already loaded
|
|
1799
|
+
"""
|
|
1800
|
+
for adapter in self.adapters:
|
|
1801
|
+
if hasattr(adapter, "_auto_enable_android_adapters"):
|
|
1802
|
+
try:
|
|
1803
|
+
await adapter._auto_enable_android_adapters()
|
|
1804
|
+
logger.info(f"[RESUME] Called _auto_enable_android_adapters on {adapter.__class__.__name__}")
|
|
1805
|
+
except Exception as e:
|
|
1806
|
+
logger.warning(
|
|
1807
|
+
f"[RESUME] Failed to auto-enable Android adapters on {adapter.__class__.__name__}: {e}"
|
|
1808
|
+
)
|
|
1809
|
+
|
|
1810
|
+
async def resume_from_first_run(self) -> None:
|
|
1811
|
+
"""Resume initialization after setup wizard completes.
|
|
1812
|
+
|
|
1813
|
+
This continues from the point where first-run mode paused (line 1088).
|
|
1814
|
+
It executes the same steps as normal mode initialization.
|
|
1815
|
+
"""
|
|
1816
|
+
# Set flag AND timestamp to prevent premature shutdown during resume
|
|
1817
|
+
# The timestamp allows timeout detection for stuck resume scenarios
|
|
1818
|
+
self._resume_in_progress = True
|
|
1819
|
+
self._resume_started_at = time.time()
|
|
1820
|
+
logger.info(f"[RESUME] Started at {self._resume_started_at:.3f}, _resume_in_progress=True")
|
|
1821
|
+
|
|
1822
|
+
start_time = time.time()
|
|
1823
|
+
total_steps = 14
|
|
1824
|
+
|
|
1825
|
+
def log_step(step_num: int, total: int, msg: str) -> None:
|
|
1826
|
+
elapsed = time.time() - start_time
|
|
1827
|
+
logger.warning(f"[RESUME {step_num}/{total}] [{elapsed:.2f}s] {msg}")
|
|
1828
|
+
|
|
1829
|
+
logger.warning("")
|
|
1830
|
+
logger.warning("=" * 70)
|
|
1831
|
+
logger.warning("🔄 RESUMING FROM FIRST-RUN MODE")
|
|
1832
|
+
logger.warning("=" * 70)
|
|
1833
|
+
logger.warning("")
|
|
1834
|
+
log_step(1, total_steps, "Starting resume from first-run...")
|
|
1835
|
+
|
|
1836
|
+
# Steps 2-3: Reload environment and config
|
|
1837
|
+
config = self._resume_reload_environment(log_step, total_steps)
|
|
1838
|
+
|
|
1839
|
+
# Step 4: Initialize identity with user-selected template
|
|
1840
|
+
await self._resume_initialize_identity(config, log_step, total_steps)
|
|
1841
|
+
|
|
1842
|
+
# Step 5: Migrate cognitive behaviors from template
|
|
1843
|
+
await self._resume_migrate_cognitive_behaviors(log_step, total_steps)
|
|
1844
|
+
|
|
1845
|
+
# Step 6: Initialize core services
|
|
1846
|
+
await self._resume_initialize_core_services(config, log_step, total_steps)
|
|
1847
|
+
|
|
1848
|
+
# Step 7: Register adapter services
|
|
1849
|
+
log_step(7, total_steps, "Registering adapter services...")
|
|
1850
|
+
await self._register_adapter_services_for_resume()
|
|
1851
|
+
log_step(7, total_steps, "✓ Adapter services registered")
|
|
1852
|
+
|
|
1853
|
+
# Step 8: Initialize maintenance service
|
|
1854
|
+
log_step(
|
|
1855
|
+
8, total_steps, f"Initializing maintenance... maintenance_service={self.maintenance_service is not None}"
|
|
1856
|
+
)
|
|
1857
|
+
if self.maintenance_service:
|
|
1858
|
+
await self._perform_startup_maintenance()
|
|
1859
|
+
log_step(8, total_steps, "✓ Maintenance service initialized")
|
|
1860
|
+
else:
|
|
1861
|
+
log_step(8, total_steps, "⚠️ Skipped maintenance - no maintenance_service")
|
|
1862
|
+
|
|
1863
|
+
# Step 9: Reinitialize billing provider
|
|
1864
|
+
log_step(9, total_steps, "Reinitializing billing provider...")
|
|
1865
|
+
await self._reinitialize_billing_provider()
|
|
1866
|
+
log_step(9, total_steps, "✓ Billing provider reinitialized")
|
|
1867
|
+
|
|
1868
|
+
# Step 10: Initialize LLM service
|
|
1869
|
+
await self._resume_initialize_llm(log_step, total_steps)
|
|
1870
|
+
|
|
1871
|
+
# Step 11: Re-inject services into adapters
|
|
1872
|
+
self._resume_reinject_adapters(log_step, total_steps)
|
|
1873
|
+
|
|
1874
|
+
# Step 12: Auto-enable Android-specific adapters (ciris_hosted_tools with web_search)
|
|
1875
|
+
log_step(12, total_steps, "Auto-enabling Android adapters...")
|
|
1876
|
+
await self._resume_auto_enable_android_adapters()
|
|
1877
|
+
log_step(12, total_steps, "✓ Android adapters auto-enabled")
|
|
1878
|
+
|
|
1879
|
+
# Step 13: Build cognitive components
|
|
1880
|
+
log_step(13, total_steps, "Building cognitive components...")
|
|
1881
|
+
await self._build_components()
|
|
1882
|
+
log_step(13, total_steps, "✓ Cognitive components built")
|
|
1883
|
+
|
|
1884
|
+
# Step 14: Create agent processor task
|
|
1885
|
+
log_step(14, total_steps, "Creating agent processor task...")
|
|
1886
|
+
self._agent_task = asyncio.create_task(self._create_agent_processor_when_ready(), name="AgentProcessorTask")
|
|
1887
|
+
log_step(14, total_steps, "Waiting for critical services (timeout=10s)...")
|
|
1888
|
+
await self._wait_for_critical_services(timeout=10.0)
|
|
1889
|
+
|
|
1890
|
+
elapsed = time.time() - start_time
|
|
1891
|
+
logger.warning("")
|
|
1892
|
+
logger.warning(f"✅ RESUME COMPLETE in {elapsed:.2f}s - Agent processor started!")
|
|
1893
|
+
logger.warning("=" * 70)
|
|
1894
|
+
logger.warning("")
|
|
1895
|
+
|
|
1896
|
+
# Clear the resume flag and timestamp - safe to shutdown now
|
|
1897
|
+
self._resume_in_progress = False
|
|
1898
|
+
self._resume_started_at = None
|
|
1899
|
+
logger.info(f"[RESUME] Completed in {elapsed:.2f}s, _resume_in_progress=False")
|
|
1900
|
+
|
|
1901
|
+
async def _create_agent_processor_when_ready(self) -> None:
|
|
1902
|
+
"""Create and start agent processor once all services are ready.
|
|
1903
|
+
|
|
1904
|
+
This replaces the placeholder task pattern with proper dependency injection.
|
|
1905
|
+
"""
|
|
1906
|
+
logger.info("Waiting for services to be ready before starting agent processor...")
|
|
1907
|
+
|
|
1908
|
+
# Wait for all critical services to be available
|
|
1909
|
+
await self._wait_for_critical_services(timeout=30.0)
|
|
1910
|
+
|
|
1911
|
+
# Check if agent processor is built (may be None in first-run setup mode)
|
|
1912
|
+
if not self.agent_processor:
|
|
1913
|
+
from ciris_engine.logic.setup.first_run import is_first_run
|
|
1914
|
+
|
|
1915
|
+
if is_first_run():
|
|
1916
|
+
logger.info("Agent processor not started - first-run setup mode active")
|
|
1917
|
+
else:
|
|
1918
|
+
logger.error("Agent processor not initialized but setup was completed!")
|
|
1919
|
+
logger.error("This indicates a configuration error - check LLM settings")
|
|
1920
|
+
return
|
|
1921
|
+
|
|
1922
|
+
# Start the multi-service sink if available
|
|
1923
|
+
if self.bus_manager:
|
|
1924
|
+
_sink_task = asyncio.create_task(self.bus_manager.start())
|
|
1925
|
+
logger.info("Started multi-service sink as background task")
|
|
1926
|
+
|
|
1927
|
+
# Start agent processing with default rounds
|
|
1928
|
+
effective_num_rounds = DEFAULT_NUM_ROUNDS
|
|
1929
|
+
logger.info(
|
|
1930
|
+
f"Starting agent processor (num_rounds={effective_num_rounds if effective_num_rounds != -1 else 'infinite'})..."
|
|
1931
|
+
)
|
|
1932
|
+
|
|
1933
|
+
# Start the actual agent processing
|
|
1934
|
+
await self.agent_processor.start_processing(effective_num_rounds)
|
|
1935
|
+
|
|
1936
|
+
def _register_core_services(self) -> None:
|
|
1937
|
+
"""Register core services in the service registry."""
|
|
1938
|
+
self.service_initializer.register_core_services()
|
|
1939
|
+
|
|
1940
|
+
def _build_action_dispatcher(self, dependencies: Any) -> ActionDispatcher:
|
|
1941
|
+
"""Build action dispatcher. Override in subclasses for custom sinks."""
|
|
1942
|
+
config = self._ensure_config()
|
|
1943
|
+
# Create BusManager for action handlers
|
|
1944
|
+
from ciris_engine.logic.buses import BusManager
|
|
1945
|
+
|
|
1946
|
+
if not self.service_registry:
|
|
1947
|
+
raise RuntimeError("Service registry not initialized")
|
|
1948
|
+
logger.debug(f"[AUDIT self.service_initializer exists: {self.service_initializer is not None}")
|
|
1949
|
+
if self.service_initializer:
|
|
1950
|
+
logger.debug(f"[AUDIT service_initializer.audit_service: {self.service_initializer.audit_service}")
|
|
1951
|
+
logger.debug(f"[AUDIT Creating BusManager with audit_service={self.audit_service}")
|
|
1952
|
+
logger.debug(f"[AUDIT self.audit_service type: {type(self.audit_service)}")
|
|
1953
|
+
logger.debug(f"[AUDIT self.audit_service is None: {self.audit_service is None}")
|
|
1954
|
+
|
|
1955
|
+
assert self.service_registry is not None
|
|
1956
|
+
# BusManager requires TimeServiceProtocol, not Optional[TimeService]
|
|
1957
|
+
if self.time_service is None:
|
|
1958
|
+
raise RuntimeError("TimeService must be initialized before creating BusManager")
|
|
1959
|
+
|
|
1960
|
+
bus_manager = BusManager(
|
|
1961
|
+
self.service_registry,
|
|
1962
|
+
time_service=self.time_service,
|
|
1963
|
+
telemetry_service=self.telemetry_service,
|
|
1964
|
+
audit_service=self.audit_service,
|
|
1965
|
+
)
|
|
1966
|
+
|
|
1967
|
+
return build_action_dispatcher(
|
|
1968
|
+
bus_manager=bus_manager,
|
|
1969
|
+
time_service=self.time_service,
|
|
1970
|
+
shutdown_callback=dependencies.shutdown_callback,
|
|
1971
|
+
max_rounds=config.workflow.max_rounds,
|
|
1972
|
+
telemetry_service=self.telemetry_service,
|
|
1973
|
+
secrets_service=self.secrets_service,
|
|
1974
|
+
)
|
|
1975
|
+
|
|
1976
|
+
def _should_exit_runtime_loop(
|
|
1977
|
+
self, agent_task: Optional[asyncio.Task[Any]], shutdown_logged: bool
|
|
1978
|
+
) -> tuple[bool, bool]:
|
|
1979
|
+
"""Check if runtime loop should exit.
|
|
1980
|
+
|
|
1981
|
+
Returns:
|
|
1982
|
+
Tuple of (should_exit, shutdown_logged)
|
|
1983
|
+
"""
|
|
1984
|
+
if agent_task and agent_task.done():
|
|
1985
|
+
return True, shutdown_logged
|
|
1986
|
+
if (self._shutdown_event and self._shutdown_event.is_set()) or is_global_shutdown_requested():
|
|
1987
|
+
return True, True
|
|
1988
|
+
return False, shutdown_logged
|
|
1989
|
+
|
|
1990
|
+
def _handle_completed_runtime_tasks(
|
|
1991
|
+
self,
|
|
1992
|
+
done: set[asyncio.Task[Any]],
|
|
1993
|
+
agent_task: Optional[asyncio.Task[Any]],
|
|
1994
|
+
adapter_tasks: List[asyncio.Task[Any]],
|
|
1995
|
+
all_tasks: list[asyncio.Task[Any]],
|
|
1996
|
+
) -> tuple[bool, bool]:
|
|
1997
|
+
"""Handle completed runtime tasks.
|
|
1998
|
+
|
|
1999
|
+
Returns:
|
|
2000
|
+
Tuple of (should_break, is_shutdown)
|
|
2001
|
+
"""
|
|
2002
|
+
from .ciris_runtime_helpers import handle_runtime_agent_task_completion, handle_runtime_task_failures
|
|
2003
|
+
|
|
2004
|
+
# Check for shutdown signal
|
|
2005
|
+
if (self._shutdown_event and self._shutdown_event.is_set()) or is_global_shutdown_requested():
|
|
2006
|
+
return True, True
|
|
2007
|
+
|
|
2008
|
+
# Check if agent task completed
|
|
2009
|
+
if agent_task and agent_task in done:
|
|
2010
|
+
handle_runtime_agent_task_completion(self, agent_task, adapter_tasks)
|
|
2011
|
+
return True, False
|
|
2012
|
+
|
|
2013
|
+
# Handle other task failures
|
|
2014
|
+
excluded_tasks = {t for t in all_tasks if t.get_name() in ["ShutdownEventWait", "GlobalShutdownWait"]}
|
|
2015
|
+
handle_runtime_task_failures(self, done, excluded_tasks)
|
|
2016
|
+
return False, False
|
|
2017
|
+
|
|
2018
|
+
async def run(self, _: Optional[int] = None) -> None:
|
|
2019
|
+
"""Run the agent processing loop with shutdown monitoring."""
|
|
2020
|
+
from .ciris_runtime_helpers import (
|
|
2021
|
+
finalize_runtime_execution,
|
|
2022
|
+
monitor_runtime_shutdown_signals,
|
|
2023
|
+
setup_runtime_monitoring_tasks,
|
|
2024
|
+
)
|
|
2025
|
+
|
|
2026
|
+
if not self._initialized:
|
|
2027
|
+
await self.initialize()
|
|
2028
|
+
|
|
2029
|
+
try:
|
|
2030
|
+
# Set up runtime monitoring tasks
|
|
2031
|
+
agent_task, adapter_tasks, all_tasks = setup_runtime_monitoring_tasks(self)
|
|
2032
|
+
if not all_tasks:
|
|
2033
|
+
logger.error("No tasks to monitor - exiting")
|
|
2034
|
+
return
|
|
2035
|
+
|
|
2036
|
+
# Keep monitoring until shutdown or agent task completes
|
|
2037
|
+
shutdown_logged = False
|
|
2038
|
+
while True:
|
|
2039
|
+
# Check exit conditions
|
|
2040
|
+
should_exit, shutdown_logged = self._should_exit_runtime_loop(agent_task, shutdown_logged)
|
|
2041
|
+
if should_exit:
|
|
2042
|
+
break
|
|
2043
|
+
|
|
2044
|
+
done, pending = await asyncio.wait(all_tasks, return_when=asyncio.FIRST_COMPLETED, timeout=1.0)
|
|
2045
|
+
|
|
2046
|
+
# Remove completed tasks from all_tasks to avoid re-processing
|
|
2047
|
+
all_tasks = [t for t in all_tasks if t not in done]
|
|
2048
|
+
|
|
2049
|
+
# Monitor shutdown signals
|
|
2050
|
+
shutdown_logged = monitor_runtime_shutdown_signals(self, shutdown_logged)
|
|
2051
|
+
|
|
2052
|
+
# Handle task completion
|
|
2053
|
+
should_break, _ = self._handle_completed_runtime_tasks(done, agent_task, adapter_tasks, all_tasks)
|
|
2054
|
+
if should_break:
|
|
2055
|
+
break
|
|
2056
|
+
|
|
2057
|
+
# Finalize execution
|
|
2058
|
+
await finalize_runtime_execution(self, set(pending) if "pending" in locals() else set())
|
|
2059
|
+
|
|
2060
|
+
except KeyboardInterrupt:
|
|
2061
|
+
logger.info("Received interrupt signal. Requesting shutdown.")
|
|
2062
|
+
self.request_shutdown("KeyboardInterrupt")
|
|
2063
|
+
except Exception as e:
|
|
2064
|
+
logger.error(f"Runtime error: {e}", exc_info=True)
|
|
2065
|
+
finally:
|
|
2066
|
+
logger.debug("Runtime.run() entering finally block")
|
|
2067
|
+
await self.shutdown()
|
|
2068
|
+
logger.debug("Runtime.run() exiting finally block")
|
|
2069
|
+
|
|
2070
|
+
async def shutdown(self) -> None:
|
|
2071
|
+
"""Gracefully shutdown all services with continuity awareness."""
|
|
2072
|
+
from .ciris_runtime_helpers import (
|
|
2073
|
+
cleanup_runtime_resources,
|
|
2074
|
+
execute_final_maintenance_tasks,
|
|
2075
|
+
execute_service_shutdown_sequence,
|
|
2076
|
+
finalize_shutdown_logging,
|
|
2077
|
+
handle_adapter_shutdown_cleanup,
|
|
2078
|
+
handle_agent_processor_shutdown,
|
|
2079
|
+
prepare_shutdown_maintenance_tasks,
|
|
2080
|
+
preserve_critical_system_state,
|
|
2081
|
+
validate_shutdown_completion,
|
|
2082
|
+
validate_shutdown_preconditions,
|
|
2083
|
+
)
|
|
2084
|
+
|
|
2085
|
+
# 1. Validate preconditions and early exit if needed
|
|
2086
|
+
if not validate_shutdown_preconditions(self):
|
|
2087
|
+
return
|
|
2088
|
+
|
|
2089
|
+
logger.info("Shutting down CIRIS Runtime...")
|
|
2090
|
+
|
|
2091
|
+
# 2. Prepare maintenance and stop scheduled services
|
|
2092
|
+
await prepare_shutdown_maintenance_tasks(self)
|
|
2093
|
+
|
|
2094
|
+
# 3. Execute final maintenance tasks
|
|
2095
|
+
await execute_final_maintenance_tasks(self)
|
|
2096
|
+
|
|
2097
|
+
# 4. Preserve critical system state
|
|
2098
|
+
await preserve_critical_system_state(self)
|
|
2099
|
+
|
|
2100
|
+
# 5. Handle agent processor shutdown
|
|
2101
|
+
logger.info("Initiating shutdown sequence for CIRIS Runtime...")
|
|
2102
|
+
self._ensure_shutdown_event()
|
|
2103
|
+
if self._shutdown_event:
|
|
2104
|
+
self._shutdown_event.set()
|
|
2105
|
+
|
|
2106
|
+
await handle_agent_processor_shutdown(self)
|
|
2107
|
+
|
|
2108
|
+
# 6. Handle adapter cleanup
|
|
2109
|
+
await handle_adapter_shutdown_cleanup(self)
|
|
2110
|
+
|
|
2111
|
+
# 7. Execute service shutdown sequence
|
|
2112
|
+
logger.debug("Stopping core services...")
|
|
2113
|
+
await execute_service_shutdown_sequence(self)
|
|
2114
|
+
|
|
2115
|
+
# 8. Finalize logging and cleanup resources
|
|
2116
|
+
await finalize_shutdown_logging(self)
|
|
2117
|
+
await cleanup_runtime_resources(self)
|
|
2118
|
+
validate_shutdown_completion(self)
|
|
2119
|
+
logger.debug("Shutdown method returning")
|
|
2120
|
+
|
|
2121
|
+
async def _create_startup_node(self) -> None:
|
|
2122
|
+
"""Create startup node for continuity tracking."""
|
|
2123
|
+
try:
|
|
2124
|
+
from ciris_engine.schemas.services.graph_core import GraphNode, GraphScope, NodeType
|
|
2125
|
+
from ciris_engine.schemas.types import JSONDict
|
|
2126
|
+
|
|
2127
|
+
# Create memory node for startup
|
|
2128
|
+
startup_node = GraphNode(
|
|
2129
|
+
id=f"startup_{self.time_service.now().isoformat() if self.time_service else datetime.now(timezone.utc).isoformat()}",
|
|
2130
|
+
type=NodeType.AGENT,
|
|
2131
|
+
scope=GraphScope.IDENTITY,
|
|
2132
|
+
attributes={"created_by": "runtime_startup", "tags": ["startup", "continuity_awareness"]},
|
|
2133
|
+
)
|
|
2134
|
+
|
|
2135
|
+
# Store in memory service
|
|
2136
|
+
if self.memory_service:
|
|
2137
|
+
await self.memory_service.memorize(startup_node)
|
|
2138
|
+
logger.info(f"Created startup continuity node: {startup_node.id}")
|
|
2139
|
+
|
|
2140
|
+
except Exception as e:
|
|
2141
|
+
logger.error(f"Failed to create startup node: {e}")
|
|
2142
|
+
|
|
2143
|
+
def _determine_shutdown_consent_status(self) -> str:
|
|
2144
|
+
"""Determine if shutdown was consensual based on agent processor result.
|
|
2145
|
+
|
|
2146
|
+
Returns:
|
|
2147
|
+
Consent status: 'accepted', 'rejected', or 'manual'
|
|
2148
|
+
"""
|
|
2149
|
+
if not self.agent_processor or not hasattr(self.agent_processor, "shutdown_processor"):
|
|
2150
|
+
return "manual"
|
|
2151
|
+
|
|
2152
|
+
shutdown_proc = self.agent_processor.shutdown_processor
|
|
2153
|
+
if not shutdown_proc or not hasattr(shutdown_proc, "shutdown_result"):
|
|
2154
|
+
return "manual"
|
|
2155
|
+
|
|
2156
|
+
result = shutdown_proc.shutdown_result
|
|
2157
|
+
if not result:
|
|
2158
|
+
return "manual"
|
|
2159
|
+
|
|
2160
|
+
if result.action == "shutdown_accepted" or result.status == "completed":
|
|
2161
|
+
return "accepted"
|
|
2162
|
+
elif result.action == "shutdown_rejected" or result.status == "rejected":
|
|
2163
|
+
return "rejected"
|
|
2164
|
+
|
|
2165
|
+
return "manual"
|
|
2166
|
+
|
|
2167
|
+
def _build_shutdown_node_attributes(self, reason: str, consent_status: str) -> JSONDict:
|
|
2168
|
+
"""Build attributes dict for shutdown memory node.
|
|
2169
|
+
|
|
2170
|
+
Args:
|
|
2171
|
+
reason: Shutdown reason text
|
|
2172
|
+
consent_status: Consent status ('accepted', 'rejected', 'manual')
|
|
2173
|
+
|
|
2174
|
+
Returns:
|
|
2175
|
+
Dictionary of node attributes
|
|
2176
|
+
"""
|
|
2177
|
+
now = self.time_service.now() if self.time_service else datetime.now(timezone.utc)
|
|
2178
|
+
return {
|
|
2179
|
+
"created_at": now.isoformat(),
|
|
2180
|
+
"updated_at": now.isoformat(),
|
|
2181
|
+
"created_by": "runtime_shutdown",
|
|
2182
|
+
"tags": ["shutdown", "continuity_awareness"],
|
|
2183
|
+
"reason": reason,
|
|
2184
|
+
"consent_status": consent_status,
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
async def _update_identity_with_shutdown_reference(self, shutdown_node_id: str) -> None:
|
|
2188
|
+
"""Update agent identity with shutdown memory reference.
|
|
2189
|
+
|
|
2190
|
+
Args:
|
|
2191
|
+
shutdown_node_id: ID of the shutdown node created
|
|
2192
|
+
"""
|
|
2193
|
+
if not self.agent_identity or not hasattr(self.agent_identity, "core_profile"):
|
|
2194
|
+
return
|
|
2195
|
+
|
|
2196
|
+
self.agent_identity.core_profile.last_shutdown_memory = shutdown_node_id
|
|
2197
|
+
|
|
2198
|
+
# Increment modification count
|
|
2199
|
+
if hasattr(self.agent_identity, "identity_metadata"):
|
|
2200
|
+
self.agent_identity.identity_metadata.modification_count += 1
|
|
2201
|
+
|
|
2202
|
+
# Save updated identity
|
|
2203
|
+
if self.identity_manager:
|
|
2204
|
+
await self.identity_manager._save_identity_to_graph(self.agent_identity)
|
|
2205
|
+
logger.debug("Agent identity updates saved to persistence layer")
|
|
2206
|
+
else:
|
|
2207
|
+
logger.debug("Agent identity updates stored in memory graph")
|
|
2208
|
+
|
|
2209
|
+
async def _preserve_shutdown_continuity(self) -> None:
|
|
2210
|
+
"""Preserve agent state for future reactivation."""
|
|
2211
|
+
try:
|
|
2212
|
+
from ciris_engine.schemas.runtime.extended import ShutdownContext
|
|
2213
|
+
from ciris_engine.schemas.services.graph_core import GraphNode, GraphScope, NodeType
|
|
2214
|
+
|
|
2215
|
+
# Create shutdown context
|
|
2216
|
+
shutdown_context = ShutdownContext(
|
|
2217
|
+
is_terminal=False,
|
|
2218
|
+
reason=self._shutdown_reason or "Graceful shutdown",
|
|
2219
|
+
initiated_by="runtime",
|
|
2220
|
+
allow_deferral=False,
|
|
2221
|
+
expected_reactivation=None,
|
|
2222
|
+
agreement_context=None,
|
|
2223
|
+
)
|
|
2224
|
+
|
|
2225
|
+
# Determine consent status and build node
|
|
2226
|
+
consent_status = self._determine_shutdown_consent_status()
|
|
2227
|
+
now = self.time_service.now() if self.time_service else datetime.now(timezone.utc)
|
|
2228
|
+
|
|
2229
|
+
shutdown_node = GraphNode(
|
|
2230
|
+
id=f"shutdown_{now.isoformat()}",
|
|
2231
|
+
type=NodeType.AGENT,
|
|
2232
|
+
scope=GraphScope.IDENTITY,
|
|
2233
|
+
attributes=self._build_shutdown_node_attributes(shutdown_context.reason, consent_status),
|
|
2234
|
+
)
|
|
2235
|
+
|
|
2236
|
+
# Store in memory service
|
|
2237
|
+
if self.memory_service:
|
|
2238
|
+
await self.memory_service.memorize(shutdown_node)
|
|
2239
|
+
logger.info(f"Preserved shutdown continuity: {shutdown_node.id}")
|
|
2240
|
+
await self._update_identity_with_shutdown_reference(shutdown_node.id)
|
|
2241
|
+
|
|
2242
|
+
except Exception as e:
|
|
2243
|
+
logger.error(f"Failed to preserve shutdown continuity: {e}")
|
|
2244
|
+
|
|
2245
|
+
def _parse_bootstrap_config(
|
|
2246
|
+
self,
|
|
2247
|
+
bootstrap: Optional["RuntimeBootstrapConfig"],
|
|
2248
|
+
essential_config: Optional[EssentialConfig],
|
|
2249
|
+
startup_channel_id: Optional[str],
|
|
2250
|
+
adapter_types: List[str],
|
|
2251
|
+
adapter_configs: Optional[Dict[str, AdapterConfig]],
|
|
2252
|
+
kwargs: JSONDict,
|
|
2253
|
+
) -> None:
|
|
2254
|
+
"""Parse bootstrap configuration or create from legacy parameters."""
|
|
2255
|
+
if bootstrap is not None:
|
|
2256
|
+
self.bootstrap = bootstrap
|
|
2257
|
+
self.essential_config = essential_config or EssentialConfig()
|
|
2258
|
+
self.essential_config.load_env_vars() # Load environment variables
|
|
2259
|
+
self.startup_channel_id = bootstrap.startup_channel_id or ""
|
|
2260
|
+
self.adapter_configs = bootstrap.adapter_overrides
|
|
2261
|
+
self.modules_to_load = bootstrap.modules
|
|
2262
|
+
self.debug = bootstrap.debug
|
|
2263
|
+
self._preload_tasks = bootstrap.preload_tasks
|
|
2264
|
+
else:
|
|
2265
|
+
self._create_bootstrap_from_legacy(
|
|
2266
|
+
essential_config, startup_channel_id, adapter_types, adapter_configs, kwargs
|
|
2267
|
+
)
|
|
2268
|
+
|
|
2269
|
+
def _create_bootstrap_from_legacy(
|
|
2270
|
+
self,
|
|
2271
|
+
essential_config: Optional[EssentialConfig],
|
|
2272
|
+
startup_channel_id: Optional[str],
|
|
2273
|
+
adapter_types: List[str],
|
|
2274
|
+
adapter_configs: Optional[Dict[str, AdapterConfig]],
|
|
2275
|
+
kwargs: JSONDict,
|
|
2276
|
+
) -> None:
|
|
2277
|
+
"""Create bootstrap config from legacy parameters."""
|
|
2278
|
+
self.essential_config = essential_config or EssentialConfig()
|
|
2279
|
+
self.essential_config.load_env_vars() # Load environment variables
|
|
2280
|
+
self.startup_channel_id = startup_channel_id or ""
|
|
2281
|
+
self.adapter_configs = adapter_configs or {}
|
|
2282
|
+
# Type narrow: kwargs.get returns JSONDict value, narrow to expected types
|
|
2283
|
+
modules_raw = kwargs.get("modules", [])
|
|
2284
|
+
self.modules_to_load = modules_raw if isinstance(modules_raw, list) else []
|
|
2285
|
+
debug_raw = kwargs.get("debug", False)
|
|
2286
|
+
self.debug = debug_raw if isinstance(debug_raw, bool) else False
|
|
2287
|
+
self._preload_tasks = []
|
|
2288
|
+
|
|
2289
|
+
from ciris_engine.schemas.runtime.adapter_management import AdapterLoadRequest
|
|
2290
|
+
from ciris_engine.schemas.runtime.bootstrap import RuntimeBootstrapConfig
|
|
2291
|
+
|
|
2292
|
+
adapter_load_requests = [
|
|
2293
|
+
AdapterLoadRequest(adapter_type=atype, adapter_id=atype, auto_start=True) for atype in adapter_types
|
|
2294
|
+
]
|
|
2295
|
+
self.bootstrap = RuntimeBootstrapConfig(
|
|
2296
|
+
adapters=adapter_load_requests,
|
|
2297
|
+
adapter_overrides=self.adapter_configs,
|
|
2298
|
+
modules=self.modules_to_load,
|
|
2299
|
+
startup_channel_id=self.startup_channel_id,
|
|
2300
|
+
debug=self.debug,
|
|
2301
|
+
preload_tasks=self._preload_tasks,
|
|
2302
|
+
)
|
|
2303
|
+
|
|
2304
|
+
def _check_mock_llm(self) -> None:
|
|
2305
|
+
"""Check for mock LLM environment variable and add to modules if needed."""
|
|
2306
|
+
if os.environ.get("CIRIS_MOCK_LLM", "").lower() in ("true", "1", "yes", "on"):
|
|
2307
|
+
logger.warning("CIRIS_MOCK_LLM environment variable detected in CIRISRuntime")
|
|
2308
|
+
if "mock_llm" not in self.modules_to_load:
|
|
2309
|
+
self.modules_to_load.append("mock_llm")
|
|
2310
|
+
logger.info("Added mock_llm to modules to load")
|
|
2311
|
+
|
|
2312
|
+
def _load_adapters_from_bootstrap(self) -> None:
|
|
2313
|
+
"""Load adapters from bootstrap configuration."""
|
|
2314
|
+
for load_request in self.bootstrap.adapters:
|
|
2315
|
+
try:
|
|
2316
|
+
adapter_class = load_adapter(load_request.adapter_type)
|
|
2317
|
+
|
|
2318
|
+
# Create AdapterStartupContext
|
|
2319
|
+
from ciris_engine.schemas.adapters.runtime_context import AdapterStartupContext
|
|
2320
|
+
|
|
2321
|
+
context = AdapterStartupContext(
|
|
2322
|
+
essential_config=self.essential_config or EssentialConfig(),
|
|
2323
|
+
modules_to_load=self.modules_to_load,
|
|
2324
|
+
startup_channel_id=self.startup_channel_id or "",
|
|
2325
|
+
debug=self.debug,
|
|
2326
|
+
bus_manager=None, # Will be set after initialization
|
|
2327
|
+
time_service=None, # Will be set after initialization
|
|
2328
|
+
service_registry=None, # Will be set after initialization
|
|
2329
|
+
)
|
|
2330
|
+
|
|
2331
|
+
# Apply overrides if present
|
|
2332
|
+
config = load_request.config or AdapterConfig(adapter_type=load_request.adapter_type)
|
|
2333
|
+
if load_request.adapter_id in self.adapter_configs:
|
|
2334
|
+
config = self.adapter_configs[load_request.adapter_id]
|
|
2335
|
+
|
|
2336
|
+
# Create adapter with context
|
|
2337
|
+
# Pass the settings as adapter_config so adapters can find them
|
|
2338
|
+
adapter_instance = adapter_class(self, context=context, adapter_config=config.settings) # type: ignore[call-arg]
|
|
2339
|
+
self.adapters.append(adapter_instance)
|
|
2340
|
+
logger.info(f"Successfully loaded adapter: {load_request.adapter_id}")
|
|
2341
|
+
except Exception as e:
|
|
2342
|
+
logger.error(f"Failed to load adapter '{load_request.adapter_id}': {e}", exc_info=True)
|