@oneciel-ai/ciel-runtime 0.1.1 → 0.2.0

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 (287) hide show
  1. package/README.md +132 -0
  2. package/ciel-runtime-menu.py +56 -6
  3. package/ciel_runtime.py +9379 -35909
  4. package/ciel_runtime_support/advisor_client.py +193 -0
  5. package/ciel_runtime_support/advisor_policy.py +320 -0
  6. package/ciel_runtime_support/advisor_refinement.py +160 -0
  7. package/ciel_runtime_support/advisor_request_builder.py +261 -0
  8. package/ciel_runtime_support/agy_installer.py +169 -0
  9. package/ciel_runtime_support/agy_mcp_restore.py +182 -0
  10. package/ciel_runtime_support/anthropic_model_policy.py +186 -0
  11. package/ciel_runtime_support/anthropic_response_writer.py +255 -0
  12. package/ciel_runtime_support/anthropic_tool_turns.py +130 -0
  13. package/ciel_runtime_support/api_key_cooldown.py +159 -0
  14. package/ciel_runtime_support/architecture.py +488 -1
  15. package/ciel_runtime_support/architecture_budget.py +42 -0
  16. package/ciel_runtime_support/channel_backlog.py +90 -0
  17. package/ciel_runtime_support/channel_cli.py +119 -0
  18. package/ciel_runtime_support/channel_compact_injection.py +82 -0
  19. package/ciel_runtime_support/channel_compact_poll.py +67 -0
  20. package/ciel_runtime_support/channel_compact_request_repository.py +113 -0
  21. package/ciel_runtime_support/channel_config_service.py +281 -0
  22. package/ciel_runtime_support/channel_connection_lifecycle.py +180 -0
  23. package/ciel_runtime_support/channel_connection_registry.py +128 -0
  24. package/ciel_runtime_support/channel_connection_worker.py +284 -0
  25. package/ciel_runtime_support/channel_cursor_recovery.py +92 -0
  26. package/ciel_runtime_support/channel_cursor_repository.py +89 -0
  27. package/ciel_runtime_support/channel_cursor_service.py +178 -0
  28. package/ciel_runtime_support/channel_event_identity.py +212 -0
  29. package/ciel_runtime_support/channel_event_projection.py +315 -0
  30. package/ciel_runtime_support/channel_inflight.py +127 -0
  31. package/ciel_runtime_support/channel_injection.py +115 -0
  32. package/ciel_runtime_support/channel_launch_guard_repository.py +58 -0
  33. package/ciel_runtime_support/channel_launch_policy.py +180 -0
  34. package/ciel_runtime_support/channel_llm_context.py +156 -0
  35. package/ciel_runtime_support/channel_mcp_discovery.py +186 -0
  36. package/ciel_runtime_support/channel_mcp_http_controller.py +239 -0
  37. package/ciel_runtime_support/channel_mcp_ownership.py +148 -0
  38. package/ciel_runtime_support/channel_mcp_tools.py +240 -0
  39. package/ciel_runtime_support/channel_mcp_transport.py +394 -0
  40. package/ciel_runtime_support/channel_message_dedupe.py +65 -0
  41. package/ciel_runtime_support/channel_message_policy.py +256 -0
  42. package/ciel_runtime_support/channel_message_prompt.py +305 -0
  43. package/ciel_runtime_support/channel_message_repository.py +234 -0
  44. package/ciel_runtime_support/channel_notification_projection.py +217 -0
  45. package/ciel_runtime_support/channel_panel.py +162 -0
  46. package/ciel_runtime_support/channel_pending_injection.py +209 -0
  47. package/ciel_runtime_support/channel_pending_poll.py +109 -0
  48. package/ciel_runtime_support/channel_probe_cache.py +433 -0
  49. package/ciel_runtime_support/channel_probe_report.py +101 -0
  50. package/ciel_runtime_support/channel_runtime_environment.py +181 -0
  51. package/ciel_runtime_support/channel_session_lifecycle.py +114 -0
  52. package/ciel_runtime_support/channel_session_repository.py +90 -0
  53. package/ciel_runtime_support/channel_terminal_dispatch.py +115 -0
  54. package/ciel_runtime_support/channel_terminal_input.py +277 -0
  55. package/ciel_runtime_support/channel_terminal_proxy.py +447 -0
  56. package/ciel_runtime_support/channel_tool_context.py +166 -0
  57. package/ciel_runtime_support/channel_transcript.py +414 -0
  58. package/ciel_runtime_support/channel_transcript_repository.py +96 -0
  59. package/ciel_runtime_support/channel_wake_claim_repository.py +126 -0
  60. package/ciel_runtime_support/channel_wake_delivery_repository.py +88 -0
  61. package/ciel_runtime_support/chat_files.py +138 -0
  62. package/ciel_runtime_support/chat_http_controller.py +235 -0
  63. package/ciel_runtime_support/claude_environment.py +375 -0
  64. package/ciel_runtime_support/claude_router.py +247 -193
  65. package/ciel_runtime_support/cli_dispatch.py +792 -0
  66. package/ciel_runtime_support/cli_parser.py +165 -0
  67. package/ciel_runtime_support/cli_usage.py +100 -0
  68. package/ciel_runtime_support/codex_app_server.py +20 -5
  69. package/ciel_runtime_support/codex_channel_sse_launch.py +87 -0
  70. package/ciel_runtime_support/codex_cli.py +42 -6
  71. package/ciel_runtime_support/codex_config.py +323 -0
  72. package/ciel_runtime_support/codex_launch_configuration.py +240 -0
  73. package/ciel_runtime_support/codex_launch_policy.py +66 -0
  74. package/ciel_runtime_support/codex_mcp_integration.py +195 -0
  75. package/ciel_runtime_support/codex_mcp_restore.py +304 -0
  76. package/ciel_runtime_support/codex_model_catalog.py +133 -0
  77. package/ciel_runtime_support/codex_process_lifecycle.py +271 -0
  78. package/ciel_runtime_support/codex_router.py +147 -1
  79. package/ciel_runtime_support/codex_session_repository.py +115 -0
  80. package/ciel_runtime_support/codex_session_selection.py +114 -0
  81. package/ciel_runtime_support/command_asset_installer.py +103 -0
  82. package/ciel_runtime_support/compatibility_probe.py +295 -0
  83. package/ciel_runtime_support/compatibility_protocol.py +251 -0
  84. package/ciel_runtime_support/compatibility_runtime.py +166 -0
  85. package/ciel_runtime_support/compatibility_test.py +370 -0
  86. package/ciel_runtime_support/config_migrations.py +307 -0
  87. package/ciel_runtime_support/config_repository.py +175 -0
  88. package/ciel_runtime_support/config_value_codec.py +64 -0
  89. package/ciel_runtime_support/configuration_cli.py +374 -0
  90. package/ciel_runtime_support/context_compaction.py +280 -0
  91. package/ciel_runtime_support/context_setup.py +208 -0
  92. package/ciel_runtime_support/context_summary_policy.py +392 -0
  93. package/ciel_runtime_support/credential_cli.py +104 -0
  94. package/ciel_runtime_support/credential_management.py +261 -0
  95. package/ciel_runtime_support/credentials.py +269 -0
  96. package/ciel_runtime_support/executable_discovery.py +141 -0
  97. package/ciel_runtime_support/github_copilot_oauth.py +335 -0
  98. package/ciel_runtime_support/github_copilot_oauth_runtime.py +213 -0
  99. package/ciel_runtime_support/header_forwarding.py +73 -0
  100. package/ciel_runtime_support/headless_config.py +221 -0
  101. package/ciel_runtime_support/http_response.py +129 -0
  102. package/ciel_runtime_support/install_diagnostics.py +149 -0
  103. package/ciel_runtime_support/kimi_identity.py +123 -0
  104. package/ciel_runtime_support/launch_diagnostics.py +204 -0
  105. package/ciel_runtime_support/launch_state.py +127 -0
  106. package/ciel_runtime_support/live_api_key_controller.py +58 -0
  107. package/ciel_runtime_support/llm_config_http.py +148 -0
  108. package/ciel_runtime_support/llm_option_config.py +259 -0
  109. package/ciel_runtime_support/llm_presentation_data.py +447 -0
  110. package/ciel_runtime_support/llm_presets.py +773 -0
  111. package/ciel_runtime_support/lm_studio_runtime.py +401 -0
  112. package/ciel_runtime_support/managed_mcp_config.py +144 -0
  113. package/ciel_runtime_support/managed_mcp_discovery.py +151 -0
  114. package/ciel_runtime_support/managed_service_cleanup.py +89 -0
  115. package/ciel_runtime_support/mcp_config_reader.py +230 -0
  116. package/ciel_runtime_support/mcp_http_proxy.py +607 -0
  117. package/ciel_runtime_support/mcp_inventory.py +59 -0
  118. package/ciel_runtime_support/mcp_notification_wait_policy.py +159 -0
  119. package/ciel_runtime_support/mcp_probe_codec.py +136 -0
  120. package/ciel_runtime_support/mcp_probe_transport.py +328 -0
  121. package/ciel_runtime_support/mcp_proxy_codec.py +345 -0
  122. package/ciel_runtime_support/mcp_proxy_config.py +107 -0
  123. package/ciel_runtime_support/mcp_proxy_notifications.py +261 -0
  124. package/ciel_runtime_support/mcp_proxy_process.py +560 -0
  125. package/ciel_runtime_support/mcp_split_proxy_http.py +165 -0
  126. package/ciel_runtime_support/mcp_stdio_probe.py +240 -0
  127. package/ciel_runtime_support/mcp_transport.py +146 -0
  128. package/ciel_runtime_support/model_cache_lifecycle.py +78 -0
  129. package/ciel_runtime_support/model_catalog_projection.py +61 -0
  130. package/ciel_runtime_support/model_context_hints.py +109 -0
  131. package/ciel_runtime_support/model_panel.py +147 -0
  132. package/ciel_runtime_support/model_registry_repository.py +231 -0
  133. package/ciel_runtime_support/npm_runtime.py +191 -0
  134. package/ciel_runtime_support/ollama_catalog.py +462 -0
  135. package/ciel_runtime_support/ollama_catalog_cli.py +41 -0
  136. package/ciel_runtime_support/ollama_catalog_repository.py +75 -0
  137. package/ciel_runtime_support/ollama_context_sync.py +87 -0
  138. package/ciel_runtime_support/ollama_forwarding.py +449 -0
  139. package/ciel_runtime_support/openai_chat_passthrough.py +67 -0
  140. package/ciel_runtime_support/openai_chat_router.py +64 -0
  141. package/ciel_runtime_support/openai_forwarding.py +194 -0
  142. package/ciel_runtime_support/openai_responses_router.py +291 -0
  143. package/ciel_runtime_support/openai_responses_stream.py +135 -0
  144. package/ciel_runtime_support/output_budget.py +89 -0
  145. package/ciel_runtime_support/package_lifecycle.py +217 -0
  146. package/ciel_runtime_support/plan_artifact_controller.py +104 -0
  147. package/ciel_runtime_support/prelaunch.py +959 -0
  148. package/ciel_runtime_support/prelaunch_launch_preference.py +75 -0
  149. package/ciel_runtime_support/prelaunch_panel_projection.py +396 -0
  150. package/ciel_runtime_support/prelaunch_terminal.py +764 -0
  151. package/ciel_runtime_support/process_control.py +708 -0
  152. package/ciel_runtime_support/prompt_compaction.py +322 -0
  153. package/ciel_runtime_support/prompt_injection.py +176 -0
  154. package/ciel_runtime_support/protocols/__init__.py +24 -0
  155. package/ciel_runtime_support/protocols/anthropic_content.py +29 -0
  156. package/ciel_runtime_support/protocols/anthropic_thinking_policy.py +336 -0
  157. package/ciel_runtime_support/protocols/chat_projection.py +315 -0
  158. package/ciel_runtime_support/protocols/conversation_policy.py +217 -0
  159. package/ciel_runtime_support/protocols/conversation_turn_policy.py +972 -0
  160. package/ciel_runtime_support/protocols/ollama_chat.py +111 -0
  161. package/ciel_runtime_support/protocols/ollama_response.py +231 -0
  162. package/ciel_runtime_support/protocols/openai_reasoning.py +75 -0
  163. package/ciel_runtime_support/protocols/openai_responses.py +271 -0
  164. package/ciel_runtime_support/protocols/pseudo_tool_history.py +248 -0
  165. package/ciel_runtime_support/protocols/tool_result_projection.py +112 -0
  166. package/ciel_runtime_support/provider_adapters.py +160 -0
  167. package/ciel_runtime_support/provider_catalog_sources.py +316 -0
  168. package/ciel_runtime_support/provider_choice.py +201 -0
  169. package/ciel_runtime_support/provider_compatibility.py +165 -0
  170. package/ciel_runtime_support/provider_config_mutations.py +361 -0
  171. package/ciel_runtime_support/provider_configuration_service.py +162 -0
  172. package/ciel_runtime_support/provider_context.py +308 -0
  173. package/ciel_runtime_support/provider_contract_projection.py +73 -0
  174. package/ciel_runtime_support/provider_descriptor.py +82 -0
  175. package/ciel_runtime_support/provider_endpoint_policy.py +130 -0
  176. package/ciel_runtime_support/provider_endpoint_probe.py +165 -0
  177. package/ciel_runtime_support/provider_launch_endpoint.py +109 -0
  178. package/ciel_runtime_support/provider_limits.py +457 -0
  179. package/ciel_runtime_support/provider_model_identity.py +139 -0
  180. package/ciel_runtime_support/provider_model_selection.py +431 -0
  181. package/ciel_runtime_support/provider_model_specs.py +142 -0
  182. package/ciel_runtime_support/provider_models.py +263 -0
  183. package/ciel_runtime_support/provider_network.py +176 -0
  184. package/ciel_runtime_support/provider_option_cli.py +238 -0
  185. package/ciel_runtime_support/provider_option_panel.py +275 -0
  186. package/ciel_runtime_support/provider_option_status.py +192 -0
  187. package/ciel_runtime_support/provider_policy.py +101 -0
  188. package/ciel_runtime_support/provider_query_policy.py +67 -0
  189. package/ciel_runtime_support/provider_readiness.py +112 -0
  190. package/ciel_runtime_support/provider_request_access.py +131 -0
  191. package/ciel_runtime_support/provider_request_builder.py +250 -0
  192. package/ciel_runtime_support/provider_responses_passthrough.py +81 -0
  193. package/ciel_runtime_support/provider_runtime_info.py +113 -0
  194. package/ciel_runtime_support/provider_runtime_modes.py +150 -0
  195. package/ciel_runtime_support/provider_sampling_policy.py +46 -0
  196. package/ciel_runtime_support/provider_status.py +145 -0
  197. package/ciel_runtime_support/provider_timeout_policy.py +184 -0
  198. package/ciel_runtime_support/provider_tool_policy.py +145 -0
  199. package/ciel_runtime_support/providers/__init__.py +65 -0
  200. package/ciel_runtime_support/providers/anthropic.py +160 -0
  201. package/ciel_runtime_support/providers/anthropic_catalog.py +119 -0
  202. package/ciel_runtime_support/providers/base.py +232 -0
  203. package/ciel_runtime_support/providers/catalog.py +326 -0
  204. package/ciel_runtime_support/providers/cloud.py +194 -0
  205. package/ciel_runtime_support/providers/constants.py +54 -0
  206. package/ciel_runtime_support/providers/deepseek.py +115 -0
  207. package/ciel_runtime_support/providers/fireworks.py +158 -0
  208. package/ciel_runtime_support/providers/github_copilot_oauth.py +199 -0
  209. package/ciel_runtime_support/providers/kimi.py +297 -0
  210. package/ciel_runtime_support/providers/lm_studio.py +76 -0
  211. package/ciel_runtime_support/providers/meta.py +257 -0
  212. package/ciel_runtime_support/providers/native.py +194 -0
  213. package/ciel_runtime_support/providers/nim.py +69 -0
  214. package/ciel_runtime_support/providers/nvidia.py +158 -0
  215. package/ciel_runtime_support/providers/nvidia_runtime.py +285 -0
  216. package/ciel_runtime_support/providers/ollama.py +181 -0
  217. package/ciel_runtime_support/providers/ollama_context.py +195 -0
  218. package/ciel_runtime_support/providers/ollama_runtime.py +213 -0
  219. package/ciel_runtime_support/providers/opencode.py +220 -0
  220. package/ciel_runtime_support/providers/opencode_go.py +37 -0
  221. package/ciel_runtime_support/providers/openrouter.py +57 -0
  222. package/ciel_runtime_support/providers/vllm.py +65 -0
  223. package/ciel_runtime_support/providers/zai.py +119 -0
  224. package/ciel_runtime_support/pseudo_tool_parser.py +115 -0
  225. package/ciel_runtime_support/rate_limit_policy.py +117 -0
  226. package/ciel_runtime_support/rate_limit_repository.py +154 -0
  227. package/ciel_runtime_support/registry.py +46 -0
  228. package/ciel_runtime_support/request_shortcuts.py +253 -0
  229. package/ciel_runtime_support/request_trace.py +323 -0
  230. package/ciel_runtime_support/response_collection.py +209 -0
  231. package/ciel_runtime_support/router_access.py +238 -0
  232. package/ciel_runtime_support/router_client_lifecycle.py +366 -0
  233. package/ciel_runtime_support/router_health_policy.py +101 -0
  234. package/ciel_runtime_support/router_http.py +513 -0
  235. package/ciel_runtime_support/router_process_lifecycle.py +401 -0
  236. package/ciel_runtime_support/router_rate_limit_service.py +285 -0
  237. package/ciel_runtime_support/router_server_runtime.py +103 -0
  238. package/ciel_runtime_support/router_shortcuts.py +201 -0
  239. package/ciel_runtime_support/routing_fallback.py +73 -0
  240. package/ciel_runtime_support/runtime_activity_repository.py +143 -0
  241. package/ciel_runtime_support/runtime_adapters.py +104 -0
  242. package/ciel_runtime_support/runtime_command_factory.py +73 -0
  243. package/ciel_runtime_support/runtime_compatibility.py +50 -0
  244. package/ciel_runtime_support/runtime_constants.py +178 -0
  245. package/ciel_runtime_support/runtime_launch.py +1602 -0
  246. package/ciel_runtime_support/runtime_llm_options.py +312 -0
  247. package/ciel_runtime_support/runtime_logging.py +161 -0
  248. package/ciel_runtime_support/runtime_paths.py +157 -0
  249. package/ciel_runtime_support/runtime_restart.py +84 -0
  250. package/ciel_runtime_support/runtime_upgrade.py +149 -0
  251. package/ciel_runtime_support/secure_json_repository.py +55 -0
  252. package/ciel_runtime_support/session_import.py +356 -0
  253. package/ciel_runtime_support/settings_repository.py +8 -0
  254. package/ciel_runtime_support/slash_command_assets.py +211 -0
  255. package/ciel_runtime_support/sse_stream.py +57 -0
  256. package/ciel_runtime_support/sse_trace.py +225 -0
  257. package/ciel_runtime_support/statusline_script.py +593 -0
  258. package/ciel_runtime_support/statusline_settings.py +53 -0
  259. package/ciel_runtime_support/stream_chunk_policy.py +18 -0
  260. package/ciel_runtime_support/streaming_anthropic.py +1955 -0
  261. package/ciel_runtime_support/synthetic_tool_policy.py +105 -0
  262. package/ciel_runtime_support/terminal_platform_io.py +127 -0
  263. package/ciel_runtime_support/timeout_profile.py +196 -0
  264. package/ciel_runtime_support/tool_dialects.py +85 -0
  265. package/ciel_runtime_support/tool_exposure_policy.py +63 -0
  266. package/ciel_runtime_support/tool_guard_hooks.py +218 -0
  267. package/ciel_runtime_support/tool_request_projection.py +96 -0
  268. package/ciel_runtime_support/tool_schema.py +483 -0
  269. package/ciel_runtime_support/tool_side_effect_dedupe.py +95 -0
  270. package/ciel_runtime_support/ui_text.py +266 -0
  271. package/ciel_runtime_support/upstream_error_policy.py +104 -0
  272. package/ciel_runtime_support/upstream_retry.py +419 -0
  273. package/ciel_runtime_support/upstream_stream_io.py +106 -0
  274. package/ciel_runtime_support/usage_events.py +96 -0
  275. package/ciel_runtime_support/visible_stream_filters.py +130 -0
  276. package/ciel_runtime_support/web_endpoints.py +447 -0
  277. package/ciel_runtime_support/web_ui.py +915 -0
  278. package/ciel_runtime_support/web_ui_controller.py +189 -0
  279. package/ciel_runtime_support/windows_console_input.py +137 -0
  280. package/ciel_runtime_support/windows_console_mode.py +112 -0
  281. package/docs/Architecture.md +54 -0
  282. package/docs/Configuration.md +17 -0
  283. package/docs/Module-Map.md +1093 -26
  284. package/docs/Providers.md +46 -1
  285. package/docs/adr/0001-runtime-bounded-contexts.md +295 -0
  286. package/npm-bin/run-ciel-runtime.js +22 -2
  287. package/package.json +9 -2
