omnibase_infra 0.2.2__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 (77) hide show
  1. omnibase_infra/__init__.py +1 -1
  2. omnibase_infra/adapters/adapter_onex_tool_execution.py +6 -1
  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/contracts/handlers/filesystem/handler_contract.yaml +1 -1
  8. omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +1 -1
  9. omnibase_infra/enums/__init__.py +6 -0
  10. omnibase_infra/enums/enum_handler_error_type.py +10 -0
  11. omnibase_infra/enums/enum_handler_source_mode.py +72 -0
  12. omnibase_infra/enums/enum_kafka_acks.py +99 -0
  13. omnibase_infra/event_bus/event_bus_kafka.py +1 -1
  14. omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +59 -10
  15. omnibase_infra/handlers/__init__.py +8 -1
  16. omnibase_infra/handlers/handler_consul.py +7 -1
  17. omnibase_infra/handlers/handler_db.py +8 -2
  18. omnibase_infra/handlers/handler_http.py +8 -2
  19. omnibase_infra/handlers/handler_intent.py +387 -0
  20. omnibase_infra/handlers/handler_mcp.py +10 -1
  21. omnibase_infra/handlers/handler_vault.py +11 -5
  22. omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +7 -0
  23. omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +7 -0
  24. omnibase_infra/mixins/mixin_node_introspection.py +18 -0
  25. omnibase_infra/models/discovery/model_introspection_config.py +11 -0
  26. omnibase_infra/models/handlers/__init__.py +38 -5
  27. omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +4 -4
  28. omnibase_infra/models/handlers/model_contract_discovery_result.py +6 -4
  29. omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
  30. omnibase_infra/models/registration/model_node_introspection_event.py +9 -0
  31. omnibase_infra/models/runtime/model_handler_contract.py +25 -9
  32. omnibase_infra/models/runtime/model_loaded_handler.py +9 -0
  33. omnibase_infra/nodes/node_registration_orchestrator/plugin.py +1 -1
  34. omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +7 -7
  35. omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +4 -3
  36. omnibase_infra/nodes/node_registration_storage_effect/node.py +4 -1
  37. omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +1 -1
  38. omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +4 -1
  39. omnibase_infra/protocols/__init__.py +2 -0
  40. omnibase_infra/protocols/protocol_container_aware.py +200 -0
  41. omnibase_infra/runtime/__init__.py +39 -0
  42. omnibase_infra/runtime/handler_bootstrap_source.py +26 -33
  43. omnibase_infra/runtime/handler_contract_config_loader.py +1 -1
  44. omnibase_infra/runtime/handler_contract_source.py +10 -51
  45. omnibase_infra/runtime/handler_identity.py +81 -0
  46. omnibase_infra/runtime/handler_plugin_loader.py +15 -0
  47. omnibase_infra/runtime/handler_registry.py +11 -3
  48. omnibase_infra/runtime/handler_source_resolver.py +326 -0
  49. omnibase_infra/runtime/protocol_lifecycle_executor.py +6 -6
  50. omnibase_infra/runtime/registry/registry_protocol_binding.py +13 -13
  51. omnibase_infra/runtime/registry_contract_source.py +693 -0
  52. omnibase_infra/runtime/service_kernel.py +1 -1
  53. omnibase_infra/runtime/service_runtime_host_process.py +463 -190
  54. omnibase_infra/runtime/util_wiring.py +12 -3
  55. omnibase_infra/services/__init__.py +21 -0
  56. omnibase_infra/services/corpus_capture.py +7 -1
  57. omnibase_infra/services/mcp/mcp_server_lifecycle.py +9 -3
  58. omnibase_infra/services/registry_api/main.py +31 -13
  59. omnibase_infra/services/registry_api/service.py +10 -19
  60. omnibase_infra/services/service_timeout_emitter.py +7 -1
  61. omnibase_infra/services/service_timeout_scanner.py +7 -3
  62. omnibase_infra/services/session/__init__.py +56 -0
  63. omnibase_infra/services/session/config_consumer.py +120 -0
  64. omnibase_infra/services/session/config_store.py +139 -0
  65. omnibase_infra/services/session/consumer.py +1007 -0
  66. omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
  67. omnibase_infra/services/session/store.py +997 -0
  68. omnibase_infra/utils/__init__.py +19 -0
  69. omnibase_infra/utils/util_atomic_file.py +261 -0
  70. omnibase_infra/utils/util_db_transaction.py +239 -0
  71. omnibase_infra/utils/util_retry_optimistic.py +281 -0
  72. omnibase_infra/validation/validation_exemptions.yaml +27 -0
  73. {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.3.dist-info}/METADATA +3 -3
  74. {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.3.dist-info}/RECORD +77 -56
  75. {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.3.dist-info}/WHEEL +0 -0
  76. {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.3.dist-info}/entry_points.txt +0 -0
  77. {omnibase_infra-0.2.2.dist-info → omnibase_infra-0.2.3.dist-info}/licenses/LICENSE +0 -0
