omnibase_infra 0.2.1__py3-none-any.whl → 0.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. omnibase_infra/__init__.py +1 -1
  2. omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
  3. omnibase_infra/capabilities/__init__.py +15 -0
  4. omnibase_infra/capabilities/capability_inference_rules.py +211 -0
  5. omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
  6. omnibase_infra/capabilities/intent_type_extractor.py +160 -0
  7. omnibase_infra/cli/commands.py +1 -1
  8. omnibase_infra/configs/widget_mapping.yaml +176 -0
  9. omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +5 -2
  10. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +5 -2
  11. omnibase_infra/enums/__init__.py +6 -0
  12. omnibase_infra/enums/enum_handler_error_type.py +10 -0
  13. omnibase_infra/enums/enum_handler_source_mode.py +72 -0
  14. omnibase_infra/enums/enum_kafka_acks.py +99 -0
  15. omnibase_infra/errors/error_compute_registry.py +4 -1
  16. omnibase_infra/errors/error_event_bus_registry.py +4 -1
  17. omnibase_infra/errors/error_infra.py +3 -1
  18. omnibase_infra/errors/error_policy_registry.py +4 -1
  19. omnibase_infra/event_bus/event_bus_kafka.py +1 -1
  20. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +59 -10
  21. omnibase_infra/handlers/__init__.py +8 -1
  22. omnibase_infra/handlers/handler_consul.py +7 -1
  23. omnibase_infra/handlers/handler_db.py +10 -3
  24. omnibase_infra/handlers/handler_graph.py +10 -5
  25. omnibase_infra/handlers/handler_http.py +8 -2
  26. omnibase_infra/handlers/handler_intent.py +387 -0
  27. omnibase_infra/handlers/handler_mcp.py +745 -63
  28. omnibase_infra/handlers/handler_vault.py +11 -5
  29. omnibase_infra/handlers/mixins/mixin_consul_kv.py +4 -3
  30. omnibase_infra/handlers/mixins/mixin_consul_service.py +2 -1
  31. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +7 -0
  32. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +308 -4
  33. omnibase_infra/handlers/service_discovery/models/model_service_info.py +10 -0
  34. omnibase_infra/mixins/mixin_async_circuit_breaker.py +3 -2
  35. omnibase_infra/mixins/mixin_node_introspection.py +42 -7
  36. omnibase_infra/mixins/mixin_retry_execution.py +1 -1
  37. omnibase_infra/models/discovery/model_introspection_config.py +11 -0
  38. omnibase_infra/models/handlers/__init__.py +48 -5
  39. omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
  40. omnibase_infra/models/handlers/model_contract_discovery_result.py +6 -4
  41. omnibase_infra/models/handlers/model_handler_descriptor.py +15 -0
  42. omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
  43. omnibase_infra/models/mcp/__init__.py +15 -0
  44. omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
  45. omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
  46. omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
  47. omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
  48. omnibase_infra/models/registration/model_node_capabilities.py +11 -0
  49. omnibase_infra/models/registration/model_node_introspection_event.py +9 -0
  50. omnibase_infra/models/runtime/model_handler_contract.py +25 -9
  51. omnibase_infra/models/runtime/model_loaded_handler.py +9 -0
  52. omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +0 -5
  53. omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +17 -10
  54. omnibase_infra/nodes/effects/contract.yaml +0 -5
  55. omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +7 -0
  56. omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +86 -1
  57. omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +3 -3
  58. omnibase_infra/nodes/node_registration_orchestrator/plugin.py +1 -1
  59. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +9 -8
  60. omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +4 -3
  61. omnibase_infra/nodes/node_registration_orchestrator/wiring.py +14 -13
  62. omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +0 -5
  63. omnibase_infra/nodes/node_registration_storage_effect/node.py +4 -1
  64. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +47 -26
  65. omnibase_infra/nodes/node_registry_effect/contract.yaml +0 -5
  66. omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +2 -1
  67. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +28 -20
  68. omnibase_infra/plugins/examples/plugin_json_normalizer.py +2 -2
  69. omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +2 -2
  70. omnibase_infra/plugins/plugin_compute_base.py +16 -2
  71. omnibase_infra/protocols/__init__.py +2 -0
  72. omnibase_infra/protocols/protocol_container_aware.py +200 -0
  73. omnibase_infra/protocols/protocol_event_projector.py +1 -1
  74. omnibase_infra/runtime/__init__.py +90 -1
  75. omnibase_infra/runtime/binding_config_resolver.py +102 -37
  76. omnibase_infra/runtime/constants_notification.py +75 -0
  77. omnibase_infra/runtime/contract_handler_discovery.py +6 -1
  78. omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
  79. omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
  80. omnibase_infra/runtime/handler_contract_source.py +267 -186
  81. omnibase_infra/runtime/handler_identity.py +81 -0
  82. omnibase_infra/runtime/handler_plugin_loader.py +19 -2
  83. omnibase_infra/runtime/handler_registry.py +11 -3
  84. omnibase_infra/runtime/handler_source_resolver.py +326 -0
  85. omnibase_infra/runtime/mixin_semver_cache.py +25 -1
  86. omnibase_infra/runtime/mixins/__init__.py +7 -0
  87. omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
  88. omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +31 -10
  89. omnibase_infra/runtime/models/__init__.py +24 -0
  90. omnibase_infra/runtime/models/model_health_check_result.py +2 -1
  91. omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
  92. omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
  93. omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
  94. omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
  95. omnibase_infra/runtime/projector_plugin_loader.py +1 -1
  96. omnibase_infra/runtime/projector_shell.py +229 -1
  97. omnibase_infra/runtime/protocol_lifecycle_executor.py +6 -6
  98. omnibase_infra/runtime/protocols/__init__.py +10 -0
  99. omnibase_infra/runtime/registry/registry_protocol_binding.py +16 -15
  100. omnibase_infra/runtime/registry_contract_source.py +693 -0
  101. omnibase_infra/runtime/registry_policy.py +9 -326
  102. omnibase_infra/runtime/secret_resolver.py +4 -2
  103. omnibase_infra/runtime/service_kernel.py +11 -3
  104. omnibase_infra/runtime/service_message_dispatch_engine.py +4 -2
  105. omnibase_infra/runtime/service_runtime_host_process.py +589 -106
  106. omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
  107. omnibase_infra/runtime/transition_notification_publisher.py +764 -0
  108. omnibase_infra/runtime/util_container_wiring.py +6 -5
  109. omnibase_infra/runtime/util_wiring.py +17 -4
  110. omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
  111. omnibase_infra/services/__init__.py +21 -0
  112. omnibase_infra/services/corpus_capture.py +7 -1
  113. omnibase_infra/services/mcp/__init__.py +31 -0
  114. omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
  115. omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
  116. omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
  117. omnibase_infra/services/mcp/service_mcp_tool_sync.py +547 -0
  118. omnibase_infra/services/registry_api/__init__.py +40 -0
  119. omnibase_infra/services/registry_api/main.py +261 -0
  120. omnibase_infra/services/registry_api/models/__init__.py +66 -0
  121. omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
  122. omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
  123. omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
  124. omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
  125. omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
  126. omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
  127. omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
  128. omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
  129. omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
  130. omnibase_infra/services/registry_api/models/model_warning.py +49 -0
  131. omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
  132. omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
  133. omnibase_infra/services/registry_api/routes.py +371 -0
  134. omnibase_infra/services/registry_api/service.py +837 -0
  135. omnibase_infra/services/service_capability_query.py +4 -4
  136. omnibase_infra/services/service_health.py +3 -2
  137. omnibase_infra/services/service_timeout_emitter.py +20 -3
  138. omnibase_infra/services/service_timeout_scanner.py +7 -3
  139. omnibase_infra/services/session/__init__.py +56 -0
  140. omnibase_infra/services/session/config_consumer.py +120 -0
  141. omnibase_infra/services/session/config_store.py +139 -0
  142. omnibase_infra/services/session/consumer.py +1007 -0
  143. omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
  144. omnibase_infra/services/session/store.py +997 -0
  145. omnibase_infra/utils/__init__.py +19 -0
  146. omnibase_infra/utils/util_atomic_file.py +261 -0
  147. omnibase_infra/utils/util_db_transaction.py +239 -0
  148. omnibase_infra/utils/util_dsn_validation.py +1 -1
  149. omnibase_infra/utils/util_retry_optimistic.py +281 -0
  150. omnibase_infra/validation/__init__.py +3 -19
  151. omnibase_infra/validation/contracts/security.validation.yaml +114 -0
  152. omnibase_infra/validation/infra_validators.py +35 -24
  153. omnibase_infra/validation/validation_exemptions.yaml +140 -9
  154. omnibase_infra/validation/validator_chain_propagation.py +2 -2
  155. omnibase_infra/validation/validator_runtime_shape.py +1 -1
  156. omnibase_infra/validation/validator_security.py +473 -370
  157. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/METADATA +3 -3
  158. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/RECORD +161 -98
  159. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/WHEEL +0 -0
  160. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/entry_points.txt +0 -0
  161. {omnibase_infra-0.2.1.dist-info → omnibase_infra-0.2.3.dist-info}/licenses/LICENSE +0 -0
