@oneciel-ai/ciel-runtime 0.2.2 → 0.2.4

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 (136) hide show
  1. package/ciel_runtime.py +2555 -9635
  2. package/ciel_runtime_support/advisor_request_builder.py +8 -21
  3. package/ciel_runtime_support/anthropic_tool_turns.py +13 -8
  4. package/ciel_runtime_support/architecture.py +68 -0
  5. package/ciel_runtime_support/architecture_budget.py +1 -1
  6. package/ciel_runtime_support/channel_connection_context.py +233 -0
  7. package/ciel_runtime_support/channel_delivery_context.py +332 -0
  8. package/ciel_runtime_support/channel_mcp_context.py +313 -0
  9. package/ciel_runtime_support/channel_mcp_discovery.py +47 -0
  10. package/ciel_runtime_support/channel_mcp_transport.py +5 -1
  11. package/ciel_runtime_support/channel_message_context.py +212 -0
  12. package/ciel_runtime_support/channel_message_repository.py +14 -3
  13. package/ciel_runtime_support/channel_pending_injection.py +9 -0
  14. package/ciel_runtime_support/channel_probe_launch_context.py +213 -0
  15. package/ciel_runtime_support/channel_replay_policy.py +38 -0
  16. package/ciel_runtime_support/channel_runtime_environment.py +8 -0
  17. package/ciel_runtime_support/channel_session_context.py +236 -0
  18. package/ciel_runtime_support/channel_terminal_context.py +350 -0
  19. package/ciel_runtime_support/channel_wake_context.py +532 -0
  20. package/ciel_runtime_support/claude_environment.py +60 -0
  21. package/ciel_runtime_support/claude_launch_assembly.py +249 -0
  22. package/ciel_runtime_support/claude_router.py +62 -12
  23. package/ciel_runtime_support/cli_application_context.py +132 -0
  24. package/ciel_runtime_support/cli_assembly.py +50 -0
  25. package/ciel_runtime_support/codex_backend_context.py +363 -0
  26. package/ciel_runtime_support/codex_config.py +13 -1
  27. package/ciel_runtime_support/codex_launch_assembly.py +213 -0
  28. package/ciel_runtime_support/codex_launch_configuration.py +30 -1
  29. package/ciel_runtime_support/codex_mcp_integration.py +90 -8
  30. package/ciel_runtime_support/codex_model_catalog.py +4 -1
  31. package/ciel_runtime_support/codex_reasoning_rejects.py +225 -0
  32. package/ciel_runtime_support/codex_router.py +38 -8
  33. package/ciel_runtime_support/codex_turn_recovery.py +154 -0
  34. package/ciel_runtime_support/config_migrations.py +103 -0
  35. package/ciel_runtime_support/config_repository.py +30 -0
  36. package/ciel_runtime_support/configuration_cli.py +38 -0
  37. package/ciel_runtime_support/context_compaction.py +9 -4
  38. package/ciel_runtime_support/credential_management.py +12 -0
  39. package/ciel_runtime_support/credentials.py +12 -0
  40. package/ciel_runtime_support/github_copilot_oauth.py +2 -2
  41. package/ciel_runtime_support/hosted_formula_tools.py +216 -0
  42. package/ciel_runtime_support/kimi_runtime_context.py +208 -0
  43. package/ciel_runtime_support/llm_preset_context.py +338 -0
  44. package/ciel_runtime_support/managed_mcp_config.py +8 -4
  45. package/ciel_runtime_support/mcp_configuration_context.py +291 -0
  46. package/ciel_runtime_support/mcp_http_proxy.py +14 -8
  47. package/ciel_runtime_support/mcp_probe_transport.py +47 -15
  48. package/ciel_runtime_support/mcp_transport.py +14 -1
  49. package/ciel_runtime_support/native_context_recovery.py +72 -0
  50. package/ciel_runtime_support/ollama_catalog_context.py +213 -0
  51. package/ciel_runtime_support/ollama_stream_collection.py +103 -0
  52. package/ciel_runtime_support/ollama_thinking.py +6 -1
  53. package/ciel_runtime_support/ollama_wire_projection.py +157 -0
  54. package/ciel_runtime_support/openai_forwarding.py +32 -10
  55. package/ciel_runtime_support/openai_responses_router.py +12 -0
  56. package/ciel_runtime_support/package_lifecycle.py +39 -0
  57. package/ciel_runtime_support/prelaunch_assembly.py +37 -0
  58. package/ciel_runtime_support/prelaunch_panel_context.py +418 -0
  59. package/ciel_runtime_support/prelaunch_shell_context.py +394 -0
  60. package/ciel_runtime_support/prompt_compaction.py +144 -0
  61. package/ciel_runtime_support/prompt_injection.py +45 -0
  62. package/ciel_runtime_support/protocols/anthropic_thinking_policy.py +1 -1
  63. package/ciel_runtime_support/protocols/chat_projection.py +85 -5
  64. package/ciel_runtime_support/protocols/conversation_turn_policy.py +43 -0
  65. package/ciel_runtime_support/protocols/ollama_chat.py +31 -0
  66. package/ciel_runtime_support/protocols/ollama_response.py +57 -5
  67. package/ciel_runtime_support/protocols/openai_reasoning.py +5 -2
  68. package/ciel_runtime_support/protocols/openai_responses.py +61 -15
  69. package/ciel_runtime_support/provider_adapters.py +26 -0
  70. package/ciel_runtime_support/provider_administration_context.py +207 -0
  71. package/ciel_runtime_support/provider_config_mutations.py +3 -0
  72. package/ciel_runtime_support/provider_model_catalog_context.py +137 -0
  73. package/ciel_runtime_support/provider_model_context.py +107 -0
  74. package/ciel_runtime_support/provider_model_metadata_context.py +197 -0
  75. package/ciel_runtime_support/provider_model_selection.py +10 -3
  76. package/ciel_runtime_support/provider_models.py +45 -2
  77. package/ciel_runtime_support/provider_option_cli.py +19 -0
  78. package/ciel_runtime_support/provider_policy.py +1 -1
  79. package/ciel_runtime_support/provider_readiness_context.py +189 -0
  80. package/ciel_runtime_support/provider_request_builder.py +64 -28
  81. package/ciel_runtime_support/provider_responses_passthrough.py +21 -2
  82. package/ciel_runtime_support/provider_timeout_policy.py +54 -0
  83. package/ciel_runtime_support/provider_tool_policy.py +9 -1
  84. package/ciel_runtime_support/providers/__init__.py +6 -0
  85. package/ciel_runtime_support/providers/alibaba.py +634 -0
  86. package/ciel_runtime_support/providers/catalog.py +24 -16
  87. package/ciel_runtime_support/providers/deepseek.py +73 -0
  88. package/ciel_runtime_support/providers/github_copilot_oauth.py +22 -1
  89. package/ciel_runtime_support/providers/kimi.py +69 -9
  90. package/ciel_runtime_support/providers/ollama.py +8 -0
  91. package/ciel_runtime_support/providers/ollama_context.py +21 -2
  92. package/ciel_runtime_support/providers/vllm.py +7 -1
  93. package/ciel_runtime_support/response_collection.py +68 -18
  94. package/ciel_runtime_support/response_collection_context.py +391 -0
  95. package/ciel_runtime_support/response_stream_context.py +555 -0
  96. package/ciel_runtime_support/responses_input_compatibility.py +121 -0
  97. package/ciel_runtime_support/responses_usage_observer.py +83 -0
  98. package/ciel_runtime_support/router_client_lifecycle.py +1 -0
  99. package/ciel_runtime_support/router_http.py +245 -3
  100. package/ciel_runtime_support/router_observability_context.py +251 -0
  101. package/ciel_runtime_support/router_process_context.py +200 -0
  102. package/ciel_runtime_support/router_process_lifecycle.py +2 -0
  103. package/ciel_runtime_support/router_request_assembly.py +399 -0
  104. package/ciel_runtime_support/router_request_context.py +215 -0
  105. package/ciel_runtime_support/router_server_context.py +84 -0
  106. package/ciel_runtime_support/runaway_output_guard.py +488 -0
  107. package/ciel_runtime_support/runtime_asset_assembly.py +147 -0
  108. package/ciel_runtime_support/runtime_asset_context.py +297 -0
  109. package/ciel_runtime_support/runtime_constants.py +16 -1
  110. package/ciel_runtime_support/runtime_launch.py +9 -5
  111. package/ciel_runtime_support/runtime_launch_context.py +130 -0
  112. package/ciel_runtime_support/runtime_maintenance_assembly.py +60 -0
  113. package/ciel_runtime_support/runtime_maintenance_context.py +309 -0
  114. package/ciel_runtime_support/runtime_maintenance_services.py +265 -0
  115. package/ciel_runtime_support/runtime_paths.py +60 -40
  116. package/ciel_runtime_support/runtime_primitives.py +78 -0
  117. package/ciel_runtime_support/speech_http_controller.py +335 -0
  118. package/ciel_runtime_support/sse_stream_collection.py +236 -0
  119. package/ciel_runtime_support/statusline_script.py +57 -8
  120. package/ciel_runtime_support/streaming_anthropic.py +361 -24
  121. package/ciel_runtime_support/tool_schema.py +40 -2
  122. package/ciel_runtime_support/tool_side_effect_dedupe.py +117 -12
  123. package/ciel_runtime_support/upstream_dump.py +68 -0
  124. package/ciel_runtime_support/upstream_retry_context.py +259 -0
  125. package/ciel_runtime_support/web_ui.py +248 -1
  126. package/ciel_runtime_support/workspace_router_selection.py +86 -0
  127. package/docs/COLAB_SPEECH.md +32 -0
  128. package/docs/Configuration.md +50 -0
  129. package/docs/Test-Suite.md +1 -0
  130. package/package.json +4 -1
  131. package/scripts/colab/__pycache__/bootstrap_moss_tts.cpython-311.pyc +0 -0
  132. package/scripts/colab/__pycache__/bootstrap_qwen_asr.cpython-311.pyc +0 -0
  133. package/scripts/colab/bootstrap_moss_tts.py +106 -0
  134. package/scripts/colab/bootstrap_qwen_asr.py +106 -0
  135. package/scripts/configure_speech_workers.py +37 -0
  136. package/scripts/deploy_colab_speech.ps1 +47 -0