@@ -44,7 +44,7 @@ class ModelBootstrapHandlerDescriptor(ModelHandlerDescriptor):
44
44
  All other fields maintain the same constraints as the parent class.
45
45
 
46
46
  Attributes:
47
- handler_id: Unique identifier for the handler (e.g., "bootstrap.consul").
47
+ handler_id: Unique identifier for the handler (e.g., "proto.consul").
48
48
  name: Human-readable name for the handler.
49
49
  version: Semantic version (ModelSemVer). Accepts string, dict, or ModelSemVer.
50
50
  handler_kind: Handler kind (compute, effect, reducer, orchestrator).
@@ -58,7 +58,7 @@ class ModelBootstrapHandlerDescriptor(ModelHandlerDescriptor):
58
58
  Create a bootstrap handler descriptor:
59
59
 
60
60
  >>> descriptor = ModelBootstrapHandlerDescriptor(
61
- ... handler_id="bootstrap.consul",
61
+ ... handler_id="proto.consul",
62
62
  ... name="Consul Handler",
63
63
  ... version="1.0.0",
64
64
  ... handler_kind="effect",
@@ -74,7 +74,7 @@ class ModelBootstrapHandlerDescriptor(ModelHandlerDescriptor):
74
74
  >>> from pydantic import ValidationError
75
75
  >>> try:
76
76
  ... ModelBootstrapHandlerDescriptor(
77
- ... handler_id="bootstrap.consul",
77
+ ... handler_id="proto.consul",
78
78
  ... name="Consul Handler",
79
79
  ... version="1.0.0",
80
80
  ... handler_kind="effect",
@@ -144,7 +144,7 @@ class ModelBootstrapHandlerDescriptor(ModelHandlerDescriptor):
144
144
 
145
145
  Example:
146
146
  >>> bootstrap_desc = ModelBootstrapHandlerDescriptor(
147
- ... handler_id="bootstrap.consul",
147
+ ... handler_id="proto.consul",
148
148
  ... name="Consul Handler",
149
149
  ... version="1.0.0",
150
150
  ... handler_kind="effect",
@@ -72,9 +72,11 @@ class ModelContractDiscoveryResult(BaseModel):
72
72
  )
73
73
 
74
74
 
75
- # Rebuild model to resolve forward reference after all imports are available.
76
- # model_rebuild() is called in omnibase_infra.runtime.handler_contract_source
77
- # after ModelHandlerValidationError is imported. See the comment block preceding
78
- # model_rebuild() in handler_contract_source.py (lines 52-71) for detailed explanation.
75
+ # Forward Reference Resolution:
76
+ # This model uses TYPE_CHECKING to defer import of ModelHandlerValidationError.
77
+ # model_rebuild() is called in runtime modules that import ModelHandlerValidationError
78
+ # (e.g., handler_contract_source.py, handler_bootstrap_source.py, registry_contract_source.py).
79
+ # Each module calls model_rebuild() at module level after importing both the model
80
+ # and the forward-referenced type. This is safe because model_rebuild() is idempotent.
79
81
 
80
82
  __all__ = ["ModelContractDiscoveryResult"]
@@ -0,0 +1,220 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2025 OmniNode Team
3
+ """Handler Source Configuration Model.
4
+
5
+ This module provides ModelHandlerSourceConfig, a Pydantic model for
6
+ configuring handler source mode selection at runtime.
7
+
8
+ The configuration controls how handlers are discovered and loaded:
9
+ - BOOTSTRAP: Hardcoded handlers from _KNOWN_HANDLERS dict (MVP mode)
10
+ - CONTRACT: YAML contracts from handler_contract.yaml files (production)
11
+ - HYBRID: Contract-first with bootstrap fallback per-handler identity
12
+
13
+ Production hardening features:
14
+ - Bootstrap expiry enforcement: If bootstrap_expires_at is set and now > expires_at,
15
+ the runtime will refuse to start in BOOTSTRAP mode (or force CONTRACT mode)
16
+ - Structured logging of expiry status at startup
17
+ - Override control for hybrid mode handler resolution
18
+
19
+ .. versionadded:: 0.7.0
20
+ Created as part of OMN-1095 handler source mode configuration.
21
+
22
+ See Also:
23
+ - HANDLER_PROTOCOL_DRIVEN_ARCHITECTURE.md: Full architecture documentation
24
+ - EnumHandlerSourceMode: Enum defining valid source modes
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from datetime import UTC, datetime, timezone
30
+
31
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
32
+
33
+ from omnibase_infra.enums.enum_handler_source_mode import EnumHandlerSourceMode
34
+
35
+
36
+ class ModelHandlerSourceConfig(BaseModel):
37
+ """Configuration for handler source mode selection.
38
+
39
+ Controls how handlers are discovered and loaded at runtime. This model
40
+ is used by RuntimeHostProcess and related components to determine the
41
+ handler loading strategy.
42
+
43
+ Configuration Options:
44
+ - handler_source_mode: Selects the loading strategy (BOOTSTRAP, CONTRACT, HYBRID)
45
+ - allow_bootstrap_override: Controls handler resolution in HYBRID mode
46
+ - bootstrap_expires_at: Production safety - forces CONTRACT after expiry
47
+
48
+ Production Hardening:
49
+ When bootstrap_expires_at is set and the current time exceeds it:
50
+ - BOOTSTRAP mode: Runtime refuses to start (safety mechanism)
51
+ - HYBRID mode: Bootstrap fallback disabled, contract-only resolution
52
+ - CONTRACT mode: No effect (already contract-only)
53
+
54
+ This prevents accidental deployment with hardcoded handlers in production.
55
+
56
+ Attributes:
57
+ handler_source_mode: Handler loading source mode.
58
+ - BOOTSTRAP: Load from hardcoded _KNOWN_HANDLERS dict (MVP)
59
+ - CONTRACT: Load from handler_contract.yaml files (production)
60
+ - HYBRID: Contract-first with bootstrap fallback per-handler identity
61
+ Defaults to HYBRID as recommended for gradual migration.
62
+
63
+ allow_bootstrap_override: If True, bootstrap handlers can override
64
+ contract handlers in HYBRID mode. Default is False, meaning
65
+ contract handlers take precedence (inverse of naive HYBRID).
66
+ Has no effect in BOOTSTRAP or CONTRACT modes.
67
+ When parsing from environment or config, string values "true",
68
+ "yes", "1", "on" (case-insensitive) are accepted as truthy.
69
+
70
+ bootstrap_expires_at: If set and expired, refuse BOOTSTRAP mode and
71
+ force CONTRACT. This is a production safety mechanism to ensure
72
+ hardcoded handlers are not accidentally deployed to production
73
+ after a migration deadline. Set to None to disable expiry checking.
74
+
75
+ Example:
76
+ >>> from datetime import datetime, timezone
77
+ >>> from omnibase_infra.models.handlers import ModelHandlerSourceConfig
78
+ >>> from omnibase_infra.enums import EnumHandlerSourceMode
79
+ >>>
80
+ >>> # Production configuration (recommended)
81
+ >>> config = ModelHandlerSourceConfig(
82
+ ... handler_source_mode=EnumHandlerSourceMode.CONTRACT,
83
+ ... )
84
+ >>>
85
+ >>> # Migration configuration with safety expiry (must be timezone-aware)
86
+ >>> config = ModelHandlerSourceConfig(
87
+ ... handler_source_mode=EnumHandlerSourceMode.HYBRID,
88
+ ... bootstrap_expires_at=datetime(2025, 3, 1, 0, 0, 0, tzinfo=timezone.utc),
89
+ ... )
90
+ >>>
91
+ >>> # Check if bootstrap is expired
92
+ >>> if config.is_bootstrap_expired:
93
+ ... print("Bootstrap mode has expired - must use CONTRACT")
94
+ """
95
+
96
+ model_config = ConfigDict(
97
+ strict=True,
98
+ frozen=True,
99
+ extra="forbid",
100
+ )
101
+
102
+ handler_source_mode: EnumHandlerSourceMode = Field(
103
+ default=EnumHandlerSourceMode.HYBRID,
104
+ description="Handler loading source mode: BOOTSTRAP, CONTRACT, or HYBRID. "
105
+ "Defaults to HYBRID (contract-first with bootstrap fallback).",
106
+ )
107
+
108
+ allow_bootstrap_override: bool = Field(
109
+ default=False,
110
+ description=(
111
+ "If True, bootstrap handlers can override contract handlers in HYBRID mode. "
112
+ "Default is False (contract handlers take precedence). "
113
+ "When parsing from config, string values 'true', 'yes', '1', 'on' "
114
+ "(case-insensitive) are accepted as truthy."
115
+ ),
116
+ )
117
+
118
+ bootstrap_expires_at: datetime | None = Field(
119
+ default=None,
120
+ description=(
121
+ "If set and expired, refuse BOOTSTRAP mode and force CONTRACT. "
122
+ "Production safety mechanism for migration deadlines. "
123
+ "Must be timezone-aware (UTC recommended); naive datetimes are rejected."
124
+ ),
125
+ )
126
+
127
+ @field_validator("allow_bootstrap_override", mode="before")
128
+ @classmethod
129
+ def _coerce_allow_bootstrap_override(cls, value: object) -> bool:
130
+ """Coerce string and numeric values to boolean for config file compatibility.
131
+
132
+ Environment variables and YAML/JSON config files often represent booleans
133
+ as strings. This validator handles common truthy string representations
134
+ before Pydantic's strict type validation.
135
+
136
+ Args:
137
+ value: The raw value to coerce (may be str, bool, int, float, None, or other).
138
+
139
+ Returns:
140
+ True if value is a truthy string ("true", "yes", "1", "on") or
141
+ a truthy boolean, False otherwise.
142
+
143
+ Type Handling:
144
+ - bool: Passed through unchanged.
145
+ - str: Case-insensitive check for "true", "yes", "1", "on".
146
+ - int/float: 0 and 0.0 are False, all other numbers are True.
147
+ - None: Returns False.
148
+ - Unknown types: Default to False for safety.
149
+ """
150
+ if isinstance(value, bool):
151
+ return value
152
+ if isinstance(value, str):
153
+ return value.lower() in ("true", "yes", "1", "on")
154
+ if isinstance(value, (int, float)):
155
+ # Explicit: 0/0.0 = False, any other number = True
156
+ return bool(value)
157
+ # Unknown types default to False for safety
158
+ return False
159
+
160
+ @field_validator("bootstrap_expires_at")
161
+ @classmethod
162
+ def _validate_expires_at_timezone(cls, value: datetime | None) -> datetime | None:
163
+ """Validate and normalize bootstrap_expires_at to UTC.
164
+
165
+ Args:
166
+ value: The datetime value to validate.
167
+
168
+ Returns:
169
+ None if value is None, otherwise the datetime normalized to UTC.
170
+
171
+ Raises:
172
+ ValueError: If the datetime is naive (no timezone info).
173
+ """
174
+ if value is None:
175
+ return None
176
+ if value.tzinfo is None:
177
+ raise ValueError(
178
+ "bootstrap_expires_at must be timezone-aware (UTC recommended). "
179
+ "Use datetime.now(timezone.utc) or datetime(..., tzinfo=timezone.utc)."
180
+ )
181
+ return value.astimezone(UTC)
182
+
183
+ @property
184
+ def is_bootstrap_expired(self) -> bool:
185
+ """Check if bootstrap mode has expired.
186
+
187
+ Returns:
188
+ True if bootstrap_expires_at is set and current time exceeds it,
189
+ False otherwise.
190
+
191
+ Note:
192
+ Uses UTC-aware comparison. The bootstrap_expires_at field is
193
+ validated and normalized to UTC at construction time.
194
+ """
195
+ if self.bootstrap_expires_at is None:
196
+ return False
197
+ return datetime.now(UTC) > self.bootstrap_expires_at
198
+
199
+ @property
200
+ def effective_mode(self) -> EnumHandlerSourceMode:
201
+ """Get the effective handler source mode after expiry check.
202
+
203
+ If bootstrap_expires_at is set and expired, returns CONTRACT
204
+ regardless of the configured handler_source_mode. Otherwise
205
+ returns the configured mode.
206
+
207
+ Returns:
208
+ The effective handler source mode to use at runtime.
209
+
210
+ Note:
211
+ This property should be used by runtime components instead of
212
+ directly accessing handler_source_mode to ensure expiry
213
+ enforcement is applied.
214
+ """
215
+ if self.is_bootstrap_expired:
216
+ return EnumHandlerSourceMode.CONTRACT
217
+ return self.handler_source_mode
218
+
219
+
220
+ __all__ = ["ModelHandlerSourceConfig"]
@@ -14,6 +14,7 @@ from uuid import UUID
14
14
  from pydantic import BaseModel, ConfigDict, Field, field_validator
15
15
 
16
16
  from omnibase_core.enums import EnumNodeKind
17
+ from omnibase_core.models.capabilities import ModelContractCapabilities
17
18
  from omnibase_core.models.primitives.model_semver import ModelSemVer
18
19
  from omnibase_infra.enums import EnumIntrospectionReason
19
20
  from omnibase_infra.models.discovery.model_discovered_capabilities import (
@@ -49,6 +50,8 @@ class ModelNodeIntrospectionEvent(BaseModel):
49
50
  node_version: Semantic version of the node.
50
51
  declared_capabilities: Contract-declared capabilities (feature flags).
51
52
  discovered_capabilities: Runtime-discovered capabilities (reflection).
53
+ contract_capabilities: Contract-derived capabilities (design-time truth).
54
+ Populated from the node's contract when available, None otherwise.
52
55
  endpoints: Dictionary of exposed endpoints (name -> URL).
53
56
  current_state: Current FSM state if the node has state management.
54
57
  reason: Why this introspection event was emitted.
@@ -98,6 +101,12 @@ class ModelNodeIntrospectionEvent(BaseModel):
98
101
  default_factory=ModelDiscoveredCapabilities,
99
102
  description="Capabilities discovered via runtime reflection",
100
103
  )
104
+ contract_capabilities: ModelContractCapabilities | None = Field(
105
+ default=None,
106
+ description="Contract-derived capabilities (design-time truth, deterministic). "
107
+ "Populated by ContractCapabilityExtractor from the node's contract. "
108
+ "None when contract is not available or extraction fails.",
109
+ )
101
110
 
102
111
  # Endpoints and state
103
112
  endpoints: dict[str, str] = Field(
@@ -27,6 +27,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_valida
27
27
 
28
28
  logger = logging.getLogger(__name__)
29
29
 
30
+ from omnibase_core.models.primitives import ModelSemVer
30
31
  from omnibase_infra.enums.enum_handler_type_category import EnumHandlerTypeCategory
31
32
  from omnibase_infra.errors import ProtocolConfigurationError
32
33
  from omnibase_infra.models.runtime.model_contract_security_config import (
@@ -131,6 +132,10 @@ class ModelHandlerContract(BaseModel):
131
132
  default=None,
132
133
  description="Optional security configuration for the handler",
133
134
  )
135
+ handler_version: ModelSemVer | None = Field(
136
+ default=None,
137
+ description="Handler version in semantic versioning format. If not provided, defaults to 1.0.0",
138
+ )
134
139
 
135
140
  @field_validator("handler_type", mode="before")
136
141
  @classmethod
@@ -207,18 +212,22 @@ class ModelHandlerContract(BaseModel):
207
212
  return []
208
213
 
209
214
  @model_validator(mode="after")
210
- def set_default_protocol_type(self) -> ModelHandlerContract:
211
- """Set protocol_type from handler_name if not explicitly provided.
215
+ def set_defaults(self) -> ModelHandlerContract:
216
+ """Set default values for protocol_type and handler_version.
212
217
 
213
- If protocol_type is None, derives it from handler_name by stripping
214
- the 'handler-' prefix. If handler_name doesn't have that prefix,
215
- uses the full handler_name as protocol_type.
218
+ Protocol Type:
219
+ If protocol_type is None, derives it from handler_name by stripping
220
+ the 'handler-' prefix. If handler_name doesn't have that prefix,
221
+ uses the full handler_name as protocol_type.
216
222
 
217
- Guards against empty derived protocol_type which would produce
218
- invalid registry keys.
223
+ Guards against empty derived protocol_type which would produce
224
+ invalid registry keys.
225
+
226
+ Handler Version:
227
+ If handler_version is None, sets it to the default version 1.0.0.
219
228
 
220
229
  Returns:
221
- Self with protocol_type populated.
230
+ Self with protocol_type and handler_version populated.
222
231
 
223
232
  Raises:
224
233
  ValueError: If derived protocol_type would be empty (e.g., handler_name
@@ -232,6 +241,8 @@ class ModelHandlerContract(BaseModel):
232
241
  ... )
233
242
  >>> contract.protocol_type
234
243
  'db'
244
+ >>> contract.handler_version
245
+ ModelSemVer(major=1, minor=0, patch=0)
235
246
 
236
247
  >>> contract = ModelHandlerContract(
237
248
  ... handler_name="custom-handler",
@@ -241,6 +252,10 @@ class ModelHandlerContract(BaseModel):
241
252
  >>> contract.protocol_type
242
253
  'custom-handler'
243
254
  """
255
+ # Set default handler_version if not provided
256
+ if self.handler_version is None:
257
+ self.handler_version = ModelSemVer(major=1, minor=0, patch=0)
258
+
244
259
  if self.protocol_type is None:
245
260
  prefix = "handler-"
246
261
  if self.handler_name.startswith(prefix):
@@ -263,7 +278,7 @@ class ModelHandlerContract(BaseModel):
263
278
  f"Provide a non-empty protocol_type or handler_name."
264
279
  )
265
280
 
266
- # Log successful protocol_type derivation for debugging
281
+ # Log successful contract validation for debugging
267
282
  logger.debug(
268
283
  "Handler contract validated successfully",
269
284
  extra={
@@ -271,6 +286,7 @@ class ModelHandlerContract(BaseModel):
271
286
  "handler_class": self.handler_class,
272
287
  "protocol_type": self.protocol_type,
273
288
  "handler_type": self.handler_type.value,
289
+ "handler_version": str(self.handler_version),
274
290
  },
275
291
  )
276
292
 
@@ -24,6 +24,7 @@ from pathlib import Path
24
24
 
25
25
  from pydantic import BaseModel, ConfigDict, Field
26
26
 
27
+ from omnibase_core.models.primitives import ModelSemVer
27
28
  from omnibase_infra.enums.enum_handler_type_category import EnumHandlerTypeCategory
28
29
 
29
30
 
@@ -53,10 +54,13 @@ class ModelLoadedHandler(BaseModel):
53
54
  Examples: ['auth', 'validation', 'http-client'].
54
55
  loaded_at: Timestamp when the handler was successfully loaded.
55
56
  Used for diagnostics and cache invalidation.
57
+ handler_version: Semantic version of the handler from the contract.
58
+ Used for version tracking and compatibility checks.
56
59
 
57
60
  Example:
58
61
  >>> from datetime import datetime, UTC
59
62
  >>> from pathlib import Path
63
+ >>> from omnibase_core.models.primitives import ModelSemVer
60
64
  >>> from omnibase_infra.enums import EnumHandlerTypeCategory
61
65
  >>> handler = ModelLoadedHandler(
62
66
  ... handler_name="auth.validate_token",
@@ -65,6 +69,7 @@ class ModelLoadedHandler(BaseModel):
65
69
  ... contract_path=Path("/app/handlers/auth/handler_contract.yaml"),
66
70
  ... capability_tags=["auth", "validation", "jwt"],
67
71
  ... loaded_at=datetime.now(UTC),
72
+ ... handler_version=ModelSemVer(major=1, minor=0, patch=0),
68
73
  ... )
69
74
  >>> handler.handler_name
70
75
  'auth.validate_token'
@@ -115,6 +120,10 @@ class ModelLoadedHandler(BaseModel):
115
120
  ...,
116
121
  description="Timestamp when the handler was successfully loaded",
117
122
  )