@@ -11,11 +11,6 @@
11
11
  name: "architecture_validator"
12
12
  contract_name: "architecture_validator"
13
13
  node_name: "architecture_validator"
14
- # Version (semantic versioning)
15
- version:
16
- major: 1
17
- minor: 0
18
- patch: 0
19
14
  contract_version:
20
15
  major: 1
21
16
  minor: 0
@@ -39,7 +39,7 @@ class RegistryInfraArchitectureValidator:
39
39
 
40
40
  This registry provides a static method to register the NodeArchitectureValidator
41
41
  with the ONEX dependency injection container. The validator is registered as
42
- a factory, allowing lazy instantiation when first resolved.
42
+ an instance after being created with the container reference.
43
43
 
44
44
  Thread Safety:
45
45
  Registration is typically done at startup before the container is frozen.
@@ -53,17 +53,17 @@ class RegistryInfraArchitectureValidator:
53
53
  )
54
54
 
55
55
  container = ModelONEXContainer()
56
- RegistryInfraArchitectureValidator.register(container)
56
+ await RegistryInfraArchitectureValidator.register(container)
57
57
  ```
58
58
  """
59
59
 
60
60
  @staticmethod
61
- def register(container: ModelONEXContainer) -> None:
61
+ async def register(container: ModelONEXContainer) -> None:
62
62
  """Register architecture validator with container.
63
63
 
64
64
  Registers the NodeArchitectureValidator as a service in the ONEX
65
- dependency injection container. The validator is registered with
66
- a factory function that creates a new instance on demand.
65
+ dependency injection container. The validator is created immediately
66
+ and registered as a singleton instance.
67
67
 
68
68
  Args:
69
69
  container: The DI container to register with.
@@ -77,22 +77,29 @@ class RegistryInfraArchitectureValidator:
77
77
  Example:
78
78
  ```python