@@ -37,12 +37,7 @@ class AdvisorBudgetPorts:
37
37
  reserve: Callable[[dict[str, Any], int], int]
38
38
  compact_messages: Callable[..., list[dict[str, Any]]]
39
39
  configured_output: Callable[..., int]
40
- ollama_options: Callable[[dict[str, Any]], dict[str, Any]]
41
- positive_int: Callable[[Any], int]
42
- ollama_num_ctx: Callable[..., int]
43
- think_value: Callable[
44
- [str, str | None, dict[str, Any], dict[str, Any]], bool | str | None
45
- ]
40
+ apply_ollama_optional: Callable[..., dict[str, Any]]
46
41
 
47
42
 
48
43
  @dataclass(frozen=True, slots=True)
@@ -208,22 +203,14 @@ class AdvisorRequestBuilder:
208
203
  "messages": messages,
209
204
  "stream": False,
210
205
  }
211
- think = self.budget.think_value(provider, upstream_model, config, body)
212
- if think is not None:
213
- request["think"] = think
214
- options = self.budget.ollama_options(config)
215
- options.setdefault(
216
- "num_predict",
217
- min(4096, self.budget.positive_int(options.get("num_predict")) or 4096),
206
+ return self.budget.apply_ollama_optional(
207
+ request,
208
+ provider,
209
+ upstream_model,
210
+ config,
211
+ body,
212
+ output_limit=4096,
218
213
  )
219
- num_ctx = self.budget.ollama_num_ctx(
220
- config, {"messages": messages, "tools": []}
221
- )
222
- if num_ctx:
223
- options.setdefault("num_ctx", num_ctx)
224
- if options:
225
- request["options"] = options
226
- return request
227
214
 
228
215
  @staticmethod
229
216
  def _sampling_options(
@@ -9,8 +9,6 @@ from typing import Any
9
9
 
10
10
  @dataclass(frozen=True, slots=True)
11
11
  class AnthropicToolTurnServices:
12
- tool_use_as_text: Callable[[dict[str, Any]], dict[str, str]]
13
- tool_result_as_text: Callable[[dict[str, Any]], dict[str, str]]
14
12
  log: Callable[[str, str], Any]
15
13
 
16
14
 
@@ -19,7 +17,14 @@ def normalize_historical_anthropic_tool_turns(
19
17
  body: dict[str, Any],
20
18
  services: AnthropicToolTurnServices,
21
19
  ) -> dict[str, Any]:
22
- """Downgrade unmatched historical tool blocks instead of synthesizing success."""
20
+ """Discard unmatched historical tool blocks instead of replaying them as text.
21
+
22
+ An unmatched tool block cannot be sent on provider wires that require strict
23
+ tool-use/result pairing. Turning it into conversation text is also unsafe:
24
+ the model can treat the diagnostic, command, or stale result as a new user
25
+ instruction. Keep valid pairs verbatim and report discarded history only
26
+ through the runtime log.
27
+ """
23
28
  messages = body.get("messages")
24
29
  if not isinstance(messages, list):
25
30
  return body
@@ -57,12 +62,12 @@ def normalize_historical_anthropic_tool_turns(
57
62
  if isinstance(block, dict) and block.get("type") == "tool_use":
58
63
  tool_id = str(block.get("id") or "")
59
64
  if not tool_id or tool_id not in retained:
60
- next_content.append(services.tool_use_as_text(block))
61
65
  converted_tool_uses += 1
62
66
  content_changed = True
63
67
  continue
64
68
  next_content.append(block)
65
- normalized_messages.append(_with_content(message, next_content) if content_changed else message)
69
+ if next_content:
70
+ normalized_messages.append(_with_content(message, next_content) if content_changed else message)
66
71
  changed = changed or content_changed
67
72
  retained_tool_ids_for_next_user = retained
68
73
  continue
@@ -76,12 +81,12 @@ def normalize_historical_anthropic_tool_turns(
76
81
  if tool_id and tool_id in retained_tool_ids_for_next_user:
77
82
  next_content.append(block)
78
83
  else:
79
- next_content.append(services.tool_result_as_text(block))
80
84
  converted_tool_results += 1
81
85
  content_changed = True
82
86
  continue
83
87
  next_content.append(block)
84
- normalized_messages.append(_with_content(message, next_content) if content_changed else message)
88
+ if next_content:
89
+ normalized_messages.append(_with_content(message, next_content) if content_changed else message)
85
90
  changed = changed or content_changed
86
91
  retained_tool_ids_for_next_user = set()
87
92
  continue
@@ -95,7 +100,7 @@ def normalize_historical_anthropic_tool_turns(
95
100
  out["messages"] = normalized_messages
96
101
  services.log(
97
102
  "WARN",
98
- "normalized historical Anthropic tool turns for provider=%s converted_tool_uses=%d converted_tool_results=%d"
103
+ "discarded unmatched historical Anthropic tool blocks for provider=%s tool_uses=%d tool_results=%d"
99
104
  % (provider, converted_tool_uses, converted_tool_results),
100
105
  )
101
106
  return out
@@ -152,6 +152,8 @@ class ProviderModelCatalogPolicy:
152
152
  allow_configured_fallback: bool = False
153
153
  allow_public_without_auth: bool = False
154
154
  use_bundled_catalog_fallback: bool = False
155
+ authoritative_upstream_catalog: bool = False
156
+ supplemental_model_aliases: tuple[tuple[str, str], ...] = ()
155
157
 
156
158
 
157
159
  @dataclass(frozen=True)
@@ -220,6 +222,14 @@ class ProviderOptionPresentationPolicy:
220
222
  show_ip_family_control: bool = False
221
223
 
222
224
 
225
+ @dataclass(frozen=True)
226
+ class HostedToolPolicy:
227
+ """Provider-owned hosted tool catalog and execution endpoint."""
228
+
229
+ base_url: str = ""
230
+ formulas: tuple[str, ...] = ()
231
+
232
+
223
233
  @dataclass(frozen=True)
224
234
  class ProviderUiPolicy:
225
235
  """Provider-owned labels used by shared menus."""
@@ -269,6 +279,12 @@ class ProviderAdapter(ABC):
269
279
  def default_base_url(self) -> str:
270
280
  """Return the provider default API base URL."""
271
281
 
282
+ def anthropic_base_url(self, config: ProviderConfig) -> str:
283
+ """Return the base URL used for native Anthropic Messages requests."""
284
+
285
+ del config
286
+ return self.default_base_url()
287
+
272
288
  def normalize_base_url(self, value: str) -> str:
273
289
  """Normalize a user-supplied endpoint before it is persisted."""
274
290
 
@@ -365,6 +381,17 @@ class ProviderAdapter(ABC):
365
381
  del model
366
382
  return self.capabilities(config).supports_tool_choice
367
383
 
384
+ def supports_tool_choice_for_request(
385
+ self,
386
+ config: ProviderConfig,
387
+ model: str | None,
388
+ request: Mapping[str, Any],
389
+ ) -> bool:
390
+ """Return whether this request may include an explicit tool choice."""
391
+
392
+ del request
393
+ return self.supports_tool_choice(config, model)
394
+
368
395
  def normalizes_anthropic_tool_use(
369
396
  self,
370
397
  config: ProviderConfig,
@@ -392,6 +419,28 @@ class ProviderAdapter(ABC):
392
419
  del config, model
393
420
  return False
394
421
 
422
+ def reasoning_passback_enabled(
423
+ self, config: ProviderConfig, model: str | None = None
424
+ ) -> bool:
425
+ """Whether provider-native reasoning should survive history conversion."""
426
+
427
+ return self.openai_reasoning_passback_enabled(config, model)
428
+
429
+ def hosted_tool_policy(self, config: ProviderConfig) -> HostedToolPolicy:
430
+ del config
431
+ return HostedToolPolicy()
432
+
433
+ def should_omit_openai_tool_choice(
434
+ self,
435
+ config: ProviderConfig,
436
+ model: str | None,
437
+ request: Mapping[str, Any],
438
+ ) -> bool:
439
+ """Whether this provider/model cannot combine reasoning with tool_choice."""
440
+
441
+ del request
442
+ return self.openai_reasoning_passback_enabled(config, model)
443
+
395
444
  def model_catalog_policy(self, config: ProviderConfig) -> ProviderModelCatalogPolicy:
396
445
  del config
397
446
  return ProviderModelCatalogPolicy()
@@ -589,6 +638,12 @@ class ProviderAdapter(ABC):
589
638
  del config
590
639
  return False
591
640
 
641
+ def supports_server_web_tools(self, config: ProviderConfig) -> bool:
642
+ """Whether this provider can execute Claude's server-side web tools."""
643
+
644
+ del config
645
+ return False
646
+
592
647
  def context_compaction_available(self, config: ProviderConfig) -> bool:
593
648
  """Whether this configured provider can run an auxiliary summary request."""
594
649
 
@@ -617,6 +672,19 @@ class ProviderAdapter(ABC):
617
672
  del config
618
673
  return request
619
674
 
675
+ def anthropic_system_role_strategy(
676
+ self, config: ProviderConfig
677
+ ) -> Literal["inline_user", "hoist_top_level"]:
678
+ """Choose how non-standard system-role history is made wire-valid.
679
+
680
+ Keeping the item at its original position protects the stable prefix
681
+ used by provider-side context caches. Compatibility servers that only
682
+ accept top-level system context may opt into hoisting explicitly.
683
+ """
684
+
685
+ del config
686
+ return "inline_user"
687
+
620
688
  def openai_reasoning_effort(
621
689
  self, config: ProviderConfig, model: str, request: Mapping[str, Any]
622
690
  ) -> str | None:
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
 
7
7
 
8
8
  FINAL_FILE_LINE_BUDGET = 4_999
9
- MAIN_FILE_LINE_BUDGET = 12_989
9
+ MAIN_FILE_LINE_BUDGET = 4_980
10
10
 
11
11
 
12
12
  def production_python_files(root: Path) -> tuple[Path, ...]:
@@ -0,0 +1,233 @@
1
+ """Channel connection registry, worker, and lifecycle bounded context."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Any, Callable
7
+
8
+ from .channel_connection_lifecycle import (
9
+ ChannelConnectionLifecycle,
10
+ ChannelConnectionLifecycleEffects,
11
+ ChannelConnectionLifecyclePolicy,
12
+ ChannelConnectionLifecycleStore,
13
+ )
14
+ from .channel_connection_registry import ChannelConnectionRegistry
15
+ from .channel_connection_worker import (
16
+ ChannelConnectionWorker,
17
+ ChannelWorkerEffects,
18
+ ChannelWorkerPolicy,
19
+ ChannelWorkerStateStore,
20
+ )
21
+
22
+
23
+ @dataclass(frozen=True, slots=True)
24
+ class ChannelConnectionStatePorts:
25
+ connections: dict[str, dict[str, Any]]
26
+ lock: Any
27
+ rpc_condition: Any
28
+
29
+
30
+ @dataclass(frozen=True, slots=True)
31
+ class ChannelConnectionWorkerPorts:
32
+ log: Callable[[str, str], None]
33
+ dispatch: Callable[..., Any]
34
+ initialize_streamable: Callable[[str], None]
35
+ close_state_session: Callable[[dict[str, Any], str], bool]
36
+ streamable_headers: Callable[..., dict[str, str]]
37
+ session_not_found: Callable[..., bool]
38
+ http_error_body: Callable[..., str]
39
+
40
+
41
+ @dataclass(frozen=True, slots=True)
42
+ class ChannelConnectionLifecyclePorts:
43
+ safe_segment: Callable[[Any], str]
44
+ close_session: Callable[[dict[str, Any], str], bool]
45
+ cleanup_stale_sessions: Callable[..., None]
46
+ parse_bool: Callable[..., bool]
47
+
48
+
49
+ @dataclass(frozen=True, slots=True)
50
+ class ChannelConnectionProtocol:
51
+ streamable_version: str
52
+ legacy_sse_version: str
53
+
54
+
55
+ @dataclass(frozen=True, slots=True)
56
+ class ChannelConnectionContext:
57
+ state: ChannelConnectionStatePorts
58
+ worker_ports: ChannelConnectionWorkerPorts
59
+ lifecycle_ports: ChannelConnectionLifecyclePorts
60
+ protocol: ChannelConnectionProtocol
61
+
62
+ def registry(self) -> ChannelConnectionRegistry:
63
+ return ChannelConnectionRegistry(
64
+ states=self.state.connections,
65
+ lock=self.state.lock,
66
+ rpc_condition=self.state.rpc_condition,
67
+ log=self.worker_ports.log,
68
+ )
69
+
70
+ def statuses(self) -> dict[str, Any]:
71
+ return self.registry().statuses()
72
+
73
+ def update(self, name: str, **updates: Any) -> None:
74
+ self.registry().update(name, **updates)
75
+
76
+ def mark_session_lost(self, name: str, reason: str) -> None:
77
+ self.registry().mark_session_lost(name, reason)
78
+
79
+ def store_rpc_response(self, name: str, data_text: str) -> bool:
80
+ return self.registry().store_rpc_response(name, data_text)
81
+
82
+ def take_rpc_response(
83
+ self, name: str, rpc_id: Any, timeout: float
84
+ ) -> dict[str, Any] | None:
85
+ return self.registry().take_rpc_response(name, rpc_id, timeout)
86
+
87
+ def state_name_for_mcp_server(self, server_name: str) -> str | None:
88
+ return self.registry().state_name_for_mcp_server(server_name)
89
+
90
+ def connection_matches(
91
+ self, state: dict[str, Any], connection_id: str | None
92
+ ) -> bool:
93
+ if not connection_id:
94
+ return True
95
+ return str(state.get("connection_id") or "") == str(connection_id)
96
+
97
+ def worker_running(self, name: str, connection_id: str | None) -> bool:
98
+ with self.state.lock:
99
+ state = self.state.connections.get(name)
100
+ return bool(
101
+ state
102
+ and state.get("running")
103
+ and self.connection_matches(state, connection_id)
104
+ )
105
+
106
+ def worker(self) -> ChannelConnectionWorker:
107
+ ports = self.worker_ports
108
+ return ChannelConnectionWorker(
109
+ state_store=ChannelWorkerStateStore(
110
+ self.state.connections, self.state.lock
111
+ ),
112
+ effects=ChannelWorkerEffects(
113
+ log=ports.log,
114
+ dispatch=ports.dispatch,
115
+ set_state=self.update,
116
+ initialize_streamable=ports.initialize_streamable,
117
+ close_state_session=ports.close_state_session,
118
+ streamable_headers=ports.streamable_headers,
119
+ session_not_found=ports.session_not_found,
120
+ http_error_body=ports.http_error_body,
121
+ ),
122
+ policy=ChannelWorkerPolicy(
123
+ streamable_protocol_version=self.protocol.streamable_version,
124
+ legacy_sse_protocol_version=self.protocol.legacy_sse_version,
125
+ parse_bool=self.lifecycle_ports.parse_bool,
126
+ ),
127
+ )
128
+
129
+ def run_sse_worker(
130
+ self, name: str, connection_id: str | None = None
131
+ ) -> None:
132
+ self.worker().run_sse(name, connection_id)
133
+
134
+ def run_streamable_http_worker(
135
+ self, name: str, connection_id: str | None = None
136
+ ) -> None:
137
+ self.worker().run_streamable_http(name, connection_id)
138
+
139
+ def lifecycle(self) -> ChannelConnectionLifecycle:
140
+ ports = self.lifecycle_ports
141
+ return ChannelConnectionLifecycle(
142
+ store=ChannelConnectionLifecycleStore(
143
+ self.state.connections, self.state.lock
144
+ ),
145
+ effects=ChannelConnectionLifecycleEffects(
146
+ safe_segment=ports.safe_segment,
147
+ close_session=ports.close_session,
148
+ cleanup_stale_sessions=ports.cleanup_stale_sessions,
149
+ public_status=ChannelConnectionRegistry.public_status,
150
+ all_statuses=self.statuses,
151
+ sse_worker=self.run_sse_worker,
152
+ streamable_http_worker=self.run_streamable_http_worker,
153
+ ),
154
+ policy=ChannelConnectionLifecyclePolicy(
155
+ streamable_protocol_version=self.protocol.streamable_version,
156
+ legacy_sse_protocol_version=self.protocol.legacy_sse_version,
157
+ parse_bool=ports.parse_bool,
158
+ ),
159
+ )
160
+
161
+ def start(self, config: dict[str, Any]) -> dict[str, Any]:
162
+ return self.lifecycle().start(config)
163
+
164
+ def stop(self, name: str | None = None) -> dict[str, Any]:
165
+ return self.lifecycle().stop(name)
166
+
167
+
168
+ @dataclass(frozen=True, slots=True)
169
+ class ChannelConnectionCompatibilityApi:
170
+ context: Callable[[], ChannelConnectionContext]
171
+
172
+ def registry(self) -> ChannelConnectionRegistry:
173
+ return self.context().registry()
174
+
175
+ def statuses(self) -> dict[str, Any]:
176
+ return self.context().statuses()
177
+
178
+ def update(self, name: str, **updates: Any) -> None:
179
+ self.context().update(name, **updates)
180
+
181
+ def mark_session_lost(self, name: str, reason: str) -> None:
182
+ self.context().mark_session_lost(name, reason)
183
+
184
+ def store_rpc_response(self, name: str, data_text: str) -> bool:
185
+ return self.context().store_rpc_response(name, data_text)
186
+
187
+ def take_rpc_response(
188
+ self, name: str, rpc_id: Any, timeout: float
189
+ ) -> dict[str, Any] | None:
190
+ return self.context().take_rpc_response(name, rpc_id, timeout)
191
+
192
+ def state_name_for_mcp_server(self, server_name: str) -> str | None:
193
+ return self.context().state_name_for_mcp_server(server_name)
194
+
195
+ def connection_matches(
196
+ self, state: dict[str, Any], connection_id: str | None
197
+ ) -> bool:
198
+ return self.context().connection_matches(state, connection_id)
199
+
200
+ def worker_running(self, name: str, connection_id: str | None) -> bool:
201
+ return self.context().worker_running(name, connection_id)
202
+
203
+ def worker(self) -> ChannelConnectionWorker:
204
+ return self.context().worker()
205
+
206
+ def run_sse_worker(
207
+ self, name: str, connection_id: str | None = None
208
+ ) -> None:
209
+ self.context().run_sse_worker(name, connection_id)
210
+
211
+ def run_streamable_http_worker(
212
+ self, name: str, connection_id: str | None = None
213
+ ) -> None:
214
+ self.context().run_streamable_http_worker(name, connection_id)
215
+
216
+ def lifecycle(self) -> ChannelConnectionLifecycle:
217
+ return self.context().lifecycle()
218
+
219
+ def start(self, config: dict[str, Any]) -> dict[str, Any]:
220
+ return self.context().start(config)
221
+
222
+ def stop(self, name: str | None = None) -> dict[str, Any]:
223
+ return self.context().stop(name)
224
+
225
+
226
+ __all__ = [
227
+ "ChannelConnectionCompatibilityApi",
228
+ "ChannelConnectionContext",
229
+ "ChannelConnectionLifecyclePorts",
230
+ "ChannelConnectionProtocol",
231
+ "ChannelConnectionStatePorts",
232
+ "ChannelConnectionWorkerPorts",
233
+ ]