123
+ handler_version: ModelSemVer = Field(
124
+ ...,
125
+ description="Handler semantic version from contract",
126
+ )
118
127
 
119
128
 
120
129
  __all__ = ["ModelLoadedHandler"]
@@ -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 ProtocolHandler 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
 
@@ -93,10 +93,10 @@ from typing import TYPE_CHECKING, cast
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
  )
99
- from omnibase_spi.protocols.handlers import ProtocolHandler
100
100
 
101
101
  logger = logging.getLogger(__name__)
102
102
 
@@ -111,14 +111,14 @@ ALLOWED_NAMESPACES: tuple[str, ...] = (
111
111
 
112
112
 
113
113
  def _validate_handler_protocol(handler: object) -> tuple[bool, list[str]]:
114
- """Validate handler implements ProtocolHandler via duck typing.
114
+ """Validate handler implements ProtocolContainerAware via duck typing.
115
115
 
116
116
  Uses duck typing to verify the handler has the required properties and
117
- methods for ProtocolHandler compliance. Per ONEX conventions,
117
+ methods for ProtocolContainerAware compliance. Per ONEX conventions,
118
118
  protocol compliance is verified via structural typing rather than
119
119
  isinstance checks.
120
120
 
121
- Protocol Requirements (ProtocolHandler):
121
+ Protocol Requirements (ProtocolContainerAware):
122
122
  - handler_id (property): Unique identifier string
123
123
  - category (property): EnumMessageCategory value
124
124
  - message_types (property): set[str] of message type names
@@ -493,7 +493,7 @@ class RegistryInfraNodeRegistrationOrchestrator:
493
493
  context=ctx,
494
494
  ) from e