79
79
  container = ModelONEXContainer()
80
- RegistryInfraArchitectureValidator.register(container)
80
+ await RegistryInfraArchitectureValidator.register(container)
81
81
 
82
82
  # Resolve when needed
83
- validator = container.get_service(NodeArchitectureValidator)
83
+ validator = await container.service_registry.resolve_service(
84
+ NodeArchitectureValidator
85
+ )
84
86
  result = await validator.compute(request)
85
87
  ```
86
88
  """
89
+ from omnibase_core.enums import EnumInjectionScope
90
+
87
91
  # Check if service_registry is available
88
92
  if container.service_registry is None:
89
93
  # Container doesn't have full DI support - skip registration
90
94
  # This allows the code to work with minimal container configurations
91
95
  return
92
96
 
93
- container.service_registry.register(
94
- NodeArchitectureValidator,
95
- lambda c: NodeArchitectureValidator(c),
97
+ # Create instance and register (factory registration not supported in v1.0)
98
+ validator_instance = NodeArchitectureValidator(container)
99
+ await container.service_registry.register_instance(
100
+ interface=NodeArchitectureValidator,
101
+ instance=validator_instance,
102
+ scope=EnumInjectionScope.GLOBAL,
96
103
  )
97
104
 
98
105
 
@@ -10,11 +10,6 @@
10
10
  name: "registry_effect"
11
11
  contract_name: "registry_effect"
12
12
  node_name: "registry_effect"
13
- # Version (semantic versioning)
14
- version:
15
- major: 1
16
- minor: 0
17
- patch: 0
18
13
  contract_version:
19
14
  major: 1
20
15
  minor: 0
@@ -36,6 +36,13 @@ node_version: "1.0.0"
36
36
  name: "node_registration_orchestrator"
37
37
  node_type: "ORCHESTRATOR_GENERIC"
38
38
  description: "Registration workflow orchestrator that coordinates node lifecycle by calling reducer for intents and effect for execution."
39
+ # MCP (Model Context Protocol) Configuration
40
+ # Exposes this orchestrator as an AI-invocable tool via MCP server
41
+ mcp:
42
+ expose: true
43
+ tool_name: "register_node"
44
+ description: "Register a new ONEX node with the cluster. Handles Consul service registration and PostgreSQL metadata storage. Returns registration status and assigned service ID."
45
+ timeout_seconds: 30
39
46
  input_model:
40
47
  name: "ModelOrchestratorInput"
41
48
  module: "omnibase_infra.nodes.node_registration_orchestrator.models"
@@ -53,6 +53,7 @@ Related Tickets:
53
53
  from __future__ import annotations
54
54
 
55
55
  import logging
56
+ import re
56
57
  import time
57
58
  from datetime import datetime, timedelta
58
59
  from typing import TYPE_CHECKING
@@ -237,6 +238,39 @@ class HandlerNodeIntrospected:
237
238
  """Check if HandlerConsul is configured for Consul registration."""
238
239
  return self._consul_handler is not None
239
240
 
241
+ def _sanitize_tool_name(self, name: str) -> str:
242
+ """Sanitize tool name for use in Consul tags.
243
+
244
+ Converts free-form text (like descriptions) into stable, Consul-safe
245
+ identifiers. This ensures consistent service discovery matching.
246
+
247
+ Transformation rules:
248
+ 1. Convert to lowercase
249
+ 2. Replace non-alphanumeric characters with dashes
250
+ 3. Collapse multiple consecutive dashes into one
251
+ 4. Remove leading/trailing dashes
252
+ 5. Truncate to 63 characters (Consul tag limit)
253
+
254
+ Args:
255
+ name: Raw tool name or description text.
256
+
257
+ Returns:
258
+ Sanitized string suitable for Consul tags (lowercase, alphanumeric
259
+ with dashes, max 63 chars).
260
+
261
+ Example:
262
+ >>> handler._sanitize_tool_name("My Cool Tool (v2.0)")
263
+ 'my-cool-tool-v2-0'
264
+ >>> handler._sanitize_tool_name(" Spaces & Special!Chars ")
265
+ 'spaces-special-chars'
266
+ """
267
+ # Replace non-alphanumeric with dash, lowercase
268
+ sanitized = re.sub(r"[^a-zA-Z0-9]+", "-", name.lower())
269
+ # Remove leading/trailing dashes
270
+ sanitized = sanitized.strip("-")
271
+ # Truncate to Consul tag limit (63 chars is common limit for DNS labels)
272
+ return sanitized[:63]
273
+
240
274
  async def handle(
241
275
  self,
242
276
  envelope: ModelEventEnvelope[ModelNodeIntrospectionEvent],
@@ -422,11 +456,19 @@ class HandlerNodeIntrospected:
422
456
 
423
457
  # Register with Consul for service discovery (dual registration)
424
458
  # This happens AFTER PostgreSQL persistence (source of truth)
459
+ # Pass MCP config from capabilities (if present) for MCP tag generation
460
+ mcp_config = (
461
+ event.declared_capabilities.mcp
462
+ if event.declared_capabilities is not None
463
+ else None
464
+ )
425
465
  await self._register_with_consul(
426
466
  node_id=node_id,
427
467
  node_type=event.node_type.value,
428
468
  endpoints=event.endpoints,
429
469
  correlation_id=correlation_id,
470
+ mcp_config=mcp_config,
471
+ node_name=event.metadata.description if event.metadata else None,
430
472
  )
431
473
 
432
474
  logger.info(
@@ -458,6 +500,8 @@ class HandlerNodeIntrospected:
458
500
  node_type: str,
459
501
  endpoints: dict[str, str] | None,
460
502
  correlation_id: UUID,
503
+ mcp_config: object | None = None,
504
+ node_name: str | None = None,
461
505
  ) -> None:
462
506
  """Register node with Consul for service discovery.
463
507
 
@@ -465,8 +509,16 @@ class HandlerNodeIntrospected:
465
509
  - service_name: `onex-{node_type}` (ONEX convention for service discovery)
466
510
  - service_id: `onex-{node_type}-{node_id}` (unique identifier)
467
511
  - tags: [`onex`, `node-type:{node_type}`]
512
+ - MCP tags (orchestrators only): [`mcp-enabled`, `mcp-tool:{tool_name}`]
468
513
  - address/port: Extracted from endpoints if available
469
514
 
515
+ MCP Tags:
516
+ MCP tags are added ONLY when:
517
+ 1. node_type is "orchestrator"
518
+ 2. mcp_config is provided with expose=True
519
+
520
+ This ensures only orchestrators can be exposed as MCP tools.
521
+
470
522
  This method is idempotent - re-registering the same service_id updates it.
471
523
  Errors are logged but not propagated (PostgreSQL is source of truth).
472
524
 
@@ -475,6 +527,8 @@ class HandlerNodeIntrospected:
475
527
  node_type: ONEX node type (effect, compute, reducer, orchestrator).
476
528
  endpoints: Optional dict of endpoint URLs from introspection event.
477
529
  correlation_id: Correlation ID for tracing.
530
+ mcp_config: Optional MCP configuration from capabilities.
531
+ node_name: Optional node name for MCP tool naming.
478
532
  """
479
533
  if self._consul_handler is None:
480
534
  logger.debug(
@@ -554,11 +608,42 @@ class HandlerNodeIntrospected:
554
608
  },
555
609
  )
556
610
 
611
+ # Build base tags
612
+ tags: list[str] = ["onex", f"node-type:{node_type}"]
613
+
614
+ # Add MCP tags for orchestrators with MCP config enabled
615
+ # MCP tags are ONLY added when:
616
+ # 1. node_type is "orchestrator" (enforces orchestrator-only rule)
617
+ # 2. mcp_config exists with expose=True
618
+ if node_type == "orchestrator" and mcp_config is not None:
619
+ # Check if mcp_config has expose attribute and it's True
620
+ mcp_expose = getattr(mcp_config, "expose", False)
621
+ if mcp_expose:
622
+ # Get tool name from mcp_config or fall back to node_name
623
+ mcp_tool_name_raw = getattr(mcp_config, "tool_name", None)
624
+ if not mcp_tool_name_raw:
625
+ # Fall back to node_name (description), then service_name
626
+ mcp_tool_name_raw = node_name or service_name
627
+ # Sanitize tool name for Consul tag safety
628
+ # node_name comes from metadata.description which can be free-form text
629
+ mcp_tool_name = self._sanitize_tool_name(mcp_tool_name_raw)
630
+ tags.extend(["mcp-enabled", f"mcp-tool:{mcp_tool_name}"])
631
+
632
+ logger.info(
633
+ "Adding MCP tags to Consul registration",
634
+ extra={
635
+ "node_id": str(node_id),
636
+ "tool_name": mcp_tool_name,
637
+ "tool_name_raw": mcp_tool_name_raw,
638
+ "correlation_id": str(correlation_id),
639
+ },
640
+ )
641
+
557
642
  # Build Consul registration payload
558
643
  consul_payload: dict[str, object] = {
559
644
  "name": service_name,
560
645
  "service_id": service_id,
561
- "tags": ["onex", f"node-type:{node_type}"],
646
+ "tags": tags,
562
647
  }
563
648
  if address:
564
649
  consul_payload["address"] = address
@@ -392,7 +392,7 @@ class IntrospectionEventRouter:
392
392
  correlation_id=raw_envelope.correlation_id or callback_correlation_id,
393
393
  source_tool=raw_envelope.source_tool,
394
394
  target_tool=raw_envelope.target_tool,
395
- metadata=_normalize_metadata(raw_envelope.metadata),
395
+ metadata=_normalize_metadata(raw_envelope.metadata), # type: ignore[arg-type]
396
396
  priority=raw_envelope.priority,
397
397
  timeout_seconds=raw_envelope.timeout_seconds,
398
398
  trace_id=raw_envelope.trace_id,
@@ -419,7 +419,7 @@ class IntrospectionEventRouter:
419
419
  },
420
420
  )
421
421
  dispatcher_start_time = time.time()
422
- result = await self._dispatcher.handle(event_envelope)
422
+ result = await self._dispatcher.handle(event_envelope) # type: ignore[arg-type]
423
423
  dispatcher_duration = time.time() - dispatcher_start_time
424
424
 
425
425
  if result.is_successful():
@@ -441,7 +441,7 @@ class IntrospectionEventRouter:
441
441
  if result.output_events:
442
442
  for output_event in result.output_events:
443
443
  # Wrap output event in envelope
444
- output_envelope = ModelEventEnvelope(
444
+ output_envelope = ModelEventEnvelope( # type: ignore[var-annotated]
445
445
  payload=output_event,
446
446
  correlation_id=event_envelope.correlation_id,
447
447
  envelope_timestamp=datetime.now(UTC),
@@ -515,7 +515,7 @@ class PluginRegistration:
515
515
  # Deferred import: Only load HandlerConsul when Consul is configured
516
516
  from omnibase_infra.handlers import HandlerConsul
517
517
 
518
- self._consul_handler = HandlerConsul()
518
+ self._consul_handler = HandlerConsul(config.container)
519
519
  await self._consul_handler.initialize(
520
520
  {"host": consul_host, "port": consul_port}
521
521
  )
@@ -13,7 +13,7 @@ Handler Wiring (from contract.yaml):
13
13
  - ModelNodeHeartbeatEvent -> HandlerNodeHeartbeat
14
14
 
15
15
  Handler Implementation:
16
- All handlers implement ProtocolMessageHandler directly with:
16
+ All handlers implement ProtocolContainerAware directly with:
17
17
  - handler_id, category, message_types, node_kind properties
18
18
  - handle(envelope) -> ModelHandlerOutput signature
19
19
 
@@ -88,11 +88,12 @@ from __future__ import annotations
88
88
  import importlib
89
89
  import logging
90
90
  from pathlib import Path
91
- from typing import TYPE_CHECKING
91
+ from typing import TYPE_CHECKING, cast
92
92
 
93
93
  from omnibase_core.services.service_handler_registry import ServiceHandlerRegistry
94
94
  from omnibase_infra.enums import EnumInfraTransportType
95
95
  from omnibase_infra.errors import ModelInfraErrorContext, ProtocolConfigurationError
96
+ from omnibase_infra.protocols import ProtocolContainerAware
96
97
  from omnibase_infra.runtime.contract_loaders import (
97
98
  load_handler_class_info_from_contract,
98
99
  )
@@ -110,14 +111,14 @@ ALLOWED_NAMESPACES: tuple[str, ...] = (
110
111
 
111
112
 
112
113
  def _validate_handler_protocol(handler: object) -> tuple[bool, list[str]]:
113
- """Validate handler implements ProtocolMessageHandler via duck typing.
114
+ """Validate handler implements ProtocolContainerAware via duck typing.
114
115
 
115
116
  Uses duck typing to verify the handler has the required properties and
116
- methods for ProtocolMessageHandler compliance. Per ONEX conventions,
117
+ methods for ProtocolContainerAware compliance. Per ONEX conventions,
117
118
  protocol compliance is verified via structural typing rather than
118
119
  isinstance checks.
119
120
 
120
- Protocol Requirements (ProtocolMessageHandler):
121
+ Protocol Requirements (ProtocolContainerAware):
121
122
  - handler_id (property): Unique identifier string
122
123
  - category (property): EnumMessageCategory value
123
124
  - message_types (property): set[str] of message type names
@@ -492,7 +493,7 @@ class RegistryInfraNodeRegistrationOrchestrator:
492
493
  context=ctx,
493
494
  ) from e
494
495
 
495
- # Validate handler implements ProtocolMessageHandler
496
+ # Validate handler implements ProtocolContainerAware
496
497
  is_valid, missing = _validate_handler_protocol(handler_instance)
497
498
  if not is_valid:
498
499
  ctx = ModelInfraErrorContext.with_correlation(
@@ -501,7 +502,7 @@ class RegistryInfraNodeRegistrationOrchestrator:
501
502
  target_name=handler_class_name,
502
503
  )
503
504
  raise ProtocolConfigurationError(
504
- f"Handler '{handler_class_name}' does not implement ProtocolMessageHandler. "
505
+ f"Handler '{handler_class_name}' does not implement ProtocolContainerAware. "
505
506
  f"Missing required members: {', '.join(missing)}. "
506
507
  f"Handlers must have: handler_id, category, message_types, node_kind properties "
507
508
  f"and handle(envelope) method. "
@@ -510,7 +511,7 @@ class RegistryInfraNodeRegistrationOrchestrator:
510
511
  )
511
512
 
512
513
  # Register handler
513
- registry.register_handler(handler_instance)
514
+ registry.register_handler(handler_instance) # type: ignore[arg-type]
514
515
  logger.debug(
515
516
  "Registered handler from contract: %s",
516
517
  handler_class_name,
@@ -203,8 +203,9 @@ class TimeoutCoordinator:
203
203
 
204
204
  Usage in orchestrator:
205
205
  >>> # Wire dependencies
206
- >>> timeout_query = ServiceTimeoutScanner(projection_reader)
206
+ >>> timeout_query = ServiceTimeoutScanner(container, projection_reader)
207
207
  >>> timeout_emission = ServiceTimeoutEmitter(
208
+ ... container=container,
208
209
  ... timeout_query=timeout_query,
209
210
  ... event_bus=event_bus,
210
211
  ... projector=projector,
@@ -235,8 +236,8 @@ class TimeoutCoordinator:
235
236
 
236
237
  Example:
237
238
  >>> reader = ProjectionReaderRegistration(pool)
238
- >>> query = ServiceTimeoutScanner(reader)
239
- >>> emission = ServiceTimeoutEmitter(query, event_bus, projector)
239
+ >>> query = ServiceTimeoutScanner(container, reader)
240
+ >>> emission = ServiceTimeoutEmitter(container, query, event_bus, projector)
240
241
  >>> coordinator = TimeoutCoordinator(query, emission)
241
242
  """
242
243
  self._timeout_query = timeout_query
@@ -44,6 +44,7 @@ import logging
44
44
  from typing import TYPE_CHECKING, TypedDict, cast
45
45
  from uuid import UUID
46
46
 
47
+ from omnibase_core.enums import EnumInjectionScope
47
48
  from omnibase_infra.enums import EnumInfraTransportType
48
49
  from omnibase_infra.errors import (
49
50
  ContainerValidationError,
@@ -269,12 +270,14 @@ async def wire_registration_dispatchers(
269
270
  # because MessageDispatchEngine.register_dispatcher() takes a callable
270
271
 
271
272
  # 3a. Register DispatcherNodeIntrospected
273
+ # Note: node_kind is NOT passed to register_dispatcher because the dispatcher's
274
+ # handle() method doesn't accept ModelDispatchContext - it handles time injection
275
+ # internally. The node_kind property is informational only.
272
276
  engine.register_dispatcher(
273
277
  dispatcher_id=dispatcher_introspected.dispatcher_id,
274
278
  dispatcher=dispatcher_introspected.handle,
275
279
  category=dispatcher_introspected.category,
276
280
  message_types=dispatcher_introspected.message_types,
277
- node_kind=dispatcher_introspected.node_kind,
278
281
  )
279
282
  dispatchers_registered.append(dispatcher_introspected.dispatcher_id)
280
283
 
@@ -284,7 +287,6 @@ async def wire_registration_dispatchers(
284
287
  dispatcher=dispatcher_runtime_tick.handle,
285
288
  category=dispatcher_runtime_tick.category,
286
289
  message_types=dispatcher_runtime_tick.message_types,
287
- node_kind=dispatcher_runtime_tick.node_kind,
288
290
  )
289
291
  dispatchers_registered.append(dispatcher_runtime_tick.dispatcher_id)
290
292
 
@@ -294,7 +296,6 @@ async def wire_registration_dispatchers(
294
296
  dispatcher=dispatcher_acked.handle,
295
297
  category=dispatcher_acked.category,
296
298
  message_types=dispatcher_acked.message_types,
297
- node_kind=dispatcher_acked.node_kind,
298
299
  )
299
300
  dispatchers_registered.append(dispatcher_acked.dispatcher_id)
300
301
 
@@ -409,7 +410,7 @@ async def wire_registration_handlers(
409
410
  ContainerWiringError: If service registration fails.
410
411
 
411
412
  Note:
412
- Services are registered with scope="global" and may conflict if multiple
413
+ Services are registered with scope=EnumInjectionScope.GLOBAL and may conflict if multiple
413
414
  plugins register the same interface type. This is acceptable for the
414
415
  Registration domain as these handlers are singletons by design. If you
415
416
  need to register multiple implementations of the same interface, use
@@ -440,7 +441,7 @@ async def wire_registration_handlers(
440
441
  await container.service_registry.register_instance(
441
442
  interface=ProjectionReaderRegistration,
442
443
  instance=projection_reader,
443
- scope="global",
444
+ scope=EnumInjectionScope.GLOBAL,
444
445
  metadata={
445
446
  "description": "Registration projection reader",
446
447
  "version": str(semver_default),
@@ -453,7 +454,7 @@ async def wire_registration_handlers(
453
454
  await container.service_registry.register_instance(
454
455
  interface=ProjectorShell,
455
456
  instance=projector,
456
- scope="global",
457
+ scope=EnumInjectionScope.GLOBAL,
457
458
  metadata={
458
459
  "description": "Registration projector",
459
460
  "version": str(semver_default),
@@ -470,7 +471,7 @@ async def wire_registration_handlers(
470
471
  await container.service_registry.register_instance(
471
472
  interface=HandlerNodeIntrospected,
472
473
  instance=handler_introspected,
473
- scope="global",
474
+ scope=EnumInjectionScope.GLOBAL,
474
475
  metadata={
475
476
  "description": "Handler for NodeIntrospectionEvent",
476
477
  "version": str(semver_default),
@@ -485,7 +486,7 @@ async def wire_registration_handlers(
485
486
  await container.service_registry.register_instance(
486
487
  interface=HandlerRuntimeTick,
487
488
  instance=handler_runtime_tick,
488
- scope="global",
489
+ scope=EnumInjectionScope.GLOBAL,
489
490
  metadata={
490
491
  "description": "Handler for RuntimeTick",
491
492
  "version": str(semver_default),
@@ -501,7 +502,7 @@ async def wire_registration_handlers(
501
502
  await container.service_registry.register_instance(
502
503
  interface=HandlerNodeRegistrationAcked,
503
504
  instance=handler_acked,
504
- scope="global",
505
+ scope=EnumInjectionScope.GLOBAL,
505
506
  metadata={
506
507
  "description": "Handler for NodeRegistrationAcked",
507
508
  "version": str(semver_default),
@@ -579,7 +580,7 @@ async def get_projection_reader_from_container(
579
580
  _validate_service_registry(container, "resolve ProjectionReaderRegistration")
580
581
  try:
581
582
  return cast(
582
- ProjectionReaderRegistration,
583
+ "ProjectionReaderRegistration",
583
584
  await container.service_registry.resolve_service(
584
585
  ProjectionReaderRegistration
585
586
  ),
@@ -622,7 +623,7 @@ async def get_handler_node_introspected_from_container(
622
623
  _validate_service_registry(container, "resolve HandlerNodeIntrospected")
623
624
  try:
624
625
  return cast(
625
- HandlerNodeIntrospected,
626
+ "HandlerNodeIntrospected",
626
627
  await container.service_registry.resolve_service(HandlerNodeIntrospected),
627
628
  )
628
629
  except Exception as e:
@@ -663,7 +664,7 @@ async def get_handler_runtime_tick_from_container(
663
664
  _validate_service_registry(container, "resolve HandlerRuntimeTick")
664
665
  try:
665
666
  return cast(
666
- HandlerRuntimeTick,
667
+ "HandlerRuntimeTick",
667
668
  await container.service_registry.resolve_service(HandlerRuntimeTick),
668
669
  )
669
670
  except Exception as e:
@@ -704,7 +705,7 @@ async def get_handler_node_registration_acked_from_container(
704
705
  _validate_service_registry(container, "resolve HandlerNodeRegistrationAcked")
705
706
  try:
706
707
  return cast(
707
- HandlerNodeRegistrationAcked,
708
+ "HandlerNodeRegistrationAcked",
708
709
  await container.service_registry.resolve_service(
709
710
  HandlerNodeRegistrationAcked
710
711
  ),
@@ -15,11 +15,6 @@
15
15
  name: "node_registration_storage_effect"
16
16
  contract_name: "node_registration_storage_effect"
17
17
  node_name: "node_registration_storage_effect"
18
- # Version (semantic versioning)
19
- version:
20
- major: 1
21
- minor: 0
22
- patch: 0
23
18
  contract_version:
24
19
  major: 1
25
20
  minor: 0
@@ -44,7 +44,10 @@ Pluggable Backends:
44
44
 
45
45
  # Create container and register handler
46
46
  container = ModelONEXContainer()
47
- handler = HandlerRegistrationStoragePostgres(dsn="postgresql://...")
47
+ handler = HandlerRegistrationStoragePostgres(
48
+ container=container,
49
+ dsn="postgresql://...",
50
+ )
48
51
  RegistryInfraRegistrationStorage.register(container)
49
52
  RegistryInfraRegistrationStorage.register_handler(container, handler)
50
53