omnibase_infra 0.2.6__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.
- omnibase_infra/__init__.py +101 -0
- omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
- omnibase_infra/capabilities/__init__.py +15 -0
- omnibase_infra/capabilities/capability_inference_rules.py +211 -0
- omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
- omnibase_infra/capabilities/intent_type_extractor.py +160 -0
- omnibase_infra/cli/__init__.py +1 -0
- omnibase_infra/cli/commands.py +216 -0
- omnibase_infra/clients/__init__.py +0 -0
- omnibase_infra/configs/widget_mapping.yaml +176 -0
- omnibase_infra/constants_topic_patterns.py +26 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +264 -0
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +141 -0
- omnibase_infra/decorators/__init__.py +29 -0
- omnibase_infra/decorators/allow_any.py +109 -0
- omnibase_infra/dlq/__init__.py +90 -0
- omnibase_infra/dlq/constants_dlq.py +57 -0
- omnibase_infra/dlq/models/__init__.py +26 -0
- omnibase_infra/dlq/models/enum_replay_status.py +37 -0
- omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
- omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
- omnibase_infra/dlq/service_dlq_tracking.py +611 -0
- omnibase_infra/enums/__init__.py +132 -0
- omnibase_infra/enums/enum_any_type_violation.py +104 -0
- omnibase_infra/enums/enum_backend_type.py +27 -0
- omnibase_infra/enums/enum_capture_outcome.py +42 -0
- omnibase_infra/enums/enum_capture_state.py +88 -0
- omnibase_infra/enums/enum_chain_violation_type.py +119 -0
- omnibase_infra/enums/enum_circuit_state.py +51 -0
- omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
- omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
- omnibase_infra/enums/enum_contract_type.py +84 -0
- omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
- omnibase_infra/enums/enum_dispatch_status.py +191 -0
- omnibase_infra/enums/enum_environment.py +46 -0
- omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
- omnibase_infra/enums/enum_handler_error_type.py +111 -0
- omnibase_infra/enums/enum_handler_loader_error.py +178 -0
- omnibase_infra/enums/enum_handler_source_mode.py +86 -0
- omnibase_infra/enums/enum_handler_source_type.py +87 -0
- omnibase_infra/enums/enum_handler_type.py +77 -0
- omnibase_infra/enums/enum_handler_type_category.py +61 -0
- omnibase_infra/enums/enum_infra_transport_type.py +73 -0
- omnibase_infra/enums/enum_introspection_reason.py +154 -0
- omnibase_infra/enums/enum_kafka_acks.py +99 -0
- omnibase_infra/enums/enum_message_category.py +213 -0
- omnibase_infra/enums/enum_node_archetype.py +74 -0
- omnibase_infra/enums/enum_node_output_type.py +185 -0
- omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
- omnibase_infra/enums/enum_policy_type.py +32 -0
- omnibase_infra/enums/enum_registration_state.py +261 -0
- omnibase_infra/enums/enum_registration_status.py +33 -0
- omnibase_infra/enums/enum_registry_response_status.py +28 -0
- omnibase_infra/enums/enum_response_status.py +26 -0
- omnibase_infra/enums/enum_retry_error_category.py +98 -0
- omnibase_infra/enums/enum_security_rule_id.py +103 -0
- omnibase_infra/enums/enum_selection_strategy.py +91 -0
- omnibase_infra/enums/enum_topic_standard.py +42 -0
- omnibase_infra/enums/enum_validation_severity.py +78 -0
- omnibase_infra/errors/__init__.py +160 -0
- omnibase_infra/errors/error_architecture_violation.py +152 -0
- omnibase_infra/errors/error_binding_resolution.py +128 -0
- omnibase_infra/errors/error_chain_propagation.py +188 -0
- omnibase_infra/errors/error_compute_registry.py +95 -0
- omnibase_infra/errors/error_consul.py +132 -0
- omnibase_infra/errors/error_container_wiring.py +243 -0
- omnibase_infra/errors/error_event_bus_registry.py +105 -0
- omnibase_infra/errors/error_infra.py +610 -0
- omnibase_infra/errors/error_message_type_registry.py +101 -0
- omnibase_infra/errors/error_policy_registry.py +115 -0
- omnibase_infra/errors/error_vault.py +123 -0
- omnibase_infra/event_bus/__init__.py +72 -0
- omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +84 -0
- omnibase_infra/event_bus/event_bus_inmemory.py +797 -0
- omnibase_infra/event_bus/event_bus_kafka.py +1716 -0
- omnibase_infra/event_bus/mixin_kafka_broadcast.py +180 -0
- omnibase_infra/event_bus/mixin_kafka_dlq.py +771 -0
- omnibase_infra/event_bus/models/__init__.py +29 -0
- omnibase_infra/event_bus/models/config/__init__.py +20 -0
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +693 -0
- omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
- omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
- omnibase_infra/event_bus/models/model_event_headers.py +115 -0
- omnibase_infra/event_bus/models/model_event_message.py +60 -0
- omnibase_infra/event_bus/testing/__init__.py +26 -0
- omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
- omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
- omnibase_infra/event_bus/topic_constants.py +376 -0
- omnibase_infra/handlers/__init__.py +82 -0
- omnibase_infra/handlers/filesystem/__init__.py +48 -0
- omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
- omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
- omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
- omnibase_infra/handlers/handler_consul.py +795 -0
- omnibase_infra/handlers/handler_db.py +1046 -0
- omnibase_infra/handlers/handler_filesystem.py +1478 -0
- omnibase_infra/handlers/handler_graph.py +2015 -0
- omnibase_infra/handlers/handler_http.py +926 -0
- omnibase_infra/handlers/handler_intent.py +387 -0
- omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
- omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
- omnibase_infra/handlers/handler_mcp.py +1430 -0
- omnibase_infra/handlers/handler_qdrant.py +1076 -0
- omnibase_infra/handlers/handler_vault.py +428 -0
- omnibase_infra/handlers/mcp/__init__.py +19 -0
- omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
- omnibase_infra/handlers/mcp/protocols.py +178 -0
- omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
- omnibase_infra/handlers/mixins/__init__.py +47 -0
- omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
- omnibase_infra/handlers/mixins/mixin_consul_kv.py +338 -0
- omnibase_infra/handlers/mixins/mixin_consul_service.py +542 -0
- omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
- omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
- omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
- omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
- omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
- omnibase_infra/handlers/models/__init__.py +286 -0
- omnibase_infra/handlers/models/consul/__init__.py +81 -0
- omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
- omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
- omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
- omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
- omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
- omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
- omnibase_infra/handlers/models/graph/__init__.py +35 -0
- omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
- omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
- omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
- omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
- omnibase_infra/handlers/models/http/__init__.py +50 -0
- omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
- omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
- omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
- omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
- omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
- omnibase_infra/handlers/models/mcp/__init__.py +23 -0
- omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
- omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
- omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
- omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
- omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
- omnibase_infra/handlers/models/model_db_query_response.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
- omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
- omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
- omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
- omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
- omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
- omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
- omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_handler_response.py +103 -0
- omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
- omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
- omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
- omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
- omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
- omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
- omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
- omnibase_infra/handlers/models/model_operation_context.py +187 -0
- omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_retry_state.py +162 -0
- omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
- omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
- omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
- omnibase_infra/handlers/models/vault/__init__.py +69 -0
- omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
- omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
- omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
- omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
- omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
- omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
- omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
- omnibase_infra/handlers/registration_storage/__init__.py +43 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +922 -0
- omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
- omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
- omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
- omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
- omnibase_infra/handlers/service_discovery/__init__.py +43 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +1051 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
- omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
- omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
- omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
- omnibase_infra/handlers/service_discovery/models/model_service_info.py +109 -0
- omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
- omnibase_infra/idempotency/__init__.py +94 -0
- omnibase_infra/idempotency/models/__init__.py +43 -0
- omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
- omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
- omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
- omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
- omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
- omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
- omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
- omnibase_infra/idempotency/store_inmemory.py +265 -0
- omnibase_infra/idempotency/store_postgres.py +923 -0
- omnibase_infra/infrastructure/__init__.py +0 -0
- omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
- omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
- omnibase_infra/mixins/__init__.py +71 -0
- omnibase_infra/mixins/mixin_async_circuit_breaker.py +656 -0
- omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
- omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
- omnibase_infra/mixins/mixin_node_introspection.py +2670 -0
- omnibase_infra/mixins/mixin_retry_execution.py +386 -0
- omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
- omnibase_infra/models/__init__.py +144 -0
- omnibase_infra/models/bindings/__init__.py +59 -0
- omnibase_infra/models/bindings/constants.py +144 -0
- omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
- omnibase_infra/models/bindings/model_operation_binding.py +44 -0
- omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
- omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
- omnibase_infra/models/corpus/__init__.py +17 -0
- omnibase_infra/models/corpus/model_capture_config.py +133 -0
- omnibase_infra/models/corpus/model_capture_result.py +86 -0
- omnibase_infra/models/discovery/__init__.py +42 -0
- omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
- omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
- omnibase_infra/models/discovery/model_introspection_config.py +330 -0
- omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
- omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
- omnibase_infra/models/dispatch/__init__.py +155 -0
- omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
- omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
- omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
- omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
- omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
- omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
- omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
- omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
- omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
- omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
- omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
- omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
- omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
- omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
- omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
- omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
- omnibase_infra/models/errors/__init__.py +45 -0
- omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
- omnibase_infra/models/errors/model_infra_error_context.py +99 -0
- omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
- omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
- omnibase_infra/models/handlers/__init__.py +80 -0
- omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
- omnibase_infra/models/handlers/model_contract_discovery_result.py +82 -0
- omnibase_infra/models/handlers/model_handler_descriptor.py +200 -0
- omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
- omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
- omnibase_infra/models/health/__init__.py +9 -0
- omnibase_infra/models/health/model_health_check_result.py +40 -0
- omnibase_infra/models/lifecycle/__init__.py +39 -0
- omnibase_infra/models/logging/__init__.py +51 -0
- omnibase_infra/models/logging/model_log_context.py +756 -0
- omnibase_infra/models/mcp/__init__.py +15 -0
- omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
- omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
- omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
- omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
- omnibase_infra/models/model_node_identity.py +126 -0
- omnibase_infra/models/model_retry_error_classification.py +78 -0
- omnibase_infra/models/projection/__init__.py +43 -0
- omnibase_infra/models/projection/model_capability_fields.py +112 -0
- omnibase_infra/models/projection/model_registration_projection.py +434 -0
- omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
- omnibase_infra/models/projection/model_sequence_info.py +182 -0
- omnibase_infra/models/projection/model_snapshot_topic_config.py +591 -0
- omnibase_infra/models/projectors/__init__.py +41 -0
- omnibase_infra/models/projectors/model_projector_column.py +289 -0
- omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
- omnibase_infra/models/projectors/model_projector_index.py +270 -0
- omnibase_infra/models/projectors/model_projector_schema.py +415 -0
- omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
- omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
- omnibase_infra/models/registration/__init__.py +68 -0
- omnibase_infra/models/registration/commands/__init__.py +15 -0
- omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
- omnibase_infra/models/registration/events/__init__.py +56 -0
- omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
- omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
- omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
- omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
- omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
- omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
- omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
- omnibase_infra/models/registration/model_node_capabilities.py +190 -0
- omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
- omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +195 -0
- omnibase_infra/models/registration/model_node_metadata.py +79 -0
- omnibase_infra/models/registration/model_node_registration.py +162 -0
- omnibase_infra/models/registration/model_node_registration_record.py +162 -0
- omnibase_infra/models/registry/__init__.py +29 -0
- omnibase_infra/models/registry/model_domain_constraint.py +202 -0
- omnibase_infra/models/registry/model_message_type_entry.py +271 -0
- omnibase_infra/models/resilience/__init__.py +9 -0
- omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
- omnibase_infra/models/routing/__init__.py +25 -0
- omnibase_infra/models/routing/model_routing_entry.py +52 -0
- omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
- omnibase_infra/models/runtime/__init__.py +49 -0
- omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
- omnibase_infra/models/runtime/model_discovery_error.py +81 -0
- omnibase_infra/models/runtime/model_discovery_result.py +162 -0
- omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
- omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
- omnibase_infra/models/runtime/model_handler_contract.py +296 -0
- omnibase_infra/models/runtime/model_loaded_handler.py +129 -0
- omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
- omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
- omnibase_infra/models/security/__init__.py +50 -0
- omnibase_infra/models/security/classification_levels.py +99 -0
- omnibase_infra/models/security/model_environment_policy.py +145 -0
- omnibase_infra/models/security/model_handler_security_policy.py +107 -0
- omnibase_infra/models/security/model_security_error.py +81 -0
- omnibase_infra/models/security/model_security_validation_result.py +328 -0
- omnibase_infra/models/security/model_security_warning.py +67 -0
- omnibase_infra/models/snapshot/__init__.py +27 -0
- omnibase_infra/models/snapshot/model_field_change.py +65 -0
- omnibase_infra/models/snapshot/model_snapshot.py +270 -0
- omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
- omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
- omnibase_infra/models/types/__init__.py +71 -0
- omnibase_infra/models/validation/__init__.py +89 -0
- omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
- omnibase_infra/models/validation/model_any_type_violation.py +141 -0
- omnibase_infra/models/validation/model_category_match_result.py +345 -0
- omnibase_infra/models/validation/model_chain_violation.py +166 -0
- omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
- omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
- omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
- omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
- omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
- omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
- omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
- omnibase_infra/models/validation/model_output_validation_params.py +74 -0
- omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
- omnibase_infra/models/validation/model_validation_error_params.py +84 -0
- omnibase_infra/models/validation/model_validation_outcome.py +287 -0
- omnibase_infra/nodes/__init__.py +57 -0
- omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
- omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
- omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +203 -0
- omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
- omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
- omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
- omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
- omnibase_infra/nodes/architecture_validator/node.py +262 -0
- omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
- omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
- omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
- omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
- omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +106 -0
- omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
- omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
- omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
- omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
- omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
- omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
- omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
- omnibase_infra/nodes/effects/README.md +358 -0
- omnibase_infra/nodes/effects/__init__.py +26 -0
- omnibase_infra/nodes/effects/contract.yaml +167 -0
- omnibase_infra/nodes/effects/models/__init__.py +32 -0
- omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
- omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
- omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
- omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
- omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
- omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
- omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
- omnibase_infra/nodes/effects/registry_effect.py +525 -0
- omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
- omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
- omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
- omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
- omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
- omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
- omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
- omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
- omnibase_infra/nodes/node_intent_storage_effect/__init__.py +50 -0
- omnibase_infra/nodes/node_intent_storage_effect/contract.yaml +194 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/__init__.py +24 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_input.py +141 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_output.py +130 -0
- omnibase_infra/nodes/node_intent_storage_effect/node.py +94 -0
- omnibase_infra/nodes/node_intent_storage_effect/registry/__init__.py +35 -0
- omnibase_infra/nodes/node_intent_storage_effect/registry/registry_infra_intent_storage.py +294 -0
- omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
- omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
- omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
- omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
- omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
- omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
- omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
- omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
- omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
- omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
- omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
- omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
- omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
- omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +482 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +694 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
- omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
- omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
- omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
- omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +528 -0
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +393 -0
- omnibase_infra/nodes/node_registration_orchestrator/wiring.py +743 -0
- omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
- omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
- omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
- omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
- omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
- omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +220 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
- omnibase_infra/nodes/node_registration_storage_effect/node.py +112 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +215 -0
- omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
- omnibase_infra/nodes/node_registry_effect/contract.yaml +677 -0
- omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +417 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
- omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
- omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
- omnibase_infra/nodes/node_registry_effect/node.py +165 -0
- omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
- omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
- omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +222 -0
- omnibase_infra/nodes/reducers/__init__.py +30 -0
- omnibase_infra/nodes/reducers/models/__init__.py +37 -0
- omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +87 -0
- omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
- omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
- omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
- omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
- omnibase_infra/nodes/reducers/registration_reducer.py +1138 -0
- omnibase_infra/observability/__init__.py +143 -0
- omnibase_infra/observability/constants_metrics.py +91 -0
- omnibase_infra/observability/factory_observability_sink.py +525 -0
- omnibase_infra/observability/handlers/__init__.py +118 -0
- omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
- omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
- omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
- omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
- omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
- omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
- omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
- omnibase_infra/observability/hooks/__init__.py +74 -0
- omnibase_infra/observability/hooks/hook_observability.py +1223 -0
- omnibase_infra/observability/models/__init__.py +30 -0
- omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
- omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
- omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
- omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
- omnibase_infra/observability/sinks/__init__.py +69 -0
- omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
- omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
- omnibase_infra/plugins/__init__.py +27 -0
- omnibase_infra/plugins/examples/__init__.py +28 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
- omnibase_infra/plugins/models/__init__.py +21 -0
- omnibase_infra/plugins/models/model_plugin_context.py +76 -0
- omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
- omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
- omnibase_infra/plugins/plugin_compute_base.py +449 -0
- omnibase_infra/projectors/__init__.py +30 -0
- omnibase_infra/projectors/contracts/__init__.py +63 -0
- omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
- omnibase_infra/projectors/projection_reader_registration.py +1559 -0
- omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
- omnibase_infra/protocols/__init__.py +104 -0
- omnibase_infra/protocols/protocol_capability_projection.py +253 -0
- omnibase_infra/protocols/protocol_capability_query.py +251 -0
- omnibase_infra/protocols/protocol_container_aware.py +200 -0
- omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
- omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
- omnibase_infra/protocols/protocol_event_projector.py +96 -0
- omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
- omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
- omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
- omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
- omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
- omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
- omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
- omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
- omnibase_infra/runtime/__init__.py +445 -0
- omnibase_infra/runtime/binding_config_resolver.py +2771 -0
- omnibase_infra/runtime/binding_resolver.py +753 -0
- omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
- omnibase_infra/runtime/constants_notification.py +75 -0
- omnibase_infra/runtime/constants_security.py +70 -0
- omnibase_infra/runtime/contract_handler_discovery.py +587 -0
- omnibase_infra/runtime/contract_loaders/__init__.py +51 -0
- omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
- omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
- omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
- omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
- omnibase_infra/runtime/emit_daemon/cli.py +844 -0
- omnibase_infra/runtime/emit_daemon/client.py +811 -0
- omnibase_infra/runtime/emit_daemon/config.py +535 -0
- omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
- omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
- omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
- omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
- omnibase_infra/runtime/emit_daemon/queue.py +618 -0
- omnibase_infra/runtime/enums/__init__.py +18 -0
- omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
- omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
- omnibase_infra/runtime/envelope_validator.py +179 -0
- omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
- omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
- omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
- omnibase_infra/runtime/handler_contract_source.py +750 -0
- omnibase_infra/runtime/handler_identity.py +81 -0
- omnibase_infra/runtime/handler_plugin_loader.py +2046 -0
- omnibase_infra/runtime/handler_registry.py +329 -0
- omnibase_infra/runtime/handler_source_resolver.py +367 -0
- omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
- omnibase_infra/runtime/kafka_contract_source.py +984 -0
- omnibase_infra/runtime/kernel.py +40 -0
- omnibase_infra/runtime/mixin_policy_validation.py +522 -0
- omnibase_infra/runtime/mixin_semver_cache.py +402 -0
- omnibase_infra/runtime/mixins/__init__.py +24 -0
- omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
- omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +778 -0
- omnibase_infra/runtime/models/__init__.py +229 -0
- omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
- omnibase_infra/runtime/models/model_binding_config.py +168 -0
- omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
- omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
- omnibase_infra/runtime/models/model_cached_secret.py +138 -0
- omnibase_infra/runtime/models/model_compute_key.py +138 -0
- omnibase_infra/runtime/models/model_compute_registration.py +97 -0
- omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
- omnibase_infra/runtime/models/model_config_ref.py +331 -0
- omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
- omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
- omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
- omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
- omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
- omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
- omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
- omnibase_infra/runtime/models/model_failed_component.py +55 -0
- omnibase_infra/runtime/models/model_health_check_response.py +168 -0
- omnibase_infra/runtime/models/model_health_check_result.py +229 -0
- omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
- omnibase_infra/runtime/models/model_logging_config.py +42 -0
- omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
- omnibase_infra/runtime/models/model_optional_string.py +94 -0
- omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
- omnibase_infra/runtime/models/model_policy_context.py +100 -0
- omnibase_infra/runtime/models/model_policy_key.py +138 -0
- omnibase_infra/runtime/models/model_policy_registration.py +139 -0
- omnibase_infra/runtime/models/model_policy_result.py +103 -0
- omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
- omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
- omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
- omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
- omnibase_infra/runtime/models/model_retry_policy.py +105 -0
- omnibase_infra/runtime/models/model_runtime_config.py +150 -0
- omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_config.py +625 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
- omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
- omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
- omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
- omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
- omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
- omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
- omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
- omnibase_infra/runtime/models/model_security_config.py +109 -0
- omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
- omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
- omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
- omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
- omnibase_infra/runtime/projector_schema_manager.py +565 -0
- omnibase_infra/runtime/projector_shell.py +1330 -0
- omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
- omnibase_infra/runtime/protocol_contract_source.py +92 -0
- omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
- omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
- omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
- omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
- omnibase_infra/runtime/protocol_policy.py +366 -0
- omnibase_infra/runtime/protocols/__init__.py +37 -0
- omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
- omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
- omnibase_infra/runtime/registry/__init__.py +93 -0
- omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
- omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
- omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
- omnibase_infra/runtime/registry/registry_message_type.py +542 -0
- omnibase_infra/runtime/registry/registry_protocol_binding.py +445 -0
- omnibase_infra/runtime/registry_compute.py +1143 -0
- omnibase_infra/runtime/registry_contract_source.py +693 -0
- omnibase_infra/runtime/registry_dispatcher.py +678 -0
- omnibase_infra/runtime/registry_policy.py +1185 -0
- omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
- omnibase_infra/runtime/runtime_scheduler.py +1070 -0
- omnibase_infra/runtime/secret_resolver.py +2112 -0
- omnibase_infra/runtime/security_metadata_validator.py +776 -0
- omnibase_infra/runtime/service_kernel.py +1651 -0
- omnibase_infra/runtime/service_message_dispatch_engine.py +2350 -0
- omnibase_infra/runtime/service_runtime_host_process.py +3493 -0
- omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
- omnibase_infra/runtime/transition_notification_publisher.py +765 -0
- omnibase_infra/runtime/util_container_wiring.py +1124 -0
- omnibase_infra/runtime/util_validation.py +314 -0
- omnibase_infra/runtime/util_version.py +98 -0
- omnibase_infra/runtime/util_wiring.py +723 -0
- omnibase_infra/schemas/schema_registration_projection.sql +320 -0
- omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
- omnibase_infra/services/__init__.py +89 -0
- omnibase_infra/services/corpus_capture.py +684 -0
- omnibase_infra/services/mcp/__init__.py +31 -0
- omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
- omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
- omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
- omnibase_infra/services/mcp/service_mcp_tool_sync.py +565 -0
- omnibase_infra/services/registry_api/__init__.py +40 -0
- omnibase_infra/services/registry_api/main.py +261 -0
- omnibase_infra/services/registry_api/models/__init__.py +66 -0
- omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
- omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
- omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
- omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
- omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
- omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
- omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
- omnibase_infra/services/registry_api/models/model_warning.py +49 -0
- omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
- omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
- omnibase_infra/services/registry_api/routes.py +371 -0
- omnibase_infra/services/registry_api/service.py +837 -0
- omnibase_infra/services/service_capability_query.py +945 -0
- omnibase_infra/services/service_health.py +898 -0
- omnibase_infra/services/service_node_selector.py +530 -0
- omnibase_infra/services/service_timeout_emitter.py +699 -0
- omnibase_infra/services/service_timeout_scanner.py +394 -0
- omnibase_infra/services/session/__init__.py +56 -0
- omnibase_infra/services/session/config_consumer.py +137 -0
- omnibase_infra/services/session/config_store.py +139 -0
- omnibase_infra/services/session/consumer.py +1007 -0
- omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
- omnibase_infra/services/session/store.py +997 -0
- omnibase_infra/services/snapshot/__init__.py +31 -0
- omnibase_infra/services/snapshot/service_snapshot.py +647 -0
- omnibase_infra/services/snapshot/store_inmemory.py +637 -0
- omnibase_infra/services/snapshot/store_postgres.py +1279 -0
- omnibase_infra/shared/__init__.py +8 -0
- omnibase_infra/testing/__init__.py +10 -0
- omnibase_infra/testing/utils.py +23 -0
- omnibase_infra/topics/__init__.py +45 -0
- omnibase_infra/topics/platform_topic_suffixes.py +140 -0
- omnibase_infra/topics/util_topic_composition.py +95 -0
- omnibase_infra/types/__init__.py +48 -0
- omnibase_infra/types/type_cache_info.py +49 -0
- omnibase_infra/types/type_dsn.py +173 -0
- omnibase_infra/types/type_infra_aliases.py +60 -0
- omnibase_infra/types/typed_dict/__init__.py +29 -0
- omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
- omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
- omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
- omnibase_infra/types/typed_dict_capabilities.py +64 -0
- omnibase_infra/utils/__init__.py +117 -0
- omnibase_infra/utils/correlation.py +208 -0
- omnibase_infra/utils/util_atomic_file.py +261 -0
- omnibase_infra/utils/util_consumer_group.py +232 -0
- omnibase_infra/utils/util_datetime.py +372 -0
- omnibase_infra/utils/util_db_transaction.py +239 -0
- omnibase_infra/utils/util_dsn_validation.py +333 -0
- omnibase_infra/utils/util_env_parsing.py +264 -0
- omnibase_infra/utils/util_error_sanitization.py +457 -0
- omnibase_infra/utils/util_pydantic_validators.py +477 -0
- omnibase_infra/utils/util_retry_optimistic.py +281 -0
- omnibase_infra/utils/util_semver.py +233 -0
- omnibase_infra/validation/__init__.py +307 -0
- omnibase_infra/validation/contracts/security.validation.yaml +114 -0
- omnibase_infra/validation/enums/__init__.py +11 -0
- omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
- omnibase_infra/validation/infra_validators.py +1514 -0
- omnibase_infra/validation/linter_contract.py +907 -0
- omnibase_infra/validation/mixin_any_type_classification.py +120 -0
- omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
- omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
- omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
- omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
- omnibase_infra/validation/models/__init__.py +15 -0
- omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
- omnibase_infra/validation/models/model_contract_violation.py +41 -0
- omnibase_infra/validation/service_validation_aggregator.py +395 -0
- omnibase_infra/validation/validation_exemptions.yaml +2033 -0
- omnibase_infra/validation/validator_any_type.py +715 -0
- omnibase_infra/validation/validator_chain_propagation.py +839 -0
- omnibase_infra/validation/validator_execution_shape.py +465 -0
- omnibase_infra/validation/validator_localhandler.py +261 -0
- omnibase_infra/validation/validator_registration_security.py +410 -0
- omnibase_infra/validation/validator_routing_coverage.py +1020 -0
- omnibase_infra/validation/validator_runtime_shape.py +915 -0
- omnibase_infra/validation/validator_security.py +513 -0
- omnibase_infra/validation/validator_topic_category.py +1152 -0
- omnibase_infra-0.2.6.dist-info/METADATA +197 -0
- omnibase_infra-0.2.6.dist-info/RECORD +833 -0
- omnibase_infra-0.2.6.dist-info/WHEEL +4 -0
- omnibase_infra-0.2.6.dist-info/entry_points.txt +5 -0
- omnibase_infra-0.2.6.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,997 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""PostgreSQL storage adapter for session snapshots.
|
|
4
|
+
|
|
5
|
+
Persists session snapshots to PostgreSQL with child tables
|
|
6
|
+
for prompts and tools. Supports idempotent upserts.
|
|
7
|
+
|
|
8
|
+
Table Schema:
|
|
9
|
+
- claude_session_snapshots: Main aggregate with session lifecycle
|
|
10
|
+
- claude_session_prompts: Child table for prompt records
|
|
11
|
+
- claude_session_tools: Child table for tool execution records
|
|
12
|
+
- claude_session_event_idempotency: Deduplication tracking
|
|
13
|
+
|
|
14
|
+
See migrations/016_create_session_snapshots.sql for full schema.
|
|
15
|
+
|
|
16
|
+
Idempotency Cleanup:
|
|
17
|
+
The idempotency table has a 24-hour TTL. Call cleanup_expired_idempotency()
|
|
18
|
+
periodically (e.g., hourly) to remove expired records and prevent table bloat.
|
|
19
|
+
|
|
20
|
+
Moved from omniclaude as part of OMN-1526 architectural cleanup.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import asyncio
|
|
26
|
+
import logging
|
|
27
|
+
from datetime import UTC, datetime
|
|
28
|
+
from typing import TYPE_CHECKING, Any
|
|
29
|
+
from uuid import UUID
|
|
30
|
+
|
|
31
|
+
import asyncpg
|
|
32
|
+
|
|
33
|
+
from omnibase_infra.services.session.config_store import ConfigSessionStorage
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from asyncpg import Connection, Pool
|
|
37
|
+
|
|
38
|
+
logger = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SessionStoreNotInitializedError(RuntimeError):
|
|
42
|
+
"""Raised when SessionSnapshotStore operations are called before initialize().
|
|
43
|
+
|
|
44
|
+
This error indicates the store's connection pool has not been created.
|
|
45
|
+
Call initialize() before performing any database operations.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Idempotency domain constant
|
|
50
|
+
_IDEMPOTENCY_DOMAIN = "claude_session"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _ensure_uuid(value: str | UUID | None) -> UUID | None:
|
|
54
|
+
"""Convert string to UUID if needed.
|
|
55
|
+
|
|
56
|
+
Handles values that may be either string (from JSON deserialization)
|
|
57
|
+
or UUID objects, converting strings to proper UUID type for database.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
value: A string, UUID, or None.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
UUID object or None if input was None.
|
|
64
|
+
"""
|
|
65
|
+
if value is None:
|
|
66
|
+
return None
|
|
67
|
+
if isinstance(value, UUID):
|
|
68
|
+
return value
|
|
69
|
+
return UUID(value)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _ensure_datetime(value: str | datetime | None) -> datetime | None:
|
|
73
|
+
"""Convert string to datetime if needed.
|
|
74
|
+
|
|
75
|
+
Handles values that may be either ISO format string (from JSON deserialization)
|
|
76
|
+
or datetime objects, converting strings to proper datetime type for database.
|
|
77
|
+
|
|
78
|
+
Supports ISO 8601 format with timezone designators:
|
|
79
|
+
- "2024-01-15T10:30:00+00:00"
|
|
80
|
+
- "2024-01-15T10:30:00Z" (UTC shorthand)
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
value: An ISO format string, datetime, or None.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
datetime object or None if input was None.
|
|
87
|
+
"""
|
|
88
|
+
if value is None:
|
|
89
|
+
return None
|
|
90
|
+
if isinstance(value, datetime):
|
|
91
|
+
return value
|
|
92
|
+
# Parse ISO format string, handling 'Z' suffix for UTC
|
|
93
|
+
return datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class SessionSnapshotStore:
|
|
97
|
+
"""PostgreSQL storage for session snapshots.
|
|
98
|
+
|
|
99
|
+
Handles persistence of session snapshots with child tables
|
|
100
|
+
for prompts and tool executions. Uses asyncpg for async operations
|
|
101
|
+
and connection pooling.
|
|
102
|
+
|
|
103
|
+
Thread Safety:
|
|
104
|
+
This class is thread-safe. The asyncpg pool handles connection
|
|
105
|
+
management internally.
|
|
106
|
+
|
|
107
|
+
Example:
|
|
108
|
+
>>> config = ConfigSessionStorage(postgres_password=SecretStr("secret"))
|
|
109
|
+
>>> store = SessionSnapshotStore(config)
|
|
110
|
+
>>> await store.initialize()
|
|
111
|
+
>>> try:
|
|
112
|
+
... snapshot_id = await store.save_snapshot(snapshot_data, correlation_id)
|
|
113
|
+
... finally:
|
|
114
|
+
... await store.close()
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(self, config: ConfigSessionStorage) -> None:
|
|
118
|
+
"""Initialize store with configuration.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
config: PostgreSQL connection configuration.
|
|
122
|
+
"""
|
|
123
|
+
self._config = config
|
|
124
|
+
self._pool: Pool | None = None
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def is_initialized(self) -> bool:
|
|
128
|
+
"""Check if the store is initialized and ready for use."""
|
|
129
|
+
return self._pool is not None
|
|
130
|
+
|
|
131
|
+
async def initialize(self) -> None:
|
|
132
|
+
"""Initialize connection pool.
|
|
133
|
+
|
|
134
|
+
Creates an asyncpg connection pool with the configured parameters.
|
|
135
|
+
Must be called before any other operations.
|
|
136
|
+
|
|
137
|
+
Raises:
|
|
138
|
+
asyncpg.PostgresError: If connection fails.
|
|
139
|
+
"""
|
|
140
|
+
if self._pool is not None:
|
|
141
|
+
logger.warning("SessionSnapshotStore already initialized, skipping")
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
self._pool = await asyncpg.create_pool(
|
|
145
|
+
dsn=self._config.dsn,
|
|
146
|
+
min_size=self._config.pool_min_size,
|
|
147
|
+
max_size=self._config.pool_max_size,
|
|
148
|
+
command_timeout=self._config.query_timeout_seconds,
|
|
149
|
+
)
|
|
150
|
+
logger.info(
|
|
151
|
+
"SessionSnapshotStore initialized",
|
|
152
|
+
extra={
|
|
153
|
+
"pool_min_size": self._config.pool_min_size,
|
|
154
|
+
"pool_max_size": self._config.pool_max_size,
|
|
155
|
+
"timeout_seconds": self._config.query_timeout_seconds,
|
|
156
|
+
},
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
async def close(self) -> None:
|
|
160
|
+
"""Close connection pool.
|
|
161
|
+
|
|
162
|
+
Releases all connections back to the pool and closes them.
|
|
163
|
+
Safe to call multiple times.
|
|
164
|
+
"""
|
|
165
|
+
if self._pool is not None:
|
|
166
|
+
await self._pool.close()
|
|
167
|
+
self._pool = None
|
|
168
|
+
logger.info("SessionSnapshotStore closed")
|
|
169
|
+
|
|
170
|
+
def _require_pool(self) -> Pool:
|
|
171
|
+
"""Get pool or raise if not initialized.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
The connection pool.
|
|
175
|
+
|
|
176
|
+
Raises:
|
|
177
|
+
SessionStoreNotInitializedError: If store not initialized.
|
|
178
|
+
"""
|
|
179
|
+
if self._pool is None:
|
|
180
|
+
raise SessionStoreNotInitializedError(
|
|
181
|
+
"SessionSnapshotStore not initialized. Call initialize() first."
|
|
182
|
+
)
|
|
183
|
+
return self._pool
|
|
184
|
+
|
|
185
|
+
# =========================================================================
|
|
186
|
+
# Public API - CRUD Operations
|
|
187
|
+
# =========================================================================
|
|
188
|
+
|
|
189
|
+
# ONEX_EXCLUDE: any_type - dict[str, Any] required for JSON-like snapshot data
|
|
190
|
+
async def save_snapshot(
|
|
191
|
+
self,
|
|
192
|
+
snapshot: dict[str, Any],
|
|
193
|
+
correlation_id: UUID,
|
|
194
|
+
) -> UUID:
|
|
195
|
+
"""Save or update a session snapshot.
|
|
196
|
+
|
|
197
|
+
Uses upsert semantics - updates existing snapshot if session_id exists.
|
|
198
|
+
Handles child tables (prompts, tools) in a transaction.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
snapshot: Snapshot data containing:
|
|
202
|
+
- session_id: Required session identifier
|
|
203
|
+
- status: Session status (orphan, active, ended, timed_out)
|
|
204
|
+
- started_at: Session start time
|
|
205
|
+
- ended_at: Session end time (optional)
|
|
206
|
+
- duration_seconds: Session duration (optional)
|
|
207
|
+
- working_directory: Working directory path
|
|
208
|
+
- git_branch: Git branch name (optional)
|
|
209
|
+
- hook_source: How session was detected
|
|
210
|
+
- end_reason: Why session ended (optional)
|
|
211
|
+
- prompt_count: Number of prompts
|
|
212
|
+
- tool_count: Number of tool executions
|
|
213
|
+
- tools_used_count: Unique tools used
|
|
214
|
+
- event_count: Total events processed
|
|
215
|
+
- last_event_at: Timestamp of last event
|
|
216
|
+
- schema_version: Schema version
|
|
217
|
+
- prompts: List of prompt records (optional)
|
|
218
|
+
- tools: List of tool records (optional)
|
|
219
|
+
correlation_id: Correlation ID for tracing.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
The snapshot_id (UUID) of the saved snapshot.
|
|
223
|
+
|
|
224
|
+
Raises:
|
|
225
|
+
RuntimeError: If store not initialized.
|
|
226
|
+
asyncpg.PostgresError: If database operation fails.
|
|
227
|
+
"""
|
|
228
|
+
pool = self._require_pool()
|
|
229
|
+
|
|
230
|
+
async with pool.acquire() as conn:
|
|
231
|
+
async with conn.transaction():
|
|
232
|
+
# Upsert main snapshot first (required for foreign key references)
|
|
233
|
+
snapshot_id = await self._upsert_snapshot(conn, snapshot)
|
|
234
|
+
|
|
235
|
+
# Sync child tables in parallel if provided.
|
|
236
|
+
# This is safe because:
|
|
237
|
+
# 1. Both operations are within the same transaction (atomicity preserved)
|
|
238
|
+
# 2. prompts and tools tables are independent (no cross-table constraints)
|
|
239
|
+
# 3. Both reference the same snapshot_id but don't conflict with each other
|
|
240
|
+
prompts = snapshot.get("prompts", [])
|
|
241
|
+
tools = snapshot.get("tools", [])
|
|
242
|
+
|
|
243
|
+
if prompts or tools:
|
|
244
|
+
async with asyncio.TaskGroup() as tg:
|
|
245
|
+
if prompts:
|
|
246
|
+
tg.create_task(
|
|
247
|
+
self._sync_prompts_with_context(
|
|
248
|
+
conn, snapshot_id, prompts
|
|
249
|
+
)
|
|
250
|
+
)
|
|
251
|
+
if tools:
|
|
252
|
+
tg.create_task(
|
|
253
|
+
self._sync_tools_with_context(conn, snapshot_id, tools)
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
logger.debug(
|
|
257
|
+
"Saved session snapshot",
|
|
258
|
+
extra={
|
|
259
|
+
"snapshot_id": str(snapshot_id),
|
|
260
|
+
"session_id": snapshot.get("session_id"),
|
|
261
|
+
"correlation_id": str(correlation_id),
|
|
262
|
+
"prompt_count": len(prompts),
|
|
263
|
+
"tool_count": len(tools),
|
|
264
|
+
},
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
return snapshot_id
|
|
268
|
+
|
|
269
|
+
# ONEX_EXCLUDE: any_type - dict[str, Any] required for JSON-like snapshot data
|
|
270
|
+
async def get_snapshot(
|
|
271
|
+
self,
|
|
272
|
+
session_id: str,
|
|
273
|
+
correlation_id: UUID,
|
|
274
|
+
) -> dict[str, Any] | None:
|
|
275
|
+
"""Get snapshot by session_id.
|
|
276
|
+
|
|
277
|
+
Includes prompts and tools as nested lists.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
session_id: The session identifier.
|
|
281
|
+
correlation_id: Correlation ID for tracing.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Snapshot dict with prompts and tools, or None if not found.
|
|
285
|
+
|
|
286
|
+
Raises:
|
|
287
|
+
RuntimeError: If store not initialized.
|
|
288
|
+
asyncpg.PostgresError: If database operation fails.
|
|
289
|
+
"""
|
|
290
|
+
pool = self._require_pool()
|
|
291
|
+
|
|
292
|
+
async with pool.acquire() as conn:
|
|
293
|
+
# Fetch main snapshot
|
|
294
|
+
row = await conn.fetchrow(
|
|
295
|
+
"""
|
|
296
|
+
SELECT
|
|
297
|
+
snapshot_id, session_id, correlation_id, status,
|
|
298
|
+
started_at, ended_at, duration_seconds,
|
|
299
|
+
working_directory, git_branch, hook_source, end_reason,
|
|
300
|
+
prompt_count, tool_count, tools_used_count, event_count,
|
|
301
|
+
last_event_at, schema_version, created_at, updated_at
|
|
302
|
+
FROM claude_session_snapshots
|
|
303
|
+
WHERE session_id = $1
|
|
304
|
+
""",
|
|
305
|
+
session_id,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
if row is None:
|
|
309
|
+
logger.debug(
|
|
310
|
+
"Snapshot not found",
|
|
311
|
+
extra={
|
|
312
|
+
"session_id": session_id,
|
|
313
|
+
"correlation_id": str(correlation_id),
|
|
314
|
+
},
|
|
315
|
+
)
|
|
316
|
+
return None
|
|
317
|
+
|
|
318
|
+
snapshot = dict(row)
|
|
319
|
+
snapshot_id = snapshot["snapshot_id"]
|
|
320
|
+
|
|
321
|
+
# Fetch child records
|
|
322
|
+
snapshot["prompts"] = await self._fetch_prompts(conn, snapshot_id)
|
|
323
|
+
snapshot["tools"] = await self._fetch_tools(conn, snapshot_id)
|
|
324
|
+
|
|
325
|
+
logger.debug(
|
|
326
|
+
"Retrieved session snapshot",
|
|
327
|
+
extra={
|
|
328
|
+
"snapshot_id": str(snapshot_id),
|
|
329
|
+
"session_id": session_id,
|
|
330
|
+
"correlation_id": str(correlation_id),
|
|
331
|
+
},
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
return snapshot
|
|
335
|
+
|
|
336
|
+
# ONEX_EXCLUDE: any_type - dict[str, Any] required for JSON-like snapshot data
|
|
337
|
+
async def get_snapshot_by_id(
|
|
338
|
+
self,
|
|
339
|
+
snapshot_id: UUID,
|
|
340
|
+
correlation_id: UUID,
|
|
341
|
+
) -> dict[str, Any] | None:
|
|
342
|
+
"""Get snapshot by snapshot_id (UUID).
|
|
343
|
+
|
|
344
|
+
Includes prompts and tools as nested lists.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
snapshot_id: The snapshot UUID.
|
|
348
|
+
correlation_id: Correlation ID for tracing.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Snapshot dict with prompts and tools, or None if not found.
|
|
352
|
+
|
|
353
|
+
Raises:
|
|
354
|
+
RuntimeError: If store not initialized.
|
|
355
|
+
asyncpg.PostgresError: If database operation fails.
|
|
356
|
+
"""
|
|
357
|
+
pool = self._require_pool()
|
|
358
|
+
|
|
359
|
+
async with pool.acquire() as conn:
|
|
360
|
+
# Fetch main snapshot
|
|
361
|
+
row = await conn.fetchrow(
|
|
362
|
+
"""
|
|
363
|
+
SELECT
|
|
364
|
+
snapshot_id, session_id, correlation_id, status,
|
|
365
|
+
started_at, ended_at, duration_seconds,
|
|
366
|
+
working_directory, git_branch, hook_source, end_reason,
|
|
367
|
+
prompt_count, tool_count, tools_used_count, event_count,
|
|
368
|
+
last_event_at, schema_version, created_at, updated_at
|
|
369
|
+
FROM claude_session_snapshots
|
|
370
|
+
WHERE snapshot_id = $1
|
|
371
|
+
""",
|
|
372
|
+
snapshot_id,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
if row is None:
|
|
376
|
+
logger.debug(
|
|
377
|
+
"Snapshot not found by ID",
|
|
378
|
+
extra={
|
|
379
|
+
"snapshot_id": str(snapshot_id),
|
|
380
|
+
"correlation_id": str(correlation_id),
|
|
381
|
+
},
|
|
382
|
+
)
|
|
383
|
+
return None
|
|
384
|
+
|
|
385
|
+
snapshot = dict(row)
|
|
386
|
+
|
|
387
|
+
# Fetch child records
|
|
388
|
+
snapshot["prompts"] = await self._fetch_prompts(conn, snapshot_id)
|
|
389
|
+
snapshot["tools"] = await self._fetch_tools(conn, snapshot_id)
|
|
390
|
+
|
|
391
|
+
logger.debug(
|
|
392
|
+
"Retrieved session snapshot by ID",
|
|
393
|
+
extra={
|
|
394
|
+
"snapshot_id": str(snapshot_id),
|
|
395
|
+
"session_id": snapshot.get("session_id"),
|
|
396
|
+
"correlation_id": str(correlation_id),
|
|
397
|
+
},
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
return snapshot
|
|
401
|
+
|
|
402
|
+
async def list_snapshots(
|
|
403
|
+
self,
|
|
404
|
+
status: str | None = None,
|
|
405
|
+
working_directory: str | None = None,
|
|
406
|
+
limit: int = 100,
|
|
407
|
+
offset: int = 0,
|
|
408
|
+
correlation_id: UUID | None = None,
|
|
409
|
+
# ONEX_EXCLUDE: any_type - dict[str, Any] required for JSON-like snapshot data
|
|
410
|
+
) -> list[dict[str, Any]]:
|
|
411
|
+
"""List snapshots with optional filters.
|
|
412
|
+
|
|
413
|
+
Does NOT include prompts/tools (use get_snapshot for full data).
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
status: Filter by session status (orphan, active, ended, timed_out).
|
|
417
|
+
working_directory: Filter by working directory (exact match).
|
|
418
|
+
limit: Maximum number of results (default 100, max 1000).
|
|
419
|
+
offset: Number of results to skip (for pagination).
|
|
420
|
+
correlation_id: Correlation ID for tracing.
|
|
421
|
+
|
|
422
|
+
Returns:
|
|
423
|
+
List of snapshot dicts (without child records).
|
|
424
|
+
|
|
425
|
+
Raises:
|
|
426
|
+
RuntimeError: If store not initialized.
|
|
427
|
+
asyncpg.PostgresError: If database operation fails.
|
|
428
|
+
"""
|
|
429
|
+
pool = self._require_pool()
|
|
430
|
+
|
|
431
|
+
# Clamp limit to prevent excessive queries
|
|
432
|
+
limit = min(limit, 1000)
|
|
433
|
+
|
|
434
|
+
# Build query with optional filters
|
|
435
|
+
conditions: list[str] = []
|
|
436
|
+
# ONEX_EXCLUDE: any_type - list[Any] for SQL query parameters
|
|
437
|
+
params: list[Any] = []
|
|
438
|
+
param_idx = 1
|
|
439
|
+
|
|
440
|
+
if status is not None:
|
|
441
|
+
conditions.append(f"status = ${param_idx}")
|
|
442
|
+
params.append(status)
|
|
443
|
+
param_idx += 1
|
|
444
|
+
|
|
445
|
+
if working_directory is not None:
|
|
446
|
+
conditions.append(f"working_directory = ${param_idx}")
|
|
447
|
+
params.append(working_directory)
|
|
448
|
+
param_idx += 1
|
|
449
|
+
|
|
450
|
+
where_clause = ""
|
|
451
|
+
if conditions:
|
|
452
|
+
where_clause = "WHERE " + " AND ".join(conditions)
|
|
453
|
+
|
|
454
|
+
# Add limit and offset
|
|
455
|
+
params.append(limit)
|
|
456
|
+
params.append(offset)
|
|
457
|
+
|
|
458
|
+
# NOTE: S608 is a false positive - where_clause is built from validated
|
|
459
|
+
# string literals, all user inputs are parameterized via $N placeholders
|
|
460
|
+
query = f"""
|
|
461
|
+
SELECT
|
|
462
|
+
snapshot_id, session_id, correlation_id, status,
|
|
463
|
+
started_at, ended_at, duration_seconds,
|
|
464
|
+
working_directory, git_branch, hook_source, end_reason,
|
|
465
|
+
prompt_count, tool_count, tools_used_count, event_count,
|
|
466
|
+
last_event_at, schema_version, created_at, updated_at
|
|
467
|
+
FROM claude_session_snapshots
|
|
468
|
+
{where_clause}
|
|
469
|
+
ORDER BY last_event_at DESC
|
|
470
|
+
LIMIT ${param_idx} OFFSET ${param_idx + 1}
|
|
471
|
+
""" # noqa: S608
|
|
472
|
+
|
|
473
|
+
async with pool.acquire() as conn:
|
|
474
|
+
rows = await conn.fetch(query, *params)
|
|
475
|
+
|
|
476
|
+
snapshots = [dict(row) for row in rows]
|
|
477
|
+
|
|
478
|
+
logger.debug(
|
|
479
|
+
"Listed session snapshots",
|
|
480
|
+
extra={
|
|
481
|
+
"count": len(snapshots),
|
|
482
|
+
"status_filter": status,
|
|
483
|
+
"working_directory_filter": working_directory,
|
|
484
|
+
"limit": limit,
|
|
485
|
+
"offset": offset,
|
|
486
|
+
"correlation_id": str(correlation_id) if correlation_id else None,
|
|
487
|
+
},
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
return snapshots
|
|
491
|
+
|
|
492
|
+
async def delete_snapshot(
|
|
493
|
+
self,
|
|
494
|
+
session_id: str,
|
|
495
|
+
correlation_id: UUID,
|
|
496
|
+
) -> bool:
|
|
497
|
+
"""Delete a snapshot and its children.
|
|
498
|
+
|
|
499
|
+
Child records (prompts, tools) are deleted automatically via CASCADE.
|
|
500
|
+
|
|
501
|
+
Args:
|
|
502
|
+
session_id: The session identifier.
|
|
503
|
+
correlation_id: Correlation ID for tracing.
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
True if deleted, False if not found.
|
|
507
|
+
|
|
508
|
+
Raises:
|
|
509
|
+
RuntimeError: If store not initialized.
|
|
510
|
+
asyncpg.PostgresError: If database operation fails.
|
|
511
|
+
"""
|
|
512
|
+
pool = self._require_pool()
|
|
513
|
+
|
|
514
|
+
async with pool.acquire() as conn:
|
|
515
|
+
result = await conn.execute(
|
|
516
|
+
"DELETE FROM claude_session_snapshots WHERE session_id = $1",
|
|
517
|
+
session_id,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
# Parse result string (e.g., "DELETE 1" or "DELETE 0")
|
|
521
|
+
deleted = self._parse_row_count(result) > 0
|
|
522
|
+
|
|
523
|
+
logger.debug(
|
|
524
|
+
"Delete snapshot result",
|
|
525
|
+
extra={
|
|
526
|
+
"session_id": session_id,
|
|
527
|
+
"deleted": deleted,
|
|
528
|
+
"correlation_id": str(correlation_id),
|
|
529
|
+
},
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
return deleted
|
|
533
|
+
|
|
534
|
+
# =========================================================================
|
|
535
|
+
# Public API - Idempotency Operations
|
|
536
|
+
# =========================================================================
|
|
537
|
+
|
|
538
|
+
async def check_idempotency(
|
|
539
|
+
self,
|
|
540
|
+
message_id: UUID,
|
|
541
|
+
correlation_id: UUID,
|
|
542
|
+
) -> bool:
|
|
543
|
+
"""Check if message was already processed.
|
|
544
|
+
|
|
545
|
+
Uses SELECT to check if message_id exists in idempotency table.
|
|
546
|
+
|
|
547
|
+
Args:
|
|
548
|
+
message_id: The unique message identifier.
|
|
549
|
+
correlation_id: Correlation ID for tracing.
|
|
550
|
+
|
|
551
|
+
Returns:
|
|
552
|
+
True if processed (duplicate), False if new.
|
|
553
|
+
|
|
554
|
+
Raises:
|
|
555
|
+
RuntimeError: If store not initialized.
|
|
556
|
+
asyncpg.PostgresError: If database operation fails.
|
|
557
|
+
"""
|
|
558
|
+
pool = self._require_pool()
|
|
559
|
+
|
|
560
|
+
async with pool.acquire() as conn:
|
|
561
|
+
row = await conn.fetchrow(
|
|
562
|
+
"""
|
|
563
|
+
SELECT message_id FROM claude_session_event_idempotency
|
|
564
|
+
WHERE message_id = $1 AND domain = $2
|
|
565
|
+
""",
|
|
566
|
+
message_id,
|
|
567
|
+
_IDEMPOTENCY_DOMAIN,
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
is_duplicate = row is not None
|
|
571
|
+
|
|
572
|
+
if is_duplicate:
|
|
573
|
+
logger.debug(
|
|
574
|
+
"Duplicate message detected",
|
|
575
|
+
extra={
|
|
576
|
+
"message_id": str(message_id),
|
|
577
|
+
"correlation_id": str(correlation_id),
|
|
578
|
+
},
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
return is_duplicate
|
|
582
|
+
|
|
583
|
+
async def record_idempotency(
|
|
584
|
+
self,
|
|
585
|
+
message_id: UUID,
|
|
586
|
+
correlation_id: UUID,
|
|
587
|
+
) -> None:
|
|
588
|
+
"""Record message as processed for idempotency.
|
|
589
|
+
|
|
590
|
+
Uses INSERT ... ON CONFLICT DO NOTHING to handle races.
|
|
591
|
+
|
|
592
|
+
Args:
|
|
593
|
+
message_id: The unique message identifier.
|
|
594
|
+
correlation_id: Correlation ID for tracing.
|
|
595
|
+
|
|
596
|
+
Raises:
|
|
597
|
+
RuntimeError: If store not initialized.
|
|
598
|
+
asyncpg.PostgresError: If database operation fails.
|
|
599
|
+
"""
|
|
600
|
+
pool = self._require_pool()
|
|
601
|
+
|
|
602
|
+
async with pool.acquire() as conn:
|
|
603
|
+
await conn.execute(
|
|
604
|
+
"""
|
|
605
|
+
INSERT INTO claude_session_event_idempotency (message_id, domain)
|
|
606
|
+
VALUES ($1, $2)
|
|
607
|
+
ON CONFLICT (message_id) DO NOTHING
|
|
608
|
+
""",
|
|
609
|
+
message_id,
|
|
610
|
+
_IDEMPOTENCY_DOMAIN,
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
logger.debug(
|
|
614
|
+
"Recorded idempotency",
|
|
615
|
+
extra={
|
|
616
|
+
"message_id": str(message_id),
|
|
617
|
+
"correlation_id": str(correlation_id),
|
|
618
|
+
},
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
async def cleanup_expired_idempotency(
|
|
622
|
+
self,
|
|
623
|
+
correlation_id: UUID,
|
|
624
|
+
) -> int:
|
|
625
|
+
"""Remove expired idempotency records.
|
|
626
|
+
|
|
627
|
+
Deletes records where expires_at < NOW(). This prevents the idempotency
|
|
628
|
+
table from growing unbounded over time.
|
|
629
|
+
|
|
630
|
+
Scheduling Guidance:
|
|
631
|
+
This method should be called periodically to clean up expired records.
|
|
632
|
+
Recommended approaches:
|
|
633
|
+
|
|
634
|
+
1. **Cron Job / Scheduled Task**: Run every hour or daily
|
|
635
|
+
```python
|
|
636
|
+
# Example with APScheduler
|
|
637
|
+
scheduler.add_job(
|
|
638
|
+
lambda: asyncio.run(store.cleanup_expired_idempotency(uuid4())),
|
|
639
|
+
'interval',
|
|
640
|
+
hours=1,
|
|
641
|
+
)
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
2. **Background Task**: Run in consumer after N messages processed
|
|
645
|
+
```python
|
|
646
|
+
if messages_processed % 1000 == 0:
|
|
647
|
+
await store.cleanup_expired_idempotency(correlation_id)
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
3. **Startup Cleanup**: Run once when consumer starts
|
|
651
|
+
```python
|
|
652
|
+
await store.initialize()
|
|
653
|
+
await store.cleanup_expired_idempotency(uuid4()) # Initial cleanup
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
The default TTL is 24 hours (set in database migration). Cleanup frequency
|
|
657
|
+
should be adjusted based on message volume - high-volume systems may need
|
|
658
|
+
more frequent cleanup.
|
|
659
|
+
|
|
660
|
+
Args:
|
|
661
|
+
correlation_id: Correlation ID for tracing.
|
|
662
|
+
|
|
663
|
+
Returns:
|
|
664
|
+
Count of removed records.
|
|
665
|
+
|
|
666
|
+
Raises:
|
|
667
|
+
SessionStoreNotInitializedError: If store not initialized.
|
|
668
|
+
asyncpg.PostgresError: If database operation fails.
|
|
669
|
+
"""
|
|
670
|
+
pool = self._require_pool()
|
|
671
|
+
|
|
672
|
+
async with pool.acquire() as conn:
|
|
673
|
+
result = await conn.execute(
|
|
674
|
+
"""
|
|
675
|
+
DELETE FROM claude_session_event_idempotency
|
|
676
|
+
WHERE expires_at < NOW() AND domain = $1
|
|
677
|
+
""",
|
|
678
|
+
_IDEMPOTENCY_DOMAIN,
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
count = self._parse_row_count(result)
|
|
682
|
+
|
|
683
|
+
if count > 0:
|
|
684
|
+
logger.info(
|
|
685
|
+
"Cleaned up expired idempotency records",
|
|
686
|
+
extra={
|
|
687
|
+
"deleted_count": count,
|
|
688
|
+
"correlation_id": str(correlation_id),
|
|
689
|
+
},
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
return count
|
|
693
|
+
|
|
694
|
+
# =========================================================================
|
|
695
|
+
# Private Helpers - Snapshot Operations
|
|
696
|
+
# =========================================================================
|
|
697
|
+
|
|
698
|
+
# ONEX_EXCLUDE: any_type - dict[str, Any] required for JSON-like snapshot data
|
|
699
|
+
async def _upsert_snapshot(
|
|
700
|
+
self,
|
|
701
|
+
conn: Connection,
|
|
702
|
+
snapshot: dict[str, Any],
|
|
703
|
+
) -> UUID:
|
|
704
|
+
"""Upsert main snapshot record.
|
|
705
|
+
|
|
706
|
+
Args:
|
|
707
|
+
conn: Database connection (within transaction).
|
|
708
|
+
snapshot: Snapshot data.
|
|
709
|
+
|
|
710
|
+
Returns:
|
|
711
|
+
The snapshot_id.
|
|
712
|
+
"""
|
|
713
|
+
# Extract required fields
|
|
714
|
+
session_id = snapshot["session_id"]
|
|
715
|
+
status = snapshot.get("status", "active")
|
|
716
|
+
working_directory = snapshot["working_directory"]
|
|
717
|
+
hook_source = snapshot["hook_source"]
|
|
718
|
+
last_event_at = _ensure_datetime(snapshot.get("last_event_at")) or datetime.now(
|
|
719
|
+
UTC
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
# Extract optional fields with datetime coercion
|
|
723
|
+
correlation_id = snapshot.get("correlation_id")
|
|
724
|
+
started_at = _ensure_datetime(snapshot.get("started_at"))
|
|
725
|
+
ended_at = _ensure_datetime(snapshot.get("ended_at"))
|
|
726
|
+
duration_seconds = snapshot.get("duration_seconds")
|
|
727
|
+
git_branch = snapshot.get("git_branch")
|
|
728
|
+
end_reason = snapshot.get("end_reason")
|
|
729
|
+
prompt_count = snapshot.get("prompt_count", 0)
|
|
730
|
+
tool_count = snapshot.get("tool_count", 0)
|
|
731
|
+
tools_used_count = snapshot.get("tools_used_count", 0)
|
|
732
|
+
event_count = snapshot.get("event_count", 0)
|
|
733
|
+
schema_version = snapshot.get("schema_version", "1.0.0")
|
|
734
|
+
|
|
735
|
+
row = await conn.fetchrow(
|
|
736
|
+
"""
|
|
737
|
+
INSERT INTO claude_session_snapshots (
|
|
738
|
+
session_id, correlation_id, status, started_at, ended_at,
|
|
739
|
+
duration_seconds, working_directory, git_branch, hook_source,
|
|
740
|
+
end_reason, prompt_count, tool_count, tools_used_count,
|
|
741
|
+
event_count, last_event_at, schema_version
|
|
742
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
|
743
|
+
ON CONFLICT (session_id) DO UPDATE SET
|
|
744
|
+
status = EXCLUDED.status,
|
|
745
|
+
ended_at = COALESCE(EXCLUDED.ended_at, claude_session_snapshots.ended_at),
|
|
746
|
+
duration_seconds = COALESCE(EXCLUDED.duration_seconds, claude_session_snapshots.duration_seconds),
|
|
747
|
+
end_reason = COALESCE(EXCLUDED.end_reason, claude_session_snapshots.end_reason),
|
|
748
|
+
prompt_count = EXCLUDED.prompt_count,
|
|
749
|
+
tool_count = EXCLUDED.tool_count,
|
|
750
|
+
tools_used_count = EXCLUDED.tools_used_count,
|
|
751
|
+
event_count = EXCLUDED.event_count,
|
|
752
|
+
last_event_at = EXCLUDED.last_event_at,
|
|
753
|
+
schema_version = EXCLUDED.schema_version
|
|
754
|
+
RETURNING snapshot_id
|
|
755
|
+
""",
|
|
756
|
+
session_id,
|
|
757
|
+
correlation_id,
|
|
758
|
+
status,
|
|
759
|
+
started_at,
|
|
760
|
+
ended_at,
|
|
761
|
+
duration_seconds,
|
|
762
|
+
working_directory,
|
|
763
|
+
git_branch,
|
|
764
|
+
hook_source,
|
|
765
|
+
end_reason,
|
|
766
|
+
prompt_count,
|
|
767
|
+
tool_count,
|
|
768
|
+
tools_used_count,
|
|
769
|
+
event_count,
|
|
770
|
+
last_event_at,
|
|
771
|
+
schema_version,
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
# row is guaranteed to exist due to RETURNING clause
|
|
775
|
+
snapshot_id: UUID = row["snapshot_id"]
|
|
776
|
+
return snapshot_id
|
|
777
|
+
|
|
778
|
+
# ONEX_EXCLUDE: any_type - list[dict[str, Any]] required for JSON-like data
|
|
779
|
+
async def _sync_prompts_with_context(
|
|
780
|
+
self,
|
|
781
|
+
conn: Connection,
|
|
782
|
+
snapshot_id: UUID,
|
|
783
|
+
prompts: list[dict[str, Any]],
|
|
784
|
+
) -> None:
|
|
785
|
+
"""Sync prompts with error context wrapper.
|
|
786
|
+
|
|
787
|
+
Wraps _sync_prompts to provide context about which table
|
|
788
|
+
operation failed when errors occur in parallel execution.
|
|
789
|
+
|
|
790
|
+
Args:
|
|
791
|
+
conn: Database connection (within transaction).
|
|
792
|
+
snapshot_id: Parent snapshot UUID.
|
|
793
|
+
prompts: List of prompt records.
|
|
794
|
+
|
|
795
|
+
Raises:
|
|
796
|
+
RuntimeError: If sync fails, with context about the prompts table.
|
|
797
|
+
"""
|
|
798
|
+
try:
|
|
799
|
+
await self._sync_prompts(conn, snapshot_id, prompts)
|
|
800
|
+
except Exception as e:
|
|
801
|
+
raise RuntimeError(
|
|
802
|
+
f"Failed to sync prompts for snapshot {snapshot_id}: "
|
|
803
|
+
f"{len(prompts)} prompts"
|
|
804
|
+
) from e
|
|
805
|
+
|
|
806
|
+
# ONEX_EXCLUDE: any_type - list[dict[str, Any]] required for JSON-like data
|
|
807
|
+
async def _sync_tools_with_context(
|
|
808
|
+
self,
|
|
809
|
+
conn: Connection,
|
|
810
|
+
snapshot_id: UUID,
|
|
811
|
+
tools: list[dict[str, Any]],
|
|
812
|
+
) -> None:
|
|
813
|
+
"""Sync tools with error context wrapper.
|
|
814
|
+
|
|
815
|
+
Wraps _sync_tools to provide context about which table
|
|
816
|
+
operation failed when errors occur in parallel execution.
|
|
817
|
+
|
|
818
|
+
Args:
|
|
819
|
+
conn: Database connection (within transaction).
|
|
820
|
+
snapshot_id: Parent snapshot UUID.
|
|
821
|
+
tools: List of tool records.
|
|
822
|
+
|
|
823
|
+
Raises:
|
|
824
|
+
RuntimeError: If sync fails, with context about the tools table.
|
|
825
|
+
"""
|
|
826
|
+
try:
|
|
827
|
+
await self._sync_tools(conn, snapshot_id, tools)
|
|
828
|
+
except Exception as e:
|
|
829
|
+
raise RuntimeError(
|
|
830
|
+
f"Failed to sync tools for snapshot {snapshot_id}: {len(tools)} tools"
|
|
831
|
+
) from e
|
|
832
|
+
|
|
833
|
+
# ONEX_EXCLUDE: any_type - list[dict[str, Any]] required for JSON-like data
|
|
834
|
+
async def _sync_prompts(
|
|
835
|
+
self,
|
|
836
|
+
conn: Connection,
|
|
837
|
+
snapshot_id: UUID,
|
|
838
|
+
prompts: list[dict[str, Any]],
|
|
839
|
+
) -> None:
|
|
840
|
+
"""Sync prompt records for snapshot.
|
|
841
|
+
|
|
842
|
+
Uses INSERT ... ON CONFLICT DO NOTHING for idempotent writes.
|
|
843
|
+
|
|
844
|
+
Args:
|
|
845
|
+
conn: Database connection (within transaction).
|
|
846
|
+
snapshot_id: Parent snapshot UUID.
|
|
847
|
+
prompts: List of prompt records.
|
|
848
|
+
"""
|
|
849
|
+
if not prompts:
|
|
850
|
+
return
|
|
851
|
+
|
|
852
|
+
# Use executemany with ON CONFLICT for efficiency
|
|
853
|
+
await conn.executemany(
|
|
854
|
+
"""
|
|
855
|
+
INSERT INTO claude_session_prompts (
|
|
856
|
+
snapshot_id, prompt_id, emitted_at, prompt_preview,
|
|
857
|
+
prompt_length, detected_intent, causation_id
|
|
858
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
859
|
+
ON CONFLICT (snapshot_id, prompt_id) DO NOTHING
|
|
860
|
+
""",
|
|
861
|
+
[
|
|
862
|
+
(
|
|
863
|
+
snapshot_id,
|
|
864
|
+
_ensure_uuid(p["prompt_id"]),
|
|
865
|
+
_ensure_datetime(p["emitted_at"]),
|
|
866
|
+
p.get("prompt_preview"),
|
|
867
|
+
p["prompt_length"],
|
|
868
|
+
p.get("detected_intent"),
|
|
869
|
+
_ensure_uuid(p.get("causation_id")),
|
|
870
|
+
)
|
|
871
|
+
for p in prompts
|
|
872
|
+
],
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
# ONEX_EXCLUDE: any_type - list[dict[str, Any]] required for JSON-like data
|
|
876
|
+
async def _sync_tools(
|
|
877
|
+
self,
|
|
878
|
+
conn: Connection,
|
|
879
|
+
snapshot_id: UUID,
|
|
880
|
+
tools: list[dict[str, Any]],
|
|
881
|
+
) -> None:
|
|
882
|
+
"""Sync tool records for snapshot.
|
|
883
|
+
|
|
884
|
+
Uses INSERT ... ON CONFLICT DO NOTHING for idempotent writes.
|
|
885
|
+
|
|
886
|
+
Args:
|
|
887
|
+
conn: Database connection (within transaction).
|
|
888
|
+
snapshot_id: Parent snapshot UUID.
|
|
889
|
+
tools: List of tool records.
|
|
890
|
+
"""
|
|
891
|
+
if not tools:
|
|
892
|
+
return
|
|
893
|
+
|
|
894
|
+
# Use executemany with ON CONFLICT for efficiency
|
|
895
|
+
await conn.executemany(
|
|
896
|
+
"""
|
|
897
|
+
INSERT INTO claude_session_tools (
|
|
898
|
+
snapshot_id, tool_execution_id, emitted_at, tool_name,
|
|
899
|
+
success, duration_ms, summary, causation_id
|
|
900
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
901
|
+
ON CONFLICT (snapshot_id, tool_execution_id) DO NOTHING
|
|
902
|
+
""",
|
|
903
|
+
[
|
|
904
|
+
(
|
|
905
|
+
snapshot_id,
|
|
906
|
+
_ensure_uuid(t["tool_execution_id"]),
|
|
907
|
+
_ensure_datetime(t["emitted_at"]),
|
|
908
|
+
t["tool_name"],
|
|
909
|
+
t["success"],
|
|
910
|
+
t["duration_ms"],
|
|
911
|
+
t.get("summary"),
|
|
912
|
+
_ensure_uuid(t.get("causation_id")),
|
|
913
|
+
)
|
|
914
|
+
for t in tools
|
|
915
|
+
],
|
|
916
|
+
)
|
|
917
|
+
|
|
918
|
+
# ONEX_EXCLUDE: any_type - list[dict[str, Any]] required for JSON-like data
|
|
919
|
+
async def _fetch_prompts(
|
|
920
|
+
self,
|
|
921
|
+
conn: Connection,
|
|
922
|
+
snapshot_id: UUID,
|
|
923
|
+
) -> list[dict[str, Any]]:
|
|
924
|
+
"""Fetch prompt records for snapshot.
|
|
925
|
+
|
|
926
|
+
Args:
|
|
927
|
+
conn: Database connection.
|
|
928
|
+
snapshot_id: Parent snapshot UUID.
|
|
929
|
+
|
|
930
|
+
Returns:
|
|
931
|
+
List of prompt dicts.
|
|
932
|
+
"""
|
|
933
|
+
rows = await conn.fetch(
|
|
934
|
+
"""
|
|
935
|
+
SELECT
|
|
936
|
+
id, prompt_id, emitted_at, prompt_preview,
|
|
937
|
+
prompt_length, detected_intent, causation_id
|
|
938
|
+
FROM claude_session_prompts
|
|
939
|
+
WHERE snapshot_id = $1
|
|
940
|
+
ORDER BY emitted_at ASC
|
|
941
|
+
""",
|
|
942
|
+
snapshot_id,
|
|
943
|
+
)
|
|
944
|
+
return [dict(row) for row in rows]
|
|
945
|
+
|
|
946
|
+
# ONEX_EXCLUDE: any_type - list[dict[str, Any]] required for JSON-like data
|
|
947
|
+
async def _fetch_tools(
|
|
948
|
+
self,
|
|
949
|
+
conn: Connection,
|
|
950
|
+
snapshot_id: UUID,
|
|
951
|
+
) -> list[dict[str, Any]]:
|
|
952
|
+
"""Fetch tool records for snapshot.
|
|
953
|
+
|
|
954
|
+
Args:
|
|
955
|
+
conn: Database connection.
|
|
956
|
+
snapshot_id: Parent snapshot UUID.
|
|
957
|
+
|
|
958
|
+
Returns:
|
|
959
|
+
List of tool dicts.
|
|
960
|
+
"""
|
|
961
|
+
rows = await conn.fetch(
|
|
962
|
+
"""
|
|
963
|
+
SELECT
|
|
964
|
+
id, tool_execution_id, emitted_at, tool_name,
|
|
965
|
+
success, duration_ms, summary, causation_id
|
|
966
|
+
FROM claude_session_tools
|
|
967
|
+
WHERE snapshot_id = $1
|
|
968
|
+
ORDER BY emitted_at ASC
|
|
969
|
+
""",
|
|
970
|
+
snapshot_id,
|
|
971
|
+
)
|
|
972
|
+
return [dict(row) for row in rows]
|
|
973
|
+
|
|
974
|
+
def _parse_row_count(self, result: str) -> int:
|
|
975
|
+
"""Parse row count from asyncpg execute result string.
|
|
976
|
+
|
|
977
|
+
asyncpg returns strings like:
|
|
978
|
+
- "INSERT 0 1" -> 1 row inserted
|
|
979
|
+
- "UPDATE 5" -> 5 rows updated
|
|
980
|
+
- "DELETE 3" -> 3 rows deleted
|
|
981
|
+
|
|
982
|
+
Args:
|
|
983
|
+
result: Result string from execute().
|
|
984
|
+
|
|
985
|
+
Returns:
|
|
986
|
+
Number of affected rows.
|
|
987
|
+
"""
|
|
988
|
+
try:
|
|
989
|
+
parts = result.split()
|
|
990
|
+
if len(parts) >= 2:
|
|
991
|
+
return int(parts[-1])
|
|
992
|
+
except (ValueError, IndexError):
|
|
993
|
+
pass
|
|
994
|
+
return 0
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
__all__ = ["SessionSnapshotStore", "SessionStoreNotInitializedError"]
|