495
495
 
496
- # Validate handler implements ProtocolHandler
496
+ # Validate handler implements ProtocolContainerAware
497
497
  is_valid, missing = _validate_handler_protocol(handler_instance)
498
498
  if not is_valid:
499
499
  ctx = ModelInfraErrorContext.with_correlation(
@@ -502,7 +502,7 @@ class RegistryInfraNodeRegistrationOrchestrator:
502
502
  target_name=handler_class_name,
503
503
  )
504
504
  raise ProtocolConfigurationError(
505
- f"Handler '{handler_class_name}' does not implement ProtocolHandler. "
505
+ f"Handler '{handler_class_name}' does not implement ProtocolContainerAware. "
506
506
  f"Missing required members: {', '.join(missing)}. "
507
507
  f"Handlers must have: handler_id, category, message_types, node_kind properties "
508
508
  f"and handle(envelope) method. "
@@ -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,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
 
@@ -139,7 +139,7 @@ class RegistryInfraRegistrationStorage:
139
139
  >>> from omnibase_infra.handlers.registration_storage import (
140
140
  ... HandlerRegistrationStoragePostgres,
141
141
  ... )
142
- >>> handler = HandlerRegistrationStoragePostgres(pool, config)
142
+ >>> handler = HandlerRegistrationStoragePostgres(container, dsn="postgresql://...")
143
143
  >>> RegistryInfraRegistrationStorage.register_handler(container, handler)
144
144
  """
145
145
  # Import at runtime for isinstance check (protocol is @runtime_checkable)
@@ -86,7 +86,10 @@ class RegistryInfraServiceDiscovery:
86
86
  RegistryInfraServiceDiscovery.register(container)
87
87
 
88
88
  # Explicit handler registration
89
- consul_handler = HandlerServiceDiscoveryConsul(config)
89
+ consul_handler = HandlerServiceDiscoveryConsul(
90
+ container=container,
91
+ consul_host="localhost",
92
+ )
90
93
  RegistryInfraServiceDiscovery.register_with_handler(
91
94
  container,
92
95
  handler=consul_handler,
@@ -62,6 +62,7 @@ from omnibase_infra.protocols.protocol_capability_projection import (
62
62
  ProtocolCapabilityProjection,
63
63
  )
64
64
  from omnibase_infra.protocols.protocol_capability_query import ProtocolCapabilityQuery
65
+ from omnibase_infra.protocols.protocol_container_aware import ProtocolContainerAware
65
66
  from omnibase_infra.protocols.protocol_event_bus_like import ProtocolEventBusLike
66
67
  from omnibase_infra.protocols.protocol_event_projector import ProtocolEventProjector
67
68
  from omnibase_infra.protocols.protocol_idempotency_store import (
@@ -88,6 +89,7 @@ __all__: list[str] = [
88
89
  "ProtocolCapabilityQuery",
89
90
  "ProtocolEventBusLike",
90
91
  "ProtocolEventProjector",
92
+ "ProtocolContainerAware",
91
93
  "ProtocolIdempotencyStore",
92
94
  "ProtocolMessageDispatcher",
93
95
  "ProtocolMessageTypeRegistry",