@@ -0,0 +1,1602 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import subprocess
5
+ import uuid
6
+ from pathlib import Path
7
+ from typing import Any, Callable
8
+ from dataclasses import dataclass
9
+
10
+ from ciel_runtime_support.channel_message_prompt import NATIVE_ROUTER_CHANNEL_NAMES
11
+ from ciel_runtime_support.runtime_constants import (
12
+ CLAUDE_SERVER_SIDE_WEB_TOOLS,
13
+ CODEX_RUNTIME_API_KEY_ENV,
14
+ PRELAUNCH_CANCEL,
15
+ PRELAUNCH_LAUNCH_AGY,
16
+ PRELAUNCH_LAUNCH_CLAUDE,
17
+ PRELAUNCH_LAUNCH_CODEX,
18
+ PRELAUNCH_LAUNCH_CODEX_APP_SERVER,
19
+ ROUTED_COMPAT_PROMPT,
20
+ )
21
+ from ciel_runtime_support.runtime_paths import CONFIG_DIR, LOG_PATH
22
+ from ciel_runtime_support.web_endpoints import web_backend_settings
23
+
24
+
25
+ CLAUDE_CODE_GENERATED_GREEDY_OPTIONS = frozenset(
26
+ {"--mcp-config", "--dangerously-load-development-channels"}
27
+ )
28
+
29
+
30
+ def web_backend_start_requested(config: dict[str, Any]) -> bool:
31
+ if str(os.environ.get("CIEL_RUNTIME_WEB_START_REQUESTED") or "").lower() in {
32
+ "1", "true", "yes", "on"
33
+ }:
34
+ return True
35
+ return web_backend_settings(config).enabled
36
+
37
+
38
+ @dataclass(frozen=True, slots=True)
39
+ class ClaudeLaunchConstants:
40
+ CLAUDE_SERVER_SIDE_WEB_TOOLS: Any
41
+ LOG_PATH: Any
42
+ PRELAUNCH_CANCEL: Any
43
+ PRELAUNCH_LAUNCH_AGY: Any
44
+ PRELAUNCH_LAUNCH_CLAUDE: Any
45
+ PRELAUNCH_LAUNCH_CODEX: Any
46
+ PRELAUNCH_LAUNCH_CODEX_APP_SERVER: Any
47
+ ROUTED_COMPAT_PROMPT: Any
48
+ _NATIVE_ROUTER_CHANNEL_NAMES: Any
49
+
50
+
51
+ def build_default_claude_launch_constants() -> ClaudeLaunchConstants:
52
+ """Build immutable Claude launch policy constants at their owner boundary."""
53
+ return ClaudeLaunchConstants(
54
+ CLAUDE_SERVER_SIDE_WEB_TOOLS=CLAUDE_SERVER_SIDE_WEB_TOOLS,
55
+ LOG_PATH=LOG_PATH,
56
+ PRELAUNCH_CANCEL=PRELAUNCH_CANCEL,
57
+ PRELAUNCH_LAUNCH_AGY=PRELAUNCH_LAUNCH_AGY,
58
+ PRELAUNCH_LAUNCH_CLAUDE=PRELAUNCH_LAUNCH_CLAUDE,
59
+ PRELAUNCH_LAUNCH_CODEX=PRELAUNCH_LAUNCH_CODEX,
60
+ PRELAUNCH_LAUNCH_CODEX_APP_SERVER=PRELAUNCH_LAUNCH_CODEX_APP_SERVER,
61
+ ROUTED_COMPAT_PROMPT=ROUTED_COMPAT_PROMPT,
62
+ _NATIVE_ROUTER_CHANNEL_NAMES=NATIVE_ROUTER_CHANNEL_NAMES,
63
+ )
64
+
65
+
66
+ @dataclass(frozen=True, slots=True)
67
+ class ClaudeLaunchProcess:
68
+ _log_claude_command_for_diagnostics: Callable[..., Any]
69
+ _subprocess_call_capturing_stderr: Callable[..., Any]
70
+ env_bool: Callable[..., Any]
71
+ env_vars: Callable[..., Any]
72
+ file_size_or_zero: Callable[..., Any]
73
+ path_with_ciel_runtime_user_dirs: Callable[..., Any]
74
+ print_routed_claude_exit_diagnostics: Callable[..., Any]
75
+ subprocess_call_with_channel_wake_proxy: Callable[..., Any]
76
+
77
+
78
+ @dataclass(frozen=True, slots=True)
79
+ class ClaudeLaunchInstallation:
80
+ find_executable: Callable[..., Any]
81
+ install_ciel_runtime_slash_commands: Callable[..., Any]
82
+ install_ciel_runtime_statusline: Callable[..., Any]
83
+ install_claude_code_if_missing: Callable[..., Any]
84
+ install_tool_guard_hooks: Callable[..., Any]
85
+ disable_ciel_runtime_slash_commands_for_native: Callable[..., Any]
86
+ launch_readiness_errors: Callable[..., Any]
87
+ warn_if_multiple_ciel_runtime_installs: Callable[..., Any]
88
+
89
+
90
+ @dataclass(frozen=True, slots=True)
91
+ class ClaudeLaunchDispatch:
92
+ launch_agy: Callable[..., Any]
93
+ launch_codex: Callable[..., Any]
94
+ launch_codex_app_server: Callable[..., Any]
95
+ materialize_runtime_command: Callable[..., Any]
96
+ run_ciel_runtime_update_check: Callable[..., Any]
97
+ run_claude_update_check: Callable[..., Any]
98
+ run_prelaunch_menu: Callable[..., Any]
99
+ claude_launch_enabled_for_provider: Callable[..., Any]
100
+
101
+
102
+ @dataclass(frozen=True, slots=True)
103
+ class ClaudeLaunchConfig:
104
+ load_config: Callable[..., Any]
105
+ save_config: Callable[..., Any]
106
+ get_current_provider: Callable[..., Any]
107
+ ensure_current_model_from_provider_list: Callable[..., Any]
108
+ ensure_model_cache_for_launch: Callable[..., Any]
109
+ apply_launch_endpoint_policy: Callable[..., Any]
110
+ provider_menu_label: Callable[..., Any]
111
+ launch_mode_name: Callable[..., Any]
112
+ current_launch_cwd_key: Callable[..., Any]
113
+
114
+
115
+ @dataclass(frozen=True, slots=True)
116
+ class ClaudeLaunchRouting:
117
+ anthropic_routed_enabled: Callable[..., Any]
118
+ direct_native_anthropic_enabled: Callable[..., Any]
119
+ cleanup_managed_services_for_provider: Callable[..., Any]
120
+ ensure_managed_router_running_for_client: Callable[..., Any]
121
+ reset_zai_mcp_config_if_inactive: Callable[..., Any]
122
+ router_health_summary: Callable[..., Any]
123
+ router_log: Callable[..., Any]
124
+ start_router_if_needed: Callable[..., Any]
125
+ run_with_router_lifetime: Callable[..., Any]
126
+ record_launch_state_for_cwd: Callable[..., Any]
127
+
128
+
129
+ @dataclass(frozen=True, slots=True)
130
+ class ClaudeLaunchPolicy:
131
+ append_claude_code_runtime_settings_args: Callable[..., Any]
132
+ claude_supports_permission_mode_arg: Callable[..., Any]
133
+ has_noninteractive_claude_args: Callable[..., Any]
134
+ has_passthrough_option: Callable[..., Any]
135
+ should_append_compat_prompt: Callable[..., Any]
136
+ should_attach_web_search: Callable[..., Any]
137
+ should_disallow_claude_server_side_web_tools: Callable[..., Any]
138
+ should_fork_native_session_after_mode_switch: Callable[..., Any]
139
+ should_insert_passthrough_option_boundary: Callable[..., Any]
140
+ strip_mcp_config_passthrough: Callable[..., Any]
141
+
142
+
143
+ @dataclass(frozen=True, slots=True)
144
+ class ClaudeLaunchChannelDiscovery:
145
+ auto_import_passthrough_channels: Callable[..., Any]
146
+ cached_channel_capable_server_names: Callable[..., Any]
147
+ cached_channel_source_paths_for_specs: Callable[..., Any]
148
+ channel_candidate_server_names_for_launch: Callable[..., Any]
149
+ channel_specs_for_launch: Callable[..., Any]
150
+ claude_channels_requested: Callable[..., Any]
151
+ claude_code_channels_auth_available: Callable[..., Any]
152
+ ensure_channel_probe_cache_for_launch: Callable[..., Any]
153
+ external_mcp_channel_server_names_from_configs: Callable[..., Any]
154
+ read_channel_probe_cache: Callable[..., Any]
155
+
156
+
157
+ @dataclass(frozen=True, slots=True)
158
+ class ClaudeLaunchChannelDelivery:
159
+ auto_start_sse_channels_from_mcp_configs: Callable[..., Any]
160
+ claude_channel_args: Callable[..., Any]
161
+ native_channel_passthrough_requested: Callable[..., Any]
162
+ normalize_channel_passthrough: Callable[..., Any]
163
+ prepare_channel_llm_delivery_for_launch: Callable[..., Any]
164
+ should_launch_process_start_channel_sse: Callable[..., Any]
165
+ should_use_channel_llm_delivery: Callable[..., Any]
166
+ should_use_channel_stdin_proxy: Callable[..., Any]
167
+ should_use_native_channel_bridge: Callable[..., Any]
168
+ write_channel_mcp_config: Callable[..., Any]
169
+
170
+
171
+ @dataclass(frozen=True, slots=True)
172
+ class ClaudeLaunchMcpConfig:
173
+ write_duckduckgo_mcp_config: Callable[..., Any]
174
+ write_mcp_proxy_config: Callable[..., Any]
175
+ write_native_mcp_config_from_discovery: Callable[..., Any]
176
+ write_zai_mcp_config: Callable[..., Any]
177
+
178
+
179
+ @dataclass(frozen=True, slots=True)
180
+ class ClaudeLaunchServices:
181
+ constants: ClaudeLaunchConstants
182
+ process: ClaudeLaunchProcess
183
+ installation: ClaudeLaunchInstallation
184
+ dispatch: ClaudeLaunchDispatch
185
+ config: ClaudeLaunchConfig
186
+ routing: ClaudeLaunchRouting
187
+ policy: ClaudeLaunchPolicy
188
+ channel_discovery: ClaudeLaunchChannelDiscovery
189
+ channel_delivery: ClaudeLaunchChannelDelivery
190
+ mcp_config: ClaudeLaunchMcpConfig
191
+
192
+
193
+ def run_claude(
194
+ passthrough: list[str],
195
+ skip_menu: bool = False,
196
+ force_menu: bool = False,
197
+ web_search_override: bool | None = None,
198
+ update_check: bool = True,
199
+ self_update_check: bool = True,
200
+ *,
201
+ services: ClaudeLaunchServices,
202
+ ) -> int:
203
+ CLAUDE_SERVER_SIDE_WEB_TOOLS = services.constants.CLAUDE_SERVER_SIDE_WEB_TOOLS
204
+ LOG_PATH = services.constants.LOG_PATH
205
+ PRELAUNCH_CANCEL = services.constants.PRELAUNCH_CANCEL
206
+ PRELAUNCH_LAUNCH_AGY = services.constants.PRELAUNCH_LAUNCH_AGY
207
+ PRELAUNCH_LAUNCH_CLAUDE = services.constants.PRELAUNCH_LAUNCH_CLAUDE
208
+ PRELAUNCH_LAUNCH_CODEX = services.constants.PRELAUNCH_LAUNCH_CODEX
209
+ PRELAUNCH_LAUNCH_CODEX_APP_SERVER = services.constants.PRELAUNCH_LAUNCH_CODEX_APP_SERVER
210
+ ROUTED_COMPAT_PROMPT = services.constants.ROUTED_COMPAT_PROMPT
211
+ _NATIVE_ROUTER_CHANNEL_NAMES = services.constants._NATIVE_ROUTER_CHANNEL_NAMES
212
+ _log_claude_command_for_diagnostics = services.process._log_claude_command_for_diagnostics
213
+ _subprocess_call_capturing_stderr = services.process._subprocess_call_capturing_stderr
214
+ anthropic_routed_enabled = services.routing.anthropic_routed_enabled
215
+ append_claude_code_runtime_settings_args = services.policy.append_claude_code_runtime_settings_args
216
+ apply_launch_endpoint_policy = services.config.apply_launch_endpoint_policy
217
+ auto_import_passthrough_channels = services.channel_discovery.auto_import_passthrough_channels
218
+ auto_start_sse_channels_from_mcp_configs = services.channel_delivery.auto_start_sse_channels_from_mcp_configs
219
+ cached_channel_capable_server_names = services.channel_discovery.cached_channel_capable_server_names
220
+ cached_channel_source_paths_for_specs = services.channel_discovery.cached_channel_source_paths_for_specs
221
+ channel_candidate_server_names_for_launch = services.channel_discovery.channel_candidate_server_names_for_launch
222
+ channel_specs_for_launch = services.channel_discovery.channel_specs_for_launch
223
+ claude_channel_args = services.channel_delivery.claude_channel_args
224
+ claude_channels_requested = services.channel_discovery.claude_channels_requested
225
+ claude_code_channels_auth_available = services.channel_discovery.claude_code_channels_auth_available
226
+ claude_launch_enabled_for_provider = services.dispatch.claude_launch_enabled_for_provider
227
+ claude_supports_permission_mode_arg = services.policy.claude_supports_permission_mode_arg
228
+ cleanup_managed_services_for_provider = services.routing.cleanup_managed_services_for_provider
229
+ current_launch_cwd_key = services.config.current_launch_cwd_key
230
+ direct_native_anthropic_enabled = services.routing.direct_native_anthropic_enabled
231
+ disable_ciel_runtime_slash_commands_for_native = services.installation.disable_ciel_runtime_slash_commands_for_native
232
+ ensure_channel_probe_cache_for_launch = services.channel_discovery.ensure_channel_probe_cache_for_launch
233
+ ensure_current_model_from_provider_list = services.config.ensure_current_model_from_provider_list
234
+ ensure_managed_router_running_for_client = services.routing.ensure_managed_router_running_for_client
235
+ ensure_model_cache_for_launch = services.config.ensure_model_cache_for_launch
236
+ env_bool = services.process.env_bool
237
+ env_vars = services.process.env_vars
238
+ external_mcp_channel_server_names_from_configs = services.channel_discovery.external_mcp_channel_server_names_from_configs
239
+ file_size_or_zero = services.process.file_size_or_zero
240
+ find_executable = services.installation.find_executable
241
+ get_current_provider = services.config.get_current_provider
242
+ has_noninteractive_claude_args = services.policy.has_noninteractive_claude_args
243
+ has_passthrough_option = services.policy.has_passthrough_option
244
+ install_ciel_runtime_slash_commands = services.installation.install_ciel_runtime_slash_commands
245
+ install_ciel_runtime_statusline = services.installation.install_ciel_runtime_statusline
246
+ install_claude_code_if_missing = services.installation.install_claude_code_if_missing
247
+ install_tool_guard_hooks = services.installation.install_tool_guard_hooks
248
+ launch_agy = services.dispatch.launch_agy
249
+ launch_codex = services.dispatch.launch_codex
250
+ launch_codex_app_server = services.dispatch.launch_codex_app_server
251
+ launch_mode_name = services.config.launch_mode_name
252
+ launch_readiness_errors = services.installation.launch_readiness_errors
253
+ load_config = services.config.load_config
254
+ materialize_runtime_command = services.dispatch.materialize_runtime_command
255
+ native_channel_passthrough_requested = services.channel_delivery.native_channel_passthrough_requested
256
+ normalize_channel_passthrough = services.channel_delivery.normalize_channel_passthrough
257
+ path_with_ciel_runtime_user_dirs = services.process.path_with_ciel_runtime_user_dirs
258
+ prepare_channel_llm_delivery_for_launch = services.channel_delivery.prepare_channel_llm_delivery_for_launch
259
+ print_routed_claude_exit_diagnostics = services.process.print_routed_claude_exit_diagnostics
260
+ provider_menu_label = services.config.provider_menu_label
261
+ read_channel_probe_cache = services.channel_discovery.read_channel_probe_cache
262
+ record_launch_state_for_cwd = services.routing.record_launch_state_for_cwd
263
+ reset_zai_mcp_config_if_inactive = services.routing.reset_zai_mcp_config_if_inactive
264
+ router_health_summary = services.routing.router_health_summary
265
+ router_log = services.routing.router_log
266
+ run_ciel_runtime_update_check = services.dispatch.run_ciel_runtime_update_check
267
+ run_claude_update_check = services.dispatch.run_claude_update_check
268
+ run_prelaunch_menu = services.dispatch.run_prelaunch_menu
269
+ run_with_router_lifetime = services.routing.run_with_router_lifetime
270
+ save_config = services.config.save_config
271
+ should_append_compat_prompt = services.policy.should_append_compat_prompt
272
+ should_attach_web_search = services.policy.should_attach_web_search
273
+ should_disallow_claude_server_side_web_tools = services.policy.should_disallow_claude_server_side_web_tools
274
+ should_fork_native_session_after_mode_switch = services.policy.should_fork_native_session_after_mode_switch
275
+ should_insert_passthrough_option_boundary = services.policy.should_insert_passthrough_option_boundary
276
+ should_launch_process_start_channel_sse = services.channel_delivery.should_launch_process_start_channel_sse
277
+ should_use_channel_llm_delivery = services.channel_delivery.should_use_channel_llm_delivery
278
+ should_use_channel_stdin_proxy = services.channel_delivery.should_use_channel_stdin_proxy
279
+ should_use_native_channel_bridge = services.channel_delivery.should_use_native_channel_bridge
280
+ start_router_if_needed = services.routing.start_router_if_needed
281
+ strip_mcp_config_passthrough = services.policy.strip_mcp_config_passthrough
282
+ subprocess_call_with_channel_wake_proxy = services.process.subprocess_call_with_channel_wake_proxy
283
+ warn_if_multiple_ciel_runtime_installs = services.installation.warn_if_multiple_ciel_runtime_installs
284
+ write_channel_mcp_config = services.channel_delivery.write_channel_mcp_config
285
+ write_duckduckgo_mcp_config = services.mcp_config.write_duckduckgo_mcp_config
286
+ write_mcp_proxy_config = services.mcp_config.write_mcp_proxy_config
287
+ write_native_mcp_config_from_discovery = services.mcp_config.write_native_mcp_config_from_discovery
288
+ write_zai_mcp_config = services.mcp_config.write_zai_mcp_config
289
+ if has_noninteractive_claude_args(passthrough):
290
+ self_update_check = False
291
+ warn_if_multiple_ciel_runtime_installs()
292
+ run_ciel_runtime_update_check(enabled=self_update_check)
293
+ auto_import_passthrough_channels(passthrough)
294
+ rc = run_prelaunch_menu(passthrough, skip_menu=skip_menu, force_menu=force_menu)
295
+ if rc == PRELAUNCH_LAUNCH_CODEX:
296
+ return launch_codex(
297
+ passthrough,
298
+ skip_menu=True,
299
+ force_menu=False,
300
+ update_check=update_check,
301
+ self_update_check=False,
302
+ )
303
+ if rc == PRELAUNCH_LAUNCH_AGY:
304
+ return launch_agy(
305
+ passthrough,
306
+ skip_menu=True,
307
+ force_menu=False,
308
+ update_check=update_check,
309
+ self_update_check=False,
310
+ )
311
+ if rc == PRELAUNCH_LAUNCH_CODEX_APP_SERVER:
312
+ return launch_codex_app_server(
313
+ passthrough,
314
+ skip_menu=True,
315
+ force_menu=False,
316
+ update_check=update_check,
317
+ self_update_check=False,
318
+ )
319
+ if rc == PRELAUNCH_CANCEL:
320
+ return 0
321
+ if rc not in (0, PRELAUNCH_LAUNCH_CLAUDE):
322
+ return rc
323
+ cfg = load_config()
324
+ provider, pcfg = get_current_provider(cfg)
325
+ for line in apply_launch_endpoint_policy(cfg, "claude"):
326
+ print(line, flush=True)
327
+ provider, pcfg = get_current_provider(cfg)
328
+ if not claude_launch_enabled_for_provider(provider):
329
+ print(f"Ciel Runtime launch blocked: Launch Claude Code is disabled while {provider_menu_label(provider, pcfg)} provider is selected.", flush=True)
330
+ print("Use the matching Launch menu item or --ca-runtime agy|codex|codex-app-server.", flush=True)
331
+ return 2
332
+ blockers = launch_readiness_errors(cfg)
333
+ if blockers:
334
+ print("Ciel Runtime launch blocked:", flush=True)
335
+ for line in blockers:
336
+ print(f"- {line}", flush=True)
337
+ return 2
338
+ use_native_anthropic = direct_native_anthropic_enabled(provider, pcfg)
339
+ use_router_mode = not use_native_anthropic
340
+ launch_cwd_key = current_launch_cwd_key()
341
+ fork_native_session, previous_launch_mode = should_fork_native_session_after_mode_switch(
342
+ provider,
343
+ pcfg,
344
+ use_native_anthropic,
345
+ passthrough,
346
+ launch_cwd_key,
347
+ )
348
+ cleanup_managed_services_for_provider(provider, pcfg, cfg, quiet=True)
349
+ env = os.environ.copy()
350
+ env["PATH"] = path_with_ciel_runtime_user_dirs(env)
351
+ launch_passthrough = normalize_channel_passthrough(passthrough)
352
+ native_channel_bridge = should_use_native_channel_bridge(use_router_mode, cfg, launch_passthrough)
353
+ stdin_channel_proxy = should_use_channel_stdin_proxy(use_router_mode, launch_passthrough, cfg)
354
+ llm_channel_delivery = should_use_channel_llm_delivery(use_router_mode, launch_passthrough, cfg)
355
+ native_auto_channel_specs: list[str] = []
356
+ if use_native_anthropic and not native_channel_bridge and not native_channel_passthrough_requested(launch_passthrough):
357
+ try:
358
+ auto_channel_names = external_mcp_channel_server_names_from_configs(launch_passthrough)
359
+ native_auto_channel_specs = [f"server:{name}" for name in auto_channel_names]
360
+ if native_auto_channel_specs:
361
+ router_log(
362
+ "INFO",
363
+ "channel_native_auto_specs servers=%s" % ",".join(auto_channel_names),
364
+ )
365
+ except Exception as exc:
366
+ router_log("WARN", f"channel_native_auto_probe_failed error={type(exc).__name__}: {exc}")
367
+ manage_router_lifetime = False
368
+ if use_router_mode or llm_channel_delivery:
369
+ manage_router_lifetime = bool(start_router_if_needed())
370
+ elif web_backend_start_requested(cfg):
371
+ manage_router_lifetime = bool(
372
+ start_router_if_needed(replace_active_clients=False)
373
+ )
374
+ if not use_native_anthropic:
375
+ ensure_model_cache_for_launch(provider, pcfg)
376
+ selected, selection_lines = ensure_current_model_from_provider_list(provider, pcfg)
377
+ if selection_lines:
378
+ for line in selection_lines:
379
+ print(line)
380
+ save_config(cfg)
381
+ if not selected:
382
+ raise RuntimeError(
383
+ f"No concrete model is selected for provider {provider}; choose a model from the provider model list before launching Claude Code."
384
+ )
385
+ launch_env = env_vars(cfg)
386
+ if claude_channels_requested(cfg, launch_passthrough) or native_channel_bridge or llm_channel_delivery or native_auto_channel_specs:
387
+ env.pop("CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS", None)
388
+ launch_env.pop("CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS", None)
389
+ if use_native_anthropic:
390
+ # Claude Native guarantee — strip every env var ciel-runtime (or a
391
+ # prior ciel-runtime session) might have left behind that would change
392
+ # Claude Code's default model selection, backend, advisor flow, or
393
+ # other behavior. See env_vars() docstring for the contract.
394
+ for key in (
395
+ "ANTHROPIC_BASE_URL",
396
+ "ANTHROPIC_MODEL",
397
+ "ANTHROPIC_CUSTOM_MODEL_OPTION",
398
+ "ANTHROPIC_CUSTOM_MODEL_OPTION_SUPPORTED_CAPABILITIES",
399
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL",
400
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL_SUPPORTS",
401
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL_SUPPORTED_CAPABILITIES",
402
+ "ANTHROPIC_DEFAULT_OPUS_MODEL",
403
+ "ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTS",
404
+ "ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES",
405
+ "ANTHROPIC_DEFAULT_SONNET_MODEL",
406
+ "ANTHROPIC_DEFAULT_SONNET_MODEL_SUPPORTS",
407
+ "ANTHROPIC_DEFAULT_SONNET_MODEL_SUPPORTED_CAPABILITIES",
408
+ "CLAUDE_CODE_SUBAGENT_MODEL",
409
+ "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY",
410
+ "CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS",
411
+ "CLAUDE_CODE_MAX_OUTPUT_TOKENS",
412
+ "CLAUDE_CODE_AUTO_COMPACT_WINDOW",
413
+ "CLAUDE_CODE_MAX_CONTEXT_TOKENS",
414
+ "CLAUDE_CODE_EFFORT_LEVEL",
415
+ "CLAUDE_CODE_DISABLE_TERMINAL_TITLE",
416
+ "CLAUDE_CODE_ATTRIBUTION_HEADER",
417
+ "CIEL_RUNTIME_ADVISOR_MODEL",
418
+ "CIEL_RUNTIME_BYPASS_PERMISSIONS",
419
+ "CIEL_RUNTIME_MODEL_ALIAS",
420
+ ):
421
+ env.pop(key, None)
422
+ launch_env.pop(key, None)
423
+ if "ANTHROPIC_API_KEY" in launch_env:
424
+ env.pop("ANTHROPIC_AUTH_TOKEN", None)
425
+ router_log(
426
+ "INFO",
427
+ "claude_native_launch model=<defer-to-claude-code> advisor=off backend=<default-anthropic>",
428
+ )
429
+ disable_ciel_runtime_slash_commands_for_native()
430
+ elif anthropic_routed_enabled(provider, pcfg):
431
+ router_log(
432
+ "INFO",
433
+ "claude_anthropic_routed_launch backend=ciel-runtime-router upstream=anthropic",
434
+ )
435
+ env.update(launch_env)
436
+ if not use_native_anthropic:
437
+ preserve_anthropic_auth = anthropic_routed_enabled(provider, pcfg)
438
+ for key in ("ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"):
439
+ if key not in launch_env and not preserve_anthropic_auth:
440
+ env.pop(key, None)
441
+ install_ciel_runtime_slash_commands(include_advisor=provider != "anthropic")
442
+ install_tool_guard_hooks()
443
+ install_ciel_runtime_statusline()
444
+ claude = install_claude_code_if_missing()
445
+ if not claude:
446
+ raise RuntimeError(
447
+ "claude executable was not found in PATH or the Ciel Runtime user bin directories, "
448
+ "and automatic install of @anthropic-ai/claude-code did not make it available"
449
+ )
450
+ updated_claude = run_claude_update_check(claude, enabled=update_check)
451
+ if isinstance(updated_claude, str) and updated_claude:
452
+ claude = updated_claude
453
+ claude = find_executable("claude") or claude
454
+ if native_channel_bridge or native_auto_channel_specs:
455
+ auth_ok, auth_reason = claude_code_channels_auth_available(claude)
456
+ if not auth_ok:
457
+ if native_channel_bridge:
458
+ router_log("WARN", f"channel_native_unavailable_fallback reason={auth_reason} delivery=llm")
459
+ native_channel_bridge = False
460
+ llm_channel_delivery = True
461
+ else:
462
+ router_log("WARN", f"channel_native_auto_disabled reason={auth_reason}")
463
+ native_auto_channel_specs = []
464
+ extra_args: list[str] = []
465
+ mcp_config_paths: list[str] = []
466
+ reset_zai_mcp_config_if_inactive(provider)
467
+ zai_mcp_config = write_zai_mcp_config(provider, pcfg)
468
+ if zai_mcp_config:
469
+ mcp_config_paths.append(str(zai_mcp_config))
470
+ if should_attach_web_search(provider, cfg, web_search_override):
471
+ mcp_config_paths.append(str(write_duckduckgo_mcp_config(cfg)))
472
+ if llm_channel_delivery:
473
+ mcp_config_paths.append(str(write_channel_mcp_config()))
474
+ native_direct_mcp_config_paths: list[str] = []
475
+ if use_native_anthropic:
476
+ native_mcp_config = write_native_mcp_config_from_discovery(launch_passthrough)
477
+ if native_mcp_config:
478
+ native_direct_mcp_config_paths = [str(native_mcp_config)]
479
+ detected_channel_specs: list[str] = []
480
+ detected_channel_capable_names: list[str] = []
481
+ channel_probe_source_paths: list[Path] = []
482
+ if stdin_channel_proxy or llm_channel_delivery:
483
+ try:
484
+ candidate_channel_names = channel_candidate_server_names_for_launch(cfg, launch_passthrough)
485
+ ensure_channel_probe_cache_for_launch(cfg, launch_passthrough)
486
+ capable_names = cached_channel_capable_server_names()
487
+ capable_name_set = set(capable_names)
488
+ detected_channel_capable_names = [
489
+ name for name in candidate_channel_names
490
+ if name in capable_name_set and name.strip().lower() not in _NATIVE_ROUTER_CHANNEL_NAMES
491
+ ]
492
+ detected_channel_specs = [f"server:{name}" for name in detected_channel_capable_names]
493
+ channel_launch_specs = channel_specs_for_launch(cfg, launch_passthrough, detected_channel_specs)
494
+ channel_probe_source_paths = cached_channel_source_paths_for_specs(channel_launch_specs)
495
+ if channel_probe_source_paths:
496
+ mcp_config_paths.extend(str(path) for path in channel_probe_source_paths)
497
+ cache_age = read_channel_probe_cache().get("probed_at") or 0
498
+ router_log(
499
+ "INFO",
500
+ "channel_probe_loaded source=cache cache_age_ts=%d count=%d servers=%s sources=%s"
501
+ % (
502
+ int(cache_age),
503
+ len(detected_channel_capable_names),
504
+ ",".join(detected_channel_capable_names) or "-",
505
+ ",".join(str(path) for path in channel_probe_source_paths) or "-",
506
+ ),
507
+ )
508
+ except Exception as exc:
509
+ router_log("WARN", f"channel_probe_cache_load_failed error={type(exc).__name__}: {exc}")
510
+ claude_passthrough = list(launch_passthrough)
511
+ if use_native_anthropic:
512
+ if native_direct_mcp_config_paths:
513
+ mcp_config_paths.extend(native_direct_mcp_config_paths)
514
+ claude_passthrough = strip_mcp_config_passthrough(launch_passthrough)
515
+ elif stdin_channel_proxy or llm_channel_delivery or native_auto_channel_specs:
516
+ if llm_channel_delivery:
517
+ prepare_channel_llm_delivery_for_launch()
518
+ if should_launch_process_start_channel_sse(stdin_channel_proxy, native_channel_bridge, llm_channel_delivery):
519
+ auto_start_sse_channels_from_mcp_configs(
520
+ launch_passthrough,
521
+ extra_config_paths=[Path(path) for path in mcp_config_paths],
522
+ )
523
+ else:
524
+ router_log("INFO", "channel_sse_auto_start_skipped reason=router_managed_llm_delivery")
525
+ # Channel-capable streamable-HTTP backends (e.g. ai-net-http) are forced
526
+ # through ciel-runtime's own mcp-proxy so there is exactly ONE backend
527
+ # connection: the proxy serves Claude Code's tool calls AND owns the
528
+ # notification stream + idle-death wake handling. Because the proxy now
529
+ # OWNS the stream, it must NOT also be in the disable set -- forcing a
530
+ # server while disabling its stream would leave zero notification owners
531
+ # and the agent would never wake. force and disable are mutually
532
+ # exclusive per server, so the disable set excludes anything we force.
533
+ forced_channel_names = (
534
+ set(detected_channel_capable_names)
535
+ if (stdin_channel_proxy or llm_channel_delivery)
536
+ else set()
537
+ )
538
+ proxy_config = write_mcp_proxy_config(
539
+ launch_passthrough,
540
+ extra_config_paths=[Path(path) for path in mcp_config_paths],
541
+ force_proxy_server_names=forced_channel_names or None,
542
+ disable_proxy_notification_stream_names=None,
543
+ )
544
+ if proxy_config:
545
+ mcp_config_paths = [str(proxy_config)]
546
+ claude_passthrough = strip_mcp_config_passthrough(launch_passthrough)
547
+ if mcp_config_paths:
548
+ extra_args.extend(["--mcp-config", *mcp_config_paths])
549
+ if should_append_compat_prompt(provider, pcfg, cfg) and not has_passthrough_option(launch_passthrough, "--system-prompt"):
550
+ extra_args.extend(["--append-system-prompt", ROUTED_COMPAT_PROMPT])
551
+ extra_args.extend(
552
+ claude_channel_args(
553
+ cfg,
554
+ launch_passthrough,
555
+ extra_specs=native_auto_channel_specs if native_auto_channel_specs else detected_channel_specs,
556
+ native_channel_bridge=bool(native_channel_bridge or native_auto_channel_specs),
557
+ )
558
+ )
559
+ append_claude_code_runtime_settings_args(extra_args, launch_passthrough, provider, pcfg)
560
+ if fork_native_session:
561
+ session_id = str(uuid.uuid4())
562
+ extra_args.extend(["--session-id", session_id])
563
+ router_log(
564
+ "INFO",
565
+ f"claude_native_session_boundary previous_mode={previous_launch_mode} cwd={launch_cwd_key} session_id={session_id}",
566
+ )
567
+ bypass_permission_mode = (
568
+ not use_native_anthropic
569
+ and not has_passthrough_option([*extra_args, *claude_passthrough], "--permission-mode")
570
+ and claude_supports_permission_mode_arg(claude)
571
+ )
572
+ disallowed_tools = ""
573
+ if (
574
+ should_disallow_claude_server_side_web_tools(provider, pcfg, use_native_anthropic)
575
+ and not has_passthrough_option([*extra_args, *claude_passthrough], "--disallowedTools", "--disallowed-tools")
576
+ ):
577
+ disallowed_tools = ",".join(CLAUDE_SERVER_SIDE_WEB_TOOLS)
578
+ model = env.get("CIEL_RUNTIME_MODEL_ALIAS")
579
+ cmd, env = materialize_runtime_command(
580
+ "claude",
581
+ claude,
582
+ env,
583
+ provider,
584
+ pcfg,
585
+ mode="native" if use_native_anthropic else "routed",
586
+ protocol="anthropic_messages",
587
+ cwd=Path.cwd(),
588
+ enable_channels=bool(stdin_channel_proxy or native_channel_bridge or llm_channel_delivery),
589
+ passthrough=claude_passthrough,
590
+ options={
591
+ "bypass_permission_mode": bypass_permission_mode,
592
+ "disallowed_tools": disallowed_tools,
593
+ "model": model or "",
594
+ "extra_args": tuple(extra_args),
595
+ "passthrough_boundary": should_insert_passthrough_option_boundary(extra_args, claude_passthrough),
596
+ },
597
+ )
598
+ _log_claude_command_for_diagnostics(cmd, env)
599
+ record_launch_state_for_cwd(
600
+ launch_cwd_key,
601
+ provider,
602
+ launch_mode_name(provider, pcfg, use_native_anthropic),
603
+ str(pcfg.get("current_model") or env.get("CIEL_RUNTIME_MODEL_ALIAS") or ""),
604
+ )
605
+ launch_log_offset = file_size_or_zero(LOG_PATH)
606
+ capture_stderr = env_bool(os.environ.get("CIEL_RUNTIME_CAPTURE_CC_STDERR"), False)
607
+ def run_claude_process() -> int:
608
+ rc = 1
609
+ try:
610
+ if use_router_mode and not ensure_managed_router_running_for_client():
611
+ print(
612
+ "Ciel Runtime warning: local router health check failed immediately before launching Claude Code.",
613
+ flush=True,
614
+ )
615
+ print(f" {router_health_summary()}", flush=True)
616
+ if stdin_channel_proxy:
617
+ rc = subprocess_call_with_channel_wake_proxy(cmd, env, wake_for_llm_delivery=llm_channel_delivery)
618
+ elif capture_stderr:
619
+ rc = _subprocess_call_capturing_stderr(cmd, env)
620
+ else:
621
+ rc = subprocess.call(cmd, env=env)
622
+ return rc
623
+ finally:
624
+ if use_router_mode:
625
+ print_routed_claude_exit_diagnostics(rc, provider, pcfg, log_offset=launch_log_offset)
626
+
627
+ return run_with_router_lifetime(run_claude_process, manage_router_lifetime)
628
+
629
+
630
+
631
+ @dataclass(frozen=True, slots=True)
632
+ class CodexLaunchConstants:
633
+ CODEX_RUNTIME_API_KEY_ENV: Any
634
+ CONFIG_DIR: Any
635
+ PRELAUNCH_CANCEL: Any
636
+ PRELAUNCH_LAUNCH_AGY: Any
637
+ PRELAUNCH_LAUNCH_CLAUDE: Any
638
+ PRELAUNCH_LAUNCH_CODEX: Any
639
+ PRELAUNCH_LAUNCH_CODEX_APP_SERVER: Any
640
+
641
+
642
+ def build_default_codex_launch_constants() -> CodexLaunchConstants:
643
+ """Build immutable Codex and App Server launch constants."""
644
+ return CodexLaunchConstants(
645
+ CODEX_RUNTIME_API_KEY_ENV=CODEX_RUNTIME_API_KEY_ENV,
646
+ CONFIG_DIR=CONFIG_DIR,
647
+ PRELAUNCH_CANCEL=PRELAUNCH_CANCEL,
648
+ PRELAUNCH_LAUNCH_AGY=PRELAUNCH_LAUNCH_AGY,
649
+ PRELAUNCH_LAUNCH_CLAUDE=PRELAUNCH_LAUNCH_CLAUDE,
650
+ PRELAUNCH_LAUNCH_CODEX=PRELAUNCH_LAUNCH_CODEX,
651
+ PRELAUNCH_LAUNCH_CODEX_APP_SERVER=PRELAUNCH_LAUNCH_CODEX_APP_SERVER,
652
+ )
653
+
654
+
655
+ @dataclass(frozen=True, slots=True)
656
+ class CodexLaunchProcess:
657
+ _channel_wake_enter_env_is_fixed: Callable[..., Any]
658
+ _codex_channel_wake_submit_delay_seconds: Callable[..., Any]
659
+ _codex_channel_wake_submit_retries: Callable[..., Any]
660
+ _log_codex_command_for_diagnostics: Callable[..., Any]
661
+ _set_channel_transcript_scope: Callable[..., Any]
662
+ codex_process_record_path: Callable[..., Any]
663
+ path_with_ciel_runtime_user_dirs: Callable[..., Any]
664
+ subprocess_call_with_channel_wake_proxy: Callable[..., Any]
665
+ terminate_existing_codex_processes_for_launch: Callable[..., Any]
666
+ terminate_existing_router_clients_for_launch: Callable[..., Any]
667
+
668
+
669
+ @dataclass(frozen=True, slots=True)
670
+ class CodexLaunchCliPolicy:
671
+ codex_alternate_screen_compat_args: Callable[..., Any]
672
+ codex_current_model_cli_args: Callable[..., Any]
673
+ codex_help_requested: Callable[..., Any]
674
+ codex_native_routed_config_args: Callable[..., Any]
675
+ codex_passthrough_args_for_launch: Callable[..., Any]
676
+ codex_passthrough_has_command: Callable[..., Any]
677
+ codex_resume_picker_requested: Callable[..., Any]
678
+ codex_resume_with_session_id: Callable[..., Any]
679
+ codex_runtime_config_args: Callable[..., Any]
680
+ codex_yolo_launch_args: Callable[..., Any]
681
+
682
+
683
+ @dataclass(frozen=True, slots=True)
684
+ class CodexLaunchConfig:
685
+ apply_launch_endpoint_policy: Callable[..., Any]
686
+ current_alias: Callable[..., Any]
687
+ current_launch_cwd_key: Callable[..., Any]
688
+ ensure_model_cache_for_launch: Callable[..., Any]
689
+ get_current_provider: Callable[..., Any]
690
+ load_config: Callable[..., Any]
691
+ provider_mode_label: Callable[..., Any]
692
+ record_launch_state_for_cwd: Callable[..., Any]
693
+ codex_runtime_model_catalog_args: Callable[..., Any]
694
+
695
+
696
+ @dataclass(frozen=True, slots=True)
697
+ class CodexLaunchInstallation:
698
+ disable_ciel_runtime_codex_prompts_for_native: Callable[..., Any]
699
+ find_executable: Callable[..., Any]
700
+ has_passthrough_option: Callable[..., Any]
701
+ install_ciel_runtime_codex_prompts: Callable[..., Any]
702
+ install_codex_if_missing: Callable[..., Any]
703
+ warn_if_multiple_ciel_runtime_installs: Callable[..., Any]
704
+
705
+
706
+ @dataclass(frozen=True, slots=True)
707
+ class CodexLaunchDispatch:
708
+ launch_agy: Callable[..., Any]
709
+ launch_claude: Callable[..., Any]
710
+ launch_codex_app_server: Callable[..., Any]
711
+ log_codex_passthrough_mapping: Callable[..., Any]
712
+ materialize_runtime_command: Callable[..., Any]
713
+ run_ciel_runtime_update_check: Callable[..., Any]
714
+ run_codex_update_check: Callable[..., Any]
715
+ run_prelaunch_menu: Callable[..., Any]
716
+
717
+
718
+ @dataclass(frozen=True, slots=True)
719
+ class CodexLaunchRouting:
720
+ cleanup_managed_services_for_provider: Callable[..., Any]
721
+ codex_routed_enabled: Callable[..., Any]
722
+ direct_native_codex_enabled: Callable[..., Any]
723
+ launch_readiness_errors: Callable[..., Any]
724
+ native_codex_enabled: Callable[..., Any]
725
+ run_with_router_lifetime: Callable[..., Any]
726
+ start_router_if_needed: Callable[..., Any]
727
+
728
+
729
+ @dataclass(frozen=True, slots=True)
730
+ class CodexLaunchChannel:
731
+ auto_import_passthrough_channels: Callable[..., Any]
732
+ channel_delivery_mode: Callable[..., Any]
733
+ codex_channel_capable_mcp_server_names: Callable[..., Any]
734
+ codex_mcp_native_http_compat_args: Callable[..., Any]
735
+ codex_mcp_split_proxy_enabled: Callable[..., Any]
736
+ restore_codex_mcp_config_from_managed: Callable[..., Any]
737
+ select_codex_resume_session: Callable[..., Any]
738
+ start_codex_mcp_channel_sse_for_launch: Callable[..., Any]
739
+ write_codex_mcp_config_for_channel_discovery: Callable[..., Any]
740
+
741
+
742
+ @dataclass(frozen=True, slots=True)
743
+ class CodexLaunchServices:
744
+ constants: CodexLaunchConstants
745
+ process: CodexLaunchProcess
746
+ cli_policy: CodexLaunchCliPolicy
747
+ config: CodexLaunchConfig
748
+ installation: CodexLaunchInstallation
749
+ dispatch: CodexLaunchDispatch
750
+ routing: CodexLaunchRouting
751
+ channel: CodexLaunchChannel
752
+
753
+
754
+ def run_codex(
755
+ passthrough: list[str],
756
+ skip_menu: bool = False,
757
+ force_menu: bool = False,
758
+ update_check: bool = True,
759
+ self_update_check: bool = True,
760
+ *,
761
+ services: CodexLaunchServices,
762
+ ) -> int:
763
+ CODEX_RUNTIME_API_KEY_ENV = services.constants.CODEX_RUNTIME_API_KEY_ENV
764
+ CONFIG_DIR = services.constants.CONFIG_DIR
765
+ PRELAUNCH_CANCEL = services.constants.PRELAUNCH_CANCEL
766
+ PRELAUNCH_LAUNCH_AGY = services.constants.PRELAUNCH_LAUNCH_AGY
767
+ PRELAUNCH_LAUNCH_CLAUDE = services.constants.PRELAUNCH_LAUNCH_CLAUDE
768
+ PRELAUNCH_LAUNCH_CODEX = services.constants.PRELAUNCH_LAUNCH_CODEX
769
+ PRELAUNCH_LAUNCH_CODEX_APP_SERVER = services.constants.PRELAUNCH_LAUNCH_CODEX_APP_SERVER
770
+ _channel_wake_enter_env_is_fixed = services.process._channel_wake_enter_env_is_fixed
771
+ _codex_channel_wake_submit_delay_seconds = services.process._codex_channel_wake_submit_delay_seconds
772
+ _codex_channel_wake_submit_retries = services.process._codex_channel_wake_submit_retries
773
+ _log_codex_command_for_diagnostics = services.process._log_codex_command_for_diagnostics
774
+ _set_channel_transcript_scope = services.process._set_channel_transcript_scope
775
+ apply_launch_endpoint_policy = services.config.apply_launch_endpoint_policy
776
+ auto_import_passthrough_channels = services.channel.auto_import_passthrough_channels
777
+ channel_delivery_mode = services.channel.channel_delivery_mode
778
+ cleanup_managed_services_for_provider = services.routing.cleanup_managed_services_for_provider
779
+ codex_alternate_screen_compat_args = services.cli_policy.codex_alternate_screen_compat_args
780
+ codex_channel_capable_mcp_server_names = services.channel.codex_channel_capable_mcp_server_names
781
+ codex_current_model_cli_args = services.cli_policy.codex_current_model_cli_args
782
+ codex_help_requested = services.cli_policy.codex_help_requested
783
+ codex_mcp_native_http_compat_args = services.channel.codex_mcp_native_http_compat_args
784
+ codex_mcp_split_proxy_enabled = services.channel.codex_mcp_split_proxy_enabled
785
+ restore_codex_mcp_config_from_managed = services.channel.restore_codex_mcp_config_from_managed
786
+ codex_native_routed_config_args = services.cli_policy.codex_native_routed_config_args
787
+ codex_passthrough_args_for_launch = services.cli_policy.codex_passthrough_args_for_launch
788
+ codex_passthrough_has_command = services.cli_policy.codex_passthrough_has_command
789
+ codex_process_record_path = services.process.codex_process_record_path
790
+ codex_resume_picker_requested = services.cli_policy.codex_resume_picker_requested
791
+ codex_resume_with_session_id = services.cli_policy.codex_resume_with_session_id
792
+ codex_routed_enabled = services.routing.codex_routed_enabled
793
+ codex_runtime_config_args = services.cli_policy.codex_runtime_config_args
794
+ codex_runtime_model_catalog_args = services.config.codex_runtime_model_catalog_args
795
+ codex_yolo_launch_args = services.cli_policy.codex_yolo_launch_args
796
+ current_alias = services.config.current_alias
797
+ current_launch_cwd_key = services.config.current_launch_cwd_key
798
+ direct_native_codex_enabled = services.routing.direct_native_codex_enabled
799
+ disable_ciel_runtime_codex_prompts_for_native = services.installation.disable_ciel_runtime_codex_prompts_for_native
800
+ ensure_model_cache_for_launch = services.config.ensure_model_cache_for_launch
801
+ find_executable = services.installation.find_executable
802
+ get_current_provider = services.config.get_current_provider
803
+ has_passthrough_option = services.installation.has_passthrough_option
804
+ install_ciel_runtime_codex_prompts = services.installation.install_ciel_runtime_codex_prompts
805
+ install_codex_if_missing = services.installation.install_codex_if_missing
806
+ launch_agy = services.dispatch.launch_agy
807
+ launch_claude = services.dispatch.launch_claude
808
+ launch_codex_app_server = services.dispatch.launch_codex_app_server
809
+ launch_readiness_errors = services.routing.launch_readiness_errors
810
+ load_config = services.config.load_config
811
+ log_codex_passthrough_mapping = services.dispatch.log_codex_passthrough_mapping
812
+ materialize_runtime_command = services.dispatch.materialize_runtime_command
813
+ native_codex_enabled = services.routing.native_codex_enabled
814
+ path_with_ciel_runtime_user_dirs = services.process.path_with_ciel_runtime_user_dirs
815
+ provider_mode_label = services.config.provider_mode_label
816
+ record_launch_state_for_cwd = services.config.record_launch_state_for_cwd
817
+ run_ciel_runtime_update_check = services.dispatch.run_ciel_runtime_update_check
818
+ run_codex_update_check = services.dispatch.run_codex_update_check
819
+ run_prelaunch_menu = services.dispatch.run_prelaunch_menu
820
+ run_with_router_lifetime = services.routing.run_with_router_lifetime
821
+ select_codex_resume_session = services.channel.select_codex_resume_session
822
+ start_codex_mcp_channel_sse_for_launch = services.channel.start_codex_mcp_channel_sse_for_launch
823
+ start_router_if_needed = services.routing.start_router_if_needed
824
+ subprocess_call_with_channel_wake_proxy = services.process.subprocess_call_with_channel_wake_proxy
825
+ terminate_existing_codex_processes_for_launch = services.process.terminate_existing_codex_processes_for_launch
826
+ terminate_existing_router_clients_for_launch = services.process.terminate_existing_router_clients_for_launch
827
+ warn_if_multiple_ciel_runtime_installs = services.installation.warn_if_multiple_ciel_runtime_installs
828
+ write_codex_mcp_config_for_channel_discovery = services.channel.write_codex_mcp_config_for_channel_discovery
829
+ warn_if_multiple_ciel_runtime_installs()
830
+ run_ciel_runtime_update_check(enabled=self_update_check)
831
+ env = os.environ.copy()
832
+ env["PATH"] = path_with_ciel_runtime_user_dirs(env)
833
+ codex = install_codex_if_missing()
834
+ if not codex:
835
+ raise RuntimeError(
836
+ "codex executable was not found in PATH or the Ciel Runtime user bin directories, "
837
+ "and automatic install of @openai/codex did not make it available"
838
+ )
839
+ updated_codex = run_codex_update_check(codex, enabled=update_check)
840
+ if isinstance(updated_codex, str) and updated_codex:
841
+ codex = updated_codex
842
+ codex = find_executable("codex") or codex
843
+ codex_passthrough, codex_passthrough_notes = codex_passthrough_args_for_launch(passthrough)
844
+ if codex_help_requested(codex_passthrough):
845
+ log_codex_passthrough_mapping(codex_passthrough_notes)
846
+ return subprocess.call([codex, *codex_passthrough], env=env)
847
+ if codex_passthrough_has_command(codex_passthrough):
848
+ skip_menu = True
849
+ auto_import_passthrough_channels(passthrough)
850
+ rc = run_prelaunch_menu(passthrough, skip_menu=skip_menu, force_menu=force_menu)
851
+ if rc == PRELAUNCH_LAUNCH_CLAUDE:
852
+ return launch_claude(
853
+ passthrough,
854
+ skip_menu=True,
855
+ force_menu=False,
856
+ update_check=update_check,
857
+ self_update_check=False,
858
+ )
859
+ if rc == PRELAUNCH_LAUNCH_AGY:
860
+ return launch_agy(
861
+ passthrough,
862
+ skip_menu=True,
863
+ force_menu=False,
864
+ update_check=update_check,
865
+ self_update_check=False,
866
+ )
867
+ if rc == PRELAUNCH_LAUNCH_CODEX_APP_SERVER:
868
+ return launch_codex_app_server(
869
+ passthrough,
870
+ skip_menu=True,
871
+ force_menu=False,
872
+ update_check=update_check,
873
+ self_update_check=False,
874
+ )
875
+ if rc == PRELAUNCH_CANCEL:
876
+ return 0
877
+ if rc not in (0, PRELAUNCH_LAUNCH_CODEX):
878
+ return rc
879
+ cfg = load_config()
880
+ provider, pcfg = get_current_provider(cfg)
881
+ for line in apply_launch_endpoint_policy(cfg, "codex"):
882
+ print(line, flush=True)
883
+ provider, pcfg = get_current_provider(cfg)
884
+ blockers = launch_readiness_errors(cfg)
885
+ if blockers:
886
+ print("Ciel Runtime Codex launch blocked:", flush=True)
887
+ for line in blockers:
888
+ print(f"- {line}", flush=True)
889
+ return 2
890
+ cleanup_managed_services_for_provider(provider, pcfg, cfg, quiet=True)
891
+ use_native_codex = direct_native_codex_enabled(provider, pcfg)
892
+ use_codex_routed = codex_routed_enabled(provider, pcfg)
893
+ launch_cwd = Path.cwd()
894
+ mapped_continue = any(note.startswith("--continue -> resume --last") for note in codex_passthrough_notes)
895
+ if not use_native_codex and mapped_continue:
896
+ try:
897
+ resume_index = codex_passthrough.index("resume")
898
+ except ValueError:
899
+ resume_index = -1
900
+ if resume_index >= 0 and resume_index + 1 < len(codex_passthrough):
901
+ if codex_passthrough[resume_index + 1] == "--last":
902
+ del codex_passthrough[resume_index + 1]
903
+ codex_passthrough_notes.append("routed --continue -> provider-independent session picker")
904
+ if not use_native_codex and codex_resume_picker_requested(codex_passthrough):
905
+ session_id = select_codex_resume_session(
906
+ env,
907
+ include_non_interactive="--include-non-interactive" in codex_passthrough,
908
+ passthrough=codex_passthrough,
909
+ cwd=launch_cwd,
910
+ )
911
+ if session_id == "":
912
+ return 0
913
+ if session_id:
914
+ codex_passthrough = codex_resume_with_session_id(codex_passthrough, session_id)
915
+ codex_passthrough_notes.append("resume picker -> selected local Codex session")
916
+ restore_codex_mcp_config_from_managed(
917
+ codex_passthrough, env=env, cwd=launch_cwd
918
+ )
919
+ if use_native_codex:
920
+ disable_ciel_runtime_codex_prompts_for_native(env)
921
+ else:
922
+ install_ciel_runtime_codex_prompts(env)
923
+ codex_mcp_config = write_codex_mcp_config_for_channel_discovery(codex_passthrough, env=env)
924
+ env["CIEL_RUNTIME_CODEX_MANAGED"] = "1"
925
+ env["CIEL_RUNTIME_CONFIG_DIR"] = str(CONFIG_DIR)
926
+ env["CIEL_RUNTIME_LAUNCH_CWD"] = str(launch_cwd)
927
+ terminate_existing_codex_processes_for_launch("codex_prelaunch_processes", cwd=launch_cwd, quiet=True)
928
+ if not use_native_codex:
929
+ terminate_existing_router_clients_for_launch("codex_prelaunch_active_clients", quiet=True)
930
+ if use_native_codex:
931
+ manage_router_lifetime = bool(
932
+ start_router_if_needed(replace_active_clients=False)
933
+ ) if web_backend_start_requested(cfg) else False
934
+ else:
935
+ manage_router_lifetime = bool(start_router_if_needed())
936
+ if not native_codex_enabled(provider):
937
+ ensure_model_cache_for_launch(provider, pcfg)
938
+ codex_channel_owned_names = (
939
+ codex_channel_capable_mcp_server_names(cfg, codex_mcp_config)
940
+ if not use_native_codex and channel_delivery_mode(cfg) == "llm"
941
+ else []
942
+ )
943
+ codex_mcp_compat_args = codex_mcp_native_http_compat_args(
944
+ codex_mcp_config,
945
+ split_http_proxy=(not use_native_codex and codex_mcp_split_proxy_enabled()),
946
+ channel_owned_server_names=codex_channel_owned_names,
947
+ include_builtin_channel=(
948
+ channel_delivery_mode(cfg) == "llm" and web_backend_start_requested(cfg)
949
+ ),
950
+ )
951
+ codex_yolo_args = codex_yolo_launch_args(codex_passthrough)
952
+ if not use_native_codex and not use_codex_routed:
953
+ env[CODEX_RUNTIME_API_KEY_ENV] = env.get(CODEX_RUNTIME_API_KEY_ENV) or "ciel-runtime-router-local-key"
954
+ log_codex_passthrough_mapping(codex_passthrough_notes)
955
+ model_alias_args: list[str] = []
956
+ if not native_codex_enabled(provider) and not has_passthrough_option(codex_passthrough, "-m", "--model"):
957
+ model = current_alias(cfg)
958
+ if model:
959
+ model_alias_args.extend(["-m", model])
960
+ codex_mode = "native" if use_native_codex else ("routed" if use_codex_routed else "router")
961
+ cmd, env = materialize_runtime_command(
962
+ "codex",
963
+ codex,
964
+ env,
965
+ provider,
966
+ pcfg,
967
+ mode=codex_mode,
968
+ protocol="openai_responses",
969
+ cwd=launch_cwd,
970
+ enable_channels=bool(codex_channel_owned_names),
971
+ passthrough=codex_passthrough,
972
+ options={
973
+ "yolo_args": tuple(codex_yolo_args),
974
+ "model_args": tuple(codex_current_model_cli_args(pcfg, codex_passthrough)),
975
+ "routed_config_args": tuple(codex_native_routed_config_args()),
976
+ "router_config_args": tuple(codex_runtime_config_args()),
977
+ "model_catalog_args": tuple(codex_runtime_model_catalog_args(codex, cfg)),
978
+ "alternate_screen_args": tuple(codex_alternate_screen_compat_args(codex_passthrough, env=env)),
979
+ "mcp_args": tuple(codex_mcp_compat_args),
980
+ "model_alias_args": tuple(model_alias_args),
981
+ },
982
+ )
983
+ _log_codex_command_for_diagnostics(cmd, env)
984
+ record_launch_state_for_cwd(
985
+ current_launch_cwd_key(),
986
+ provider,
987
+ provider_mode_label(provider, pcfg) if native_codex_enabled(provider) else "codex-router",
988
+ str(pcfg.get("current_model") or ("" if native_codex_enabled(provider) else current_alias(cfg)) or ""),
989
+ )
990
+
991
+ def run_codex_process() -> int:
992
+ _set_channel_transcript_scope(
993
+ "codex",
994
+ codex_home=Path(env.get("CODEX_HOME") or (Path.home() / ".codex")),
995
+ )
996
+ if not use_native_codex:
997
+ start_codex_mcp_channel_sse_for_launch(cfg, codex_mcp_config, allowed_server_names=codex_channel_owned_names)
998
+ codex_synthetic_enter = None if _channel_wake_enter_env_is_fixed() else b"\r"
999
+ return subprocess_call_with_channel_wake_proxy(
1000
+ cmd,
1001
+ env,
1002
+ wake_for_llm_delivery=channel_delivery_mode(cfg) == "llm",
1003
+ synthetic_enter_bytes=codex_synthetic_enter,
1004
+ normalize_bare_cr_for_synthetic_enter=False,
1005
+ channel_wake_submit_retries=_codex_channel_wake_submit_retries(),
1006
+ channel_wake_confirm_submit=True,
1007
+ channel_wake_bracketed_paste=True,
1008
+ channel_wake_submit_delay_seconds=_codex_channel_wake_submit_delay_seconds(),
1009
+ tracked_child_pid_path=codex_process_record_path("client"),
1010
+ )
1011
+
1012
+ return run_with_router_lifetime(run_codex_process, manage_router_lifetime)
1013
+
1014
+
1015
+
1016
+ @dataclass(frozen=True, slots=True)
1017
+ class CodexAppServerProcess:
1018
+ _log_codex_app_server_command_for_diagnostics: Callable[..., Any]
1019
+ codex_process_record_path: Callable[..., Any]
1020
+ path_with_ciel_runtime_user_dirs: Callable[..., Any]
1021
+ subprocess_call_with_child_pid_record: Callable[..., Any]
1022
+ terminate_existing_codex_processes_for_launch: Callable[..., Any]
1023
+ terminate_existing_router_clients_for_launch: Callable[..., Any]
1024
+
1025
+
1026
+ @dataclass(frozen=True, slots=True)
1027
+ class CodexAppServerConfig:
1028
+ apply_launch_endpoint_policy: Callable[..., Any]
1029
+ current_alias: Callable[..., Any]
1030
+ current_launch_cwd_key: Callable[..., Any]
1031
+ ensure_model_cache_for_launch: Callable[..., Any]
1032
+ get_current_provider: Callable[..., Any]
1033
+ load_config: Callable[..., Any]
1034
+ provider_mode_label: Callable[..., Any]
1035
+ record_launch_state_for_cwd: Callable[..., Any]
1036
+
1037
+
1038
+ @dataclass(frozen=True, slots=True)
1039
+ class CodexAppServerCliPolicy:
1040
+ codex_app_server_default_listen_url: Callable[..., Any]
1041
+ codex_app_server_launch_args: Callable[..., Any]
1042
+ codex_current_model_config_args: Callable[..., Any]
1043
+ codex_native_routed_config_args: Callable[..., Any]
1044
+ codex_passthrough_has_model_override: Callable[..., Any]
1045
+ codex_runtime_config_args: Callable[..., Any]
1046
+ toml_string: Callable[..., Any]
1047
+
1048
+
1049
+ @dataclass(frozen=True, slots=True)
1050
+ class CodexAppServerInstallation:
1051
+ find_executable: Callable[..., Any]
1052
+ install_codex_if_missing: Callable[..., Any]
1053
+ warn_if_multiple_ciel_runtime_installs: Callable[..., Any]
1054
+
1055
+
1056
+ @dataclass(frozen=True, slots=True)
1057
+ class CodexAppServerDispatch:
1058
+ launch_agy: Callable[..., Any]
1059
+ launch_claude: Callable[..., Any]
1060
+ launch_codex: Callable[..., Any]
1061
+ run_ciel_runtime_update_check: Callable[..., Any]
1062
+ run_codex_update_check: Callable[..., Any]
1063
+ run_prelaunch_menu: Callable[..., Any]
1064
+
1065
+
1066
+ @dataclass(frozen=True, slots=True)
1067
+ class CodexAppServerRouting:
1068
+ cleanup_managed_services_for_provider: Callable[..., Any]
1069
+ codex_launch_enabled_for_provider: Callable[..., Any]
1070
+ codex_routed_enabled: Callable[..., Any]
1071
+ direct_native_codex_enabled: Callable[..., Any]
1072
+ launch_readiness_errors: Callable[..., Any]
1073
+ native_codex_enabled: Callable[..., Any]
1074
+ run_with_router_lifetime: Callable[..., Any]
1075
+ start_router_if_needed: Callable[..., Any]
1076
+
1077
+
1078
+ @dataclass(frozen=True, slots=True)
1079
+ class CodexAppServerChannel:
1080
+ auto_import_passthrough_channels: Callable[..., Any]
1081
+ channel_delivery_mode: Callable[..., Any]
1082
+ codex_channel_capable_mcp_server_names: Callable[..., Any]
1083
+ codex_mcp_native_http_compat_args: Callable[..., Any]
1084
+ codex_mcp_split_proxy_enabled: Callable[..., Any]
1085
+ restore_codex_mcp_config_from_managed: Callable[..., Any]
1086
+ start_codex_mcp_channel_sse_for_launch: Callable[..., Any]
1087
+ write_codex_mcp_config_for_channel_discovery: Callable[..., Any]
1088
+
1089
+
1090
+ @dataclass(frozen=True, slots=True)
1091
+ class CodexAppServerLaunchServices:
1092
+ constants: CodexLaunchConstants
1093
+ process: CodexAppServerProcess
1094
+ config: CodexAppServerConfig
1095
+ cli_policy: CodexAppServerCliPolicy
1096
+ installation: CodexAppServerInstallation
1097
+ dispatch: CodexAppServerDispatch
1098
+ routing: CodexAppServerRouting
1099
+ channel: CodexAppServerChannel
1100
+
1101
+
1102
+ def run_codex_app_server(
1103
+ passthrough: list[str],
1104
+ skip_menu: bool = True,
1105
+ force_menu: bool = False,
1106
+ update_check: bool = True,
1107
+ self_update_check: bool = True,
1108
+ *,
1109
+ services: CodexAppServerLaunchServices,
1110
+ ) -> int:
1111
+ CODEX_RUNTIME_API_KEY_ENV = services.constants.CODEX_RUNTIME_API_KEY_ENV
1112
+ CONFIG_DIR = services.constants.CONFIG_DIR
1113
+ PRELAUNCH_CANCEL = services.constants.PRELAUNCH_CANCEL
1114
+ PRELAUNCH_LAUNCH_AGY = services.constants.PRELAUNCH_LAUNCH_AGY
1115
+ PRELAUNCH_LAUNCH_CLAUDE = services.constants.PRELAUNCH_LAUNCH_CLAUDE
1116
+ PRELAUNCH_LAUNCH_CODEX = services.constants.PRELAUNCH_LAUNCH_CODEX
1117
+ PRELAUNCH_LAUNCH_CODEX_APP_SERVER = services.constants.PRELAUNCH_LAUNCH_CODEX_APP_SERVER
1118
+ _log_codex_app_server_command_for_diagnostics = services.process._log_codex_app_server_command_for_diagnostics
1119
+ apply_launch_endpoint_policy = services.config.apply_launch_endpoint_policy
1120
+ auto_import_passthrough_channels = services.channel.auto_import_passthrough_channels
1121
+ channel_delivery_mode = services.channel.channel_delivery_mode
1122
+ cleanup_managed_services_for_provider = services.routing.cleanup_managed_services_for_provider
1123
+ codex_app_server_default_listen_url = services.cli_policy.codex_app_server_default_listen_url
1124
+ codex_app_server_launch_args = services.cli_policy.codex_app_server_launch_args
1125
+ codex_channel_capable_mcp_server_names = services.channel.codex_channel_capable_mcp_server_names
1126
+ codex_current_model_config_args = services.cli_policy.codex_current_model_config_args
1127
+ codex_launch_enabled_for_provider = services.routing.codex_launch_enabled_for_provider
1128
+ codex_mcp_native_http_compat_args = services.channel.codex_mcp_native_http_compat_args
1129
+ codex_mcp_split_proxy_enabled = services.channel.codex_mcp_split_proxy_enabled
1130
+ restore_codex_mcp_config_from_managed = services.channel.restore_codex_mcp_config_from_managed
1131
+ codex_native_routed_config_args = services.cli_policy.codex_native_routed_config_args
1132
+ codex_passthrough_has_model_override = services.cli_policy.codex_passthrough_has_model_override
1133
+ codex_process_record_path = services.process.codex_process_record_path
1134
+ codex_routed_enabled = services.routing.codex_routed_enabled
1135
+ codex_runtime_config_args = services.cli_policy.codex_runtime_config_args
1136
+ current_alias = services.config.current_alias
1137
+ current_launch_cwd_key = services.config.current_launch_cwd_key
1138
+ direct_native_codex_enabled = services.routing.direct_native_codex_enabled
1139
+ ensure_model_cache_for_launch = services.config.ensure_model_cache_for_launch
1140
+ find_executable = services.installation.find_executable
1141
+ get_current_provider = services.config.get_current_provider
1142
+ install_codex_if_missing = services.installation.install_codex_if_missing
1143
+ launch_agy = services.dispatch.launch_agy
1144
+ launch_claude = services.dispatch.launch_claude
1145
+ launch_codex = services.dispatch.launch_codex
1146
+ launch_readiness_errors = services.routing.launch_readiness_errors
1147
+ load_config = services.config.load_config
1148
+ native_codex_enabled = services.routing.native_codex_enabled
1149
+ path_with_ciel_runtime_user_dirs = services.process.path_with_ciel_runtime_user_dirs
1150
+ provider_mode_label = services.config.provider_mode_label
1151
+ record_launch_state_for_cwd = services.config.record_launch_state_for_cwd
1152
+ run_ciel_runtime_update_check = services.dispatch.run_ciel_runtime_update_check
1153
+ run_codex_update_check = services.dispatch.run_codex_update_check
1154
+ run_prelaunch_menu = services.dispatch.run_prelaunch_menu
1155
+ run_with_router_lifetime = services.routing.run_with_router_lifetime
1156
+ start_codex_mcp_channel_sse_for_launch = services.channel.start_codex_mcp_channel_sse_for_launch
1157
+ start_router_if_needed = services.routing.start_router_if_needed
1158
+ subprocess_call_with_child_pid_record = services.process.subprocess_call_with_child_pid_record
1159
+ terminate_existing_codex_processes_for_launch = services.process.terminate_existing_codex_processes_for_launch
1160
+ terminate_existing_router_clients_for_launch = services.process.terminate_existing_router_clients_for_launch
1161
+ toml_string = services.cli_policy.toml_string
1162
+ warn_if_multiple_ciel_runtime_installs = services.installation.warn_if_multiple_ciel_runtime_installs
1163
+ write_codex_mcp_config_for_channel_discovery = services.channel.write_codex_mcp_config_for_channel_discovery
1164
+ warn_if_multiple_ciel_runtime_installs()
1165
+ run_ciel_runtime_update_check(enabled=self_update_check)
1166
+ env = os.environ.copy()
1167
+ env["PATH"] = path_with_ciel_runtime_user_dirs(env)
1168
+ codex = install_codex_if_missing()
1169
+ if not codex:
1170
+ raise RuntimeError(
1171
+ "codex executable was not found in PATH or the Ciel Runtime user bin directories, "
1172
+ "and automatic install of @openai/codex did not make it available"
1173
+ )
1174
+ updated_codex = run_codex_update_check(codex, enabled=update_check)
1175
+ if isinstance(updated_codex, str) and updated_codex:
1176
+ codex = updated_codex
1177
+ codex = find_executable("codex") or codex
1178
+ auto_import_passthrough_channels(passthrough)
1179
+ rc = run_prelaunch_menu(passthrough, skip_menu=skip_menu, force_menu=force_menu)
1180
+ if rc == PRELAUNCH_LAUNCH_CLAUDE:
1181
+ return launch_claude(
1182
+ passthrough,
1183
+ skip_menu=True,
1184
+ force_menu=False,
1185
+ update_check=update_check,
1186
+ self_update_check=False,
1187
+ )
1188
+ if rc == PRELAUNCH_LAUNCH_CODEX:
1189
+ return launch_codex(
1190
+ passthrough,
1191
+ skip_menu=True,
1192
+ force_menu=False,
1193
+ update_check=update_check,
1194
+ self_update_check=False,
1195
+ )
1196
+ if rc == PRELAUNCH_LAUNCH_AGY:
1197
+ return launch_agy(
1198
+ passthrough,
1199
+ skip_menu=True,
1200
+ force_menu=False,
1201
+ update_check=update_check,
1202
+ self_update_check=False,
1203
+ )
1204
+ if rc == PRELAUNCH_CANCEL:
1205
+ return 0
1206
+ if rc not in (0, PRELAUNCH_LAUNCH_CODEX_APP_SERVER):
1207
+ return rc
1208
+ cfg = load_config()
1209
+ provider, pcfg = get_current_provider(cfg)
1210
+ for line in apply_launch_endpoint_policy(cfg, "codex-app-server"):
1211
+ print(line, flush=True)
1212
+ provider, pcfg = get_current_provider(cfg)
1213
+ if not codex_launch_enabled_for_provider(provider):
1214
+ print("Ciel Runtime Codex App Server launch blocked:", flush=True)
1215
+ print("- Select Codex or Codex routed as the provider before launching Codex App Server.", flush=True)
1216
+ return 2
1217
+ blockers = launch_readiness_errors(cfg)
1218
+ if blockers:
1219
+ print("Ciel Runtime Codex App Server launch blocked:", flush=True)
1220
+ for line in blockers:
1221
+ print(f"- {line}", flush=True)
1222
+ return 2
1223
+ cleanup_managed_services_for_provider(provider, pcfg, cfg, quiet=True)
1224
+ restore_codex_mcp_config_from_managed(
1225
+ passthrough, env=env, cwd=Path.cwd()
1226
+ )
1227
+ codex_mcp_config = write_codex_mcp_config_for_channel_discovery(passthrough, env=env)
1228
+ use_native_codex = direct_native_codex_enabled(provider, pcfg)
1229
+ use_codex_routed = codex_routed_enabled(provider, pcfg)
1230
+ launch_cwd = Path.cwd()
1231
+ env["CIEL_RUNTIME_CODEX_MANAGED"] = "1"
1232
+ env["CIEL_RUNTIME_CONFIG_DIR"] = str(CONFIG_DIR)
1233
+ env["CIEL_RUNTIME_LAUNCH_CWD"] = str(launch_cwd)
1234
+ terminate_existing_codex_processes_for_launch("codex_app_server_prelaunch_processes", cwd=launch_cwd, quiet=True)
1235
+ if not use_native_codex:
1236
+ terminate_existing_router_clients_for_launch("codex_app_server_prelaunch_active_clients", quiet=True)
1237
+ if use_native_codex:
1238
+ manage_router_lifetime = bool(
1239
+ start_router_if_needed(replace_active_clients=False)
1240
+ ) if web_backend_start_requested(cfg) else False
1241
+ else:
1242
+ manage_router_lifetime = bool(start_router_if_needed())
1243
+ if use_codex_routed:
1244
+ config_args = codex_native_routed_config_args()
1245
+ elif use_native_codex:
1246
+ config_args = []
1247
+ else:
1248
+ env[CODEX_RUNTIME_API_KEY_ENV] = env.get(CODEX_RUNTIME_API_KEY_ENV) or "ciel-runtime-router-local-key"
1249
+ config_args = codex_runtime_config_args()
1250
+ if native_codex_enabled(provider):
1251
+ config_args = [*config_args, *codex_current_model_config_args(pcfg, passthrough)]
1252
+ if not native_codex_enabled(provider):
1253
+ ensure_model_cache_for_launch(provider, pcfg)
1254
+ model = current_alias(cfg)
1255
+ if model and not codex_passthrough_has_model_override(passthrough):
1256
+ config_args = [*config_args, "-c", f"model={toml_string(model)}"]
1257
+ codex_channel_owned_names = (
1258
+ codex_channel_capable_mcp_server_names(cfg, codex_mcp_config)
1259
+ if not use_native_codex and channel_delivery_mode(cfg) == "llm"
1260
+ else []
1261
+ )
1262
+ codex_mcp_compat_args = codex_mcp_native_http_compat_args(
1263
+ codex_mcp_config,
1264
+ split_http_proxy=(not use_native_codex and codex_mcp_split_proxy_enabled()),
1265
+ channel_owned_server_names=codex_channel_owned_names,
1266
+ include_builtin_channel=(
1267
+ channel_delivery_mode(cfg) == "llm" and web_backend_start_requested(cfg)
1268
+ ),
1269
+ )
1270
+ config_args = [*config_args, *codex_mcp_compat_args]
1271
+ listen_url = codex_app_server_default_listen_url()
1272
+ app_server_args = codex_app_server_launch_args(
1273
+ passthrough,
1274
+ config_args=config_args,
1275
+ default_listen_url=listen_url,
1276
+ )
1277
+ cmd = [codex, *app_server_args]
1278
+ print("Launching Codex App Server through Ciel Runtime.", flush=True)
1279
+ if "--listen" in cmd:
1280
+ try:
1281
+ print(f"Codex App Server listen: {cmd[cmd.index('--listen') + 1]}", flush=True)
1282
+ except Exception:
1283
+ pass
1284
+ _log_codex_app_server_command_for_diagnostics(cmd, env)
1285
+ record_launch_state_for_cwd(
1286
+ current_launch_cwd_key(),
1287
+ provider,
1288
+ provider_mode_label(provider, pcfg) if native_codex_enabled(provider) else "codex-app-server-router",
1289
+ str(pcfg.get("current_model") or ("" if native_codex_enabled(provider) else current_alias(cfg)) or ""),
1290
+ )
1291
+
1292
+ split_proxy_enabled = bool(not use_native_codex and (codex_mcp_split_proxy_enabled() or codex_channel_owned_names))
1293
+
1294
+ def run_codex_app_server_process() -> int:
1295
+ if split_proxy_enabled:
1296
+ start_codex_mcp_channel_sse_for_launch(cfg, codex_mcp_config, allowed_server_names=codex_channel_owned_names)
1297
+ return subprocess_call_with_child_pid_record(cmd, env, codex_process_record_path("app-server"))
1298
+
1299
+ return run_with_router_lifetime(run_codex_app_server_process, manage_router_lifetime)
1300
+
1301
+
1302
+
1303
+ @dataclass(frozen=True, slots=True)
1304
+ class AgyLaunchConstants:
1305
+ PRELAUNCH_CANCEL: Any
1306
+ PRELAUNCH_LAUNCH_AGY: Any
1307
+ PRELAUNCH_LAUNCH_CLAUDE: Any
1308
+ PRELAUNCH_LAUNCH_CODEX: Any
1309
+ PRELAUNCH_LAUNCH_CODEX_APP_SERVER: Any
1310
+
1311
+
1312
+ def build_default_agy_launch_constants() -> AgyLaunchConstants:
1313
+ """Build immutable AGY launch dispatch constants."""
1314
+ return AgyLaunchConstants(
1315
+ PRELAUNCH_CANCEL=PRELAUNCH_CANCEL,
1316
+ PRELAUNCH_LAUNCH_AGY=PRELAUNCH_LAUNCH_AGY,
1317
+ PRELAUNCH_LAUNCH_CLAUDE=PRELAUNCH_LAUNCH_CLAUDE,
1318
+ PRELAUNCH_LAUNCH_CODEX=PRELAUNCH_LAUNCH_CODEX,
1319
+ PRELAUNCH_LAUNCH_CODEX_APP_SERVER=PRELAUNCH_LAUNCH_CODEX_APP_SERVER,
1320
+ )
1321
+
1322
+
1323
+ @dataclass(frozen=True, slots=True)
1324
+ class AgyLaunchProcess:
1325
+ _codex_channel_wake_submit_delay_seconds: Callable[..., Any]
1326
+ _codex_channel_wake_submit_retries: Callable[..., Any]
1327
+ _log_agy_command_for_diagnostics: Callable[..., Any]
1328
+ path_with_ciel_runtime_user_dirs: Callable[..., Any]
1329
+ subprocess_call_with_channel_wake_proxy: Callable[..., Any]
1330
+
1331
+
1332
+ @dataclass(frozen=True, slots=True)
1333
+ class AgyLaunchCliPolicy:
1334
+ agy_dangerous_launch_args: Callable[..., Any]
1335
+ agy_help_requested: Callable[..., Any]
1336
+ agy_passthrough_args_for_launch: Callable[..., Any]
1337
+ agy_passthrough_has_command: Callable[..., Any]
1338
+
1339
+
1340
+ @dataclass(frozen=True, slots=True)
1341
+ class AgyLaunchChannel:
1342
+ auto_import_passthrough_channels: Callable[..., Any]
1343
+ channel_delivery_mode: Callable[..., Any]
1344
+
1345
+
1346
+ @dataclass(frozen=True, slots=True)
1347
+ class AgyLaunchConfig:
1348
+ current_launch_cwd_key: Callable[..., Any]
1349
+ get_current_provider: Callable[..., Any]
1350
+ load_config: Callable[..., Any]
1351
+ provider_mode_label: Callable[..., Any]
1352
+ record_launch_state_for_cwd: Callable[..., Any]
1353
+ restore_agy_mcp_config_from_managed: Callable[..., Any]
1354
+
1355
+
1356
+ @dataclass(frozen=True, slots=True)
1357
+ class AgyLaunchInstallation:
1358
+ find_executable: Callable[..., Any]
1359
+ install_agy_if_missing: Callable[..., Any]
1360
+ warn_if_multiple_ciel_runtime_installs: Callable[..., Any]
1361
+
1362
+
1363
+ @dataclass(frozen=True, slots=True)
1364
+ class AgyLaunchDispatch:
1365
+ launch_claude: Callable[..., Any]
1366
+ launch_codex: Callable[..., Any]
1367
+ launch_codex_app_server: Callable[..., Any]
1368
+ log_agy_passthrough_mapping: Callable[..., Any]
1369
+ materialize_runtime_command: Callable[..., Any]
1370
+ run_agy_update_check: Callable[..., Any]
1371
+ run_ciel_runtime_update_check: Callable[..., Any]
1372
+ run_prelaunch_menu: Callable[..., Any]
1373
+
1374
+
1375
+ @dataclass(frozen=True, slots=True)
1376
+ class AgyLaunchRouting:
1377
+ agy_routed_enabled: Callable[..., Any]
1378
+ cleanup_managed_services_for_provider: Callable[..., Any]
1379
+ launch_readiness_errors: Callable[..., Any]
1380
+ native_agy_enabled: Callable[..., Any]
1381
+ run_with_router_lifetime: Callable[..., Any]
1382
+ start_router_if_needed: Callable[..., Any]
1383
+
1384
+
1385
+ @dataclass(frozen=True, slots=True)
1386
+ class AgyLaunchServices:
1387
+ constants: AgyLaunchConstants
1388
+ process: AgyLaunchProcess
1389
+ cli_policy: AgyLaunchCliPolicy
1390
+ channel: AgyLaunchChannel
1391
+ config: AgyLaunchConfig
1392
+ installation: AgyLaunchInstallation
1393
+ dispatch: AgyLaunchDispatch
1394
+ routing: AgyLaunchRouting
1395
+
1396
+
1397
+ def run_agy(
1398
+ passthrough: list[str],
1399
+ skip_menu: bool = False,
1400
+ force_menu: bool = False,
1401
+ update_check: bool = True,
1402
+ self_update_check: bool = True,
1403
+ *,
1404
+ services: AgyLaunchServices,
1405
+ ) -> int:
1406
+ PRELAUNCH_CANCEL = services.constants.PRELAUNCH_CANCEL
1407
+ PRELAUNCH_LAUNCH_AGY = services.constants.PRELAUNCH_LAUNCH_AGY
1408
+ PRELAUNCH_LAUNCH_CLAUDE = services.constants.PRELAUNCH_LAUNCH_CLAUDE
1409
+ PRELAUNCH_LAUNCH_CODEX = services.constants.PRELAUNCH_LAUNCH_CODEX
1410
+ PRELAUNCH_LAUNCH_CODEX_APP_SERVER = services.constants.PRELAUNCH_LAUNCH_CODEX_APP_SERVER
1411
+ _codex_channel_wake_submit_delay_seconds = services.process._codex_channel_wake_submit_delay_seconds
1412
+ _codex_channel_wake_submit_retries = services.process._codex_channel_wake_submit_retries
1413
+ _log_agy_command_for_diagnostics = services.process._log_agy_command_for_diagnostics
1414
+ agy_dangerous_launch_args = services.cli_policy.agy_dangerous_launch_args
1415
+ agy_help_requested = services.cli_policy.agy_help_requested
1416
+ agy_passthrough_args_for_launch = services.cli_policy.agy_passthrough_args_for_launch
1417
+ agy_passthrough_has_command = services.cli_policy.agy_passthrough_has_command
1418
+ agy_routed_enabled = services.routing.agy_routed_enabled
1419
+ auto_import_passthrough_channels = services.channel.auto_import_passthrough_channels
1420
+ channel_delivery_mode = services.channel.channel_delivery_mode
1421
+ cleanup_managed_services_for_provider = services.routing.cleanup_managed_services_for_provider
1422
+ current_launch_cwd_key = services.config.current_launch_cwd_key
1423
+ find_executable = services.installation.find_executable
1424
+ get_current_provider = services.config.get_current_provider
1425
+ install_agy_if_missing = services.installation.install_agy_if_missing
1426
+ launch_claude = services.dispatch.launch_claude
1427
+ launch_codex = services.dispatch.launch_codex
1428
+ launch_codex_app_server = services.dispatch.launch_codex_app_server
1429
+ launch_readiness_errors = services.routing.launch_readiness_errors
1430
+ load_config = services.config.load_config
1431
+ log_agy_passthrough_mapping = services.dispatch.log_agy_passthrough_mapping
1432
+ materialize_runtime_command = services.dispatch.materialize_runtime_command
1433
+ native_agy_enabled = services.routing.native_agy_enabled
1434
+ path_with_ciel_runtime_user_dirs = services.process.path_with_ciel_runtime_user_dirs
1435
+ provider_mode_label = services.config.provider_mode_label
1436
+ record_launch_state_for_cwd = services.config.record_launch_state_for_cwd
1437
+ restore_agy_mcp_config_from_managed = services.config.restore_agy_mcp_config_from_managed
1438
+ run_agy_update_check = services.dispatch.run_agy_update_check
1439
+ run_ciel_runtime_update_check = services.dispatch.run_ciel_runtime_update_check
1440
+ run_prelaunch_menu = services.dispatch.run_prelaunch_menu
1441
+ run_with_router_lifetime = services.routing.run_with_router_lifetime
1442
+ start_router_if_needed = services.routing.start_router_if_needed
1443
+ subprocess_call_with_channel_wake_proxy = services.process.subprocess_call_with_channel_wake_proxy
1444
+ warn_if_multiple_ciel_runtime_installs = services.installation.warn_if_multiple_ciel_runtime_installs
1445
+ warn_if_multiple_ciel_runtime_installs()
1446
+ run_ciel_runtime_update_check(enabled=self_update_check)
1447
+ env = os.environ.copy()
1448
+ env["PATH"] = path_with_ciel_runtime_user_dirs(env)
1449
+ agy = install_agy_if_missing()
1450
+ if not agy:
1451
+ raise RuntimeError(
1452
+ "agy executable was not found in PATH or the Ciel Runtime/AGY user bin directories, "
1453
+ "and automatic install from Google's official AGY manifest did not make it available"
1454
+ )
1455
+ updated_agy = run_agy_update_check(agy, enabled=update_check)
1456
+ if isinstance(updated_agy, str) and updated_agy:
1457
+ agy = updated_agy
1458
+ agy = find_executable("agy") or agy
1459
+ agy_passthrough, agy_passthrough_notes = agy_passthrough_args_for_launch(passthrough)
1460
+ if agy_help_requested(agy_passthrough):
1461
+ log_agy_passthrough_mapping(agy_passthrough_notes)
1462
+ return subprocess.call([agy, *agy_passthrough], env=env)
1463
+ if agy_passthrough_has_command(agy_passthrough):
1464
+ skip_menu = True
1465
+ auto_import_passthrough_channels(passthrough)
1466
+ rc = run_prelaunch_menu(passthrough, skip_menu=skip_menu, force_menu=force_menu)
1467
+ if rc == PRELAUNCH_LAUNCH_CLAUDE:
1468
+ return launch_claude(
1469
+ passthrough,
1470
+ skip_menu=True,
1471
+ force_menu=False,
1472
+ update_check=update_check,
1473
+ self_update_check=False,
1474
+ )
1475
+ if rc == PRELAUNCH_LAUNCH_CODEX:
1476
+ return launch_codex(
1477
+ passthrough,
1478
+ skip_menu=True,
1479
+ force_menu=False,
1480
+ update_check=update_check,
1481
+ self_update_check=False,
1482
+ )
1483
+ if rc == PRELAUNCH_LAUNCH_CODEX_APP_SERVER:
1484
+ return launch_codex_app_server(
1485
+ passthrough,
1486
+ skip_menu=True,
1487
+ force_menu=False,
1488
+ update_check=update_check,
1489
+ self_update_check=False,
1490
+ )
1491
+ if rc == PRELAUNCH_CANCEL:
1492
+ return 0
1493
+ if rc not in (0, PRELAUNCH_LAUNCH_AGY):
1494
+ return rc
1495
+ cfg = load_config()
1496
+ provider, pcfg = get_current_provider(cfg)
1497
+ if not native_agy_enabled(provider):
1498
+ print("Ciel Runtime AGY launch blocked:", flush=True)
1499
+ print("- Select AGY or AGY Routed as the provider before launching AGY.", flush=True)
1500
+ return 2
1501
+ blockers = launch_readiness_errors(cfg)
1502
+ if blockers:
1503
+ print("Ciel Runtime AGY launch blocked:", flush=True)
1504
+ for line in blockers:
1505
+ print(f"- {line}", flush=True)
1506
+ return 2
1507
+ cleanup_managed_services_for_provider(provider, pcfg, cfg, quiet=True)
1508
+ restore_agy_mcp_config_from_managed(env=env, cwd=Path.cwd())
1509
+ use_agy_routed = agy_routed_enabled(provider, pcfg)
1510
+ if use_agy_routed and channel_delivery_mode(cfg) == "llm":
1511
+ manage_router_lifetime = bool(start_router_if_needed())
1512
+ elif web_backend_start_requested(cfg):
1513
+ manage_router_lifetime = bool(
1514
+ start_router_if_needed(replace_active_clients=False)
1515
+ )
1516
+ else:
1517
+ manage_router_lifetime = False
1518
+ agy_dangerous_args = agy_dangerous_launch_args(agy_passthrough)
1519
+ cmd, env = materialize_runtime_command(
1520
+ "agy",
1521
+ agy,
1522
+ env,
1523
+ provider,
1524
+ pcfg,
1525
+ mode="routed" if use_agy_routed else "native",
1526
+ protocol="anthropic_messages",
1527
+ cwd=Path.cwd(),
1528
+ enable_channels=use_agy_routed,
1529
+ passthrough=agy_passthrough,
1530
+ options={"dangerous_args": tuple(agy_dangerous_args)},
1531
+ )
1532
+ log_agy_passthrough_mapping(agy_passthrough_notes)
1533
+ _log_agy_command_for_diagnostics(cmd, env)
1534
+ record_launch_state_for_cwd(
1535
+ current_launch_cwd_key(),
1536
+ provider,
1537
+ provider_mode_label(provider, pcfg),
1538
+ str(pcfg.get("current_model") or ""),
1539
+ )
1540
+
1541
+ def run_agy_process() -> int:
1542
+ if use_agy_routed:
1543
+ return subprocess_call_with_channel_wake_proxy(
1544
+ cmd,
1545
+ env,
1546
+ wake_for_llm_delivery=channel_delivery_mode(cfg) == "llm",
1547
+ synthetic_enter_bytes=None,
1548
+ normalize_bare_cr_for_synthetic_enter=False,
1549
+ channel_wake_submit_retries=_codex_channel_wake_submit_retries(),
1550
+ channel_wake_confirm_submit=True,
1551
+ channel_wake_bracketed_paste=True,
1552
+ channel_wake_submit_delay_seconds=_codex_channel_wake_submit_delay_seconds(),
1553
+ )
1554
+ return subprocess.call(cmd, env=env)
1555
+
1556
+ return run_with_router_lifetime(run_agy_process, manage_router_lifetime)
1557
+
1558
+
1559
+
1560
+ __all__ = [
1561
+ "AgyLaunchServices",
1562
+ "AgyLaunchChannel",
1563
+ "AgyLaunchCliPolicy",
1564
+ "AgyLaunchConfig",
1565
+ "AgyLaunchConstants",
1566
+ "AgyLaunchDispatch",
1567
+ "AgyLaunchInstallation",
1568
+ "AgyLaunchProcess",
1569
+ "AgyLaunchRouting",
1570
+ "ClaudeLaunchChannelDelivery",
1571
+ "ClaudeLaunchChannelDiscovery",
1572
+ "ClaudeLaunchConfig",
1573
+ "ClaudeLaunchConstants",
1574
+ "ClaudeLaunchDispatch",
1575
+ "ClaudeLaunchInstallation",
1576
+ "ClaudeLaunchMcpConfig",
1577
+ "ClaudeLaunchPolicy",
1578
+ "ClaudeLaunchProcess",
1579
+ "ClaudeLaunchRouting",
1580
+ "ClaudeLaunchServices",
1581
+ "CodexAppServerLaunchServices",
1582
+ "CodexAppServerChannel",
1583
+ "CodexAppServerCliPolicy",
1584
+ "CodexAppServerConfig",
1585
+ "CodexAppServerDispatch",
1586
+ "CodexAppServerInstallation",
1587
+ "CodexAppServerProcess",
1588
+ "CodexAppServerRouting",
1589
+ "CodexLaunchChannel",
1590
+ "CodexLaunchCliPolicy",
1591
+ "CodexLaunchConfig",
1592
+ "CodexLaunchConstants",
1593
+ "CodexLaunchDispatch",
1594
+ "CodexLaunchInstallation",
1595
+ "CodexLaunchProcess",
1596
+ "CodexLaunchRouting",
1597
+ "CodexLaunchServices",
1598
+ "run_agy",
1599
+ "run_claude",
1600
+ "run_codex",
1601
+ "run_codex_app_server",
1602
+ ]