@oneciel-ai/ciel-runtime 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/ciel_runtime.py +2555 -9635
  2. package/ciel_runtime_support/advisor_request_builder.py +8 -21
  3. package/ciel_runtime_support/anthropic_tool_turns.py +13 -8
  4. package/ciel_runtime_support/architecture.py +68 -0
  5. package/ciel_runtime_support/architecture_budget.py +1 -1
  6. package/ciel_runtime_support/channel_connection_context.py +233 -0
  7. package/ciel_runtime_support/channel_delivery_context.py +332 -0
  8. package/ciel_runtime_support/channel_mcp_context.py +313 -0
  9. package/ciel_runtime_support/channel_mcp_discovery.py +47 -0
  10. package/ciel_runtime_support/channel_mcp_transport.py +5 -1
  11. package/ciel_runtime_support/channel_message_context.py +212 -0
  12. package/ciel_runtime_support/channel_message_repository.py +14 -3
  13. package/ciel_runtime_support/channel_pending_injection.py +9 -0
  14. package/ciel_runtime_support/channel_probe_launch_context.py +213 -0
  15. package/ciel_runtime_support/channel_replay_policy.py +38 -0
  16. package/ciel_runtime_support/channel_runtime_environment.py +8 -0
  17. package/ciel_runtime_support/channel_session_context.py +236 -0
  18. package/ciel_runtime_support/channel_terminal_context.py +350 -0
  19. package/ciel_runtime_support/channel_wake_context.py +532 -0
  20. package/ciel_runtime_support/claude_environment.py +60 -0
  21. package/ciel_runtime_support/claude_launch_assembly.py +249 -0
  22. package/ciel_runtime_support/claude_router.py +62 -12
  23. package/ciel_runtime_support/cli_application_context.py +132 -0
  24. package/ciel_runtime_support/cli_assembly.py +50 -0
  25. package/ciel_runtime_support/codex_backend_context.py +363 -0
  26. package/ciel_runtime_support/codex_config.py +13 -1
  27. package/ciel_runtime_support/codex_launch_assembly.py +213 -0
  28. package/ciel_runtime_support/codex_launch_configuration.py +30 -1
  29. package/ciel_runtime_support/codex_mcp_integration.py +90 -8
  30. package/ciel_runtime_support/codex_model_catalog.py +4 -1
  31. package/ciel_runtime_support/codex_reasoning_rejects.py +225 -0
  32. package/ciel_runtime_support/codex_router.py +38 -8
  33. package/ciel_runtime_support/codex_turn_recovery.py +154 -0
  34. package/ciel_runtime_support/config_migrations.py +103 -0
  35. package/ciel_runtime_support/config_repository.py +30 -0
  36. package/ciel_runtime_support/configuration_cli.py +38 -0
  37. package/ciel_runtime_support/context_compaction.py +9 -4
  38. package/ciel_runtime_support/credential_management.py +12 -0
  39. package/ciel_runtime_support/credentials.py +12 -0
  40. package/ciel_runtime_support/github_copilot_oauth.py +2 -2
  41. package/ciel_runtime_support/hosted_formula_tools.py +216 -0
  42. package/ciel_runtime_support/kimi_runtime_context.py +208 -0
  43. package/ciel_runtime_support/llm_preset_context.py +338 -0
  44. package/ciel_runtime_support/managed_mcp_config.py +8 -4
  45. package/ciel_runtime_support/mcp_configuration_context.py +291 -0
  46. package/ciel_runtime_support/mcp_http_proxy.py +14 -8
  47. package/ciel_runtime_support/mcp_probe_transport.py +47 -15
  48. package/ciel_runtime_support/mcp_transport.py +14 -1
  49. package/ciel_runtime_support/native_context_recovery.py +72 -0
  50. package/ciel_runtime_support/ollama_catalog_context.py +213 -0
  51. package/ciel_runtime_support/ollama_stream_collection.py +103 -0
  52. package/ciel_runtime_support/ollama_thinking.py +6 -1
  53. package/ciel_runtime_support/ollama_wire_projection.py +157 -0
  54. package/ciel_runtime_support/openai_forwarding.py +32 -10
  55. package/ciel_runtime_support/openai_responses_router.py +12 -0
  56. package/ciel_runtime_support/package_lifecycle.py +39 -0
  57. package/ciel_runtime_support/prelaunch_assembly.py +37 -0
  58. package/ciel_runtime_support/prelaunch_panel_context.py +418 -0
  59. package/ciel_runtime_support/prelaunch_shell_context.py +394 -0
  60. package/ciel_runtime_support/prompt_compaction.py +144 -0
  61. package/ciel_runtime_support/prompt_injection.py +45 -0
  62. package/ciel_runtime_support/protocols/anthropic_thinking_policy.py +1 -1
  63. package/ciel_runtime_support/protocols/chat_projection.py +85 -5
  64. package/ciel_runtime_support/protocols/conversation_turn_policy.py +43 -0
  65. package/ciel_runtime_support/protocols/ollama_chat.py +31 -0
  66. package/ciel_runtime_support/protocols/ollama_response.py +57 -5
  67. package/ciel_runtime_support/protocols/openai_reasoning.py +5 -2
  68. package/ciel_runtime_support/protocols/openai_responses.py +61 -15
  69. package/ciel_runtime_support/provider_adapters.py +26 -0
  70. package/ciel_runtime_support/provider_administration_context.py +207 -0
  71. package/ciel_runtime_support/provider_config_mutations.py +3 -0
  72. package/ciel_runtime_support/provider_model_catalog_context.py +137 -0
  73. package/ciel_runtime_support/provider_model_context.py +107 -0
  74. package/ciel_runtime_support/provider_model_metadata_context.py +197 -0
  75. package/ciel_runtime_support/provider_model_selection.py +10 -3
  76. package/ciel_runtime_support/provider_models.py +45 -2
  77. package/ciel_runtime_support/provider_option_cli.py +19 -0
  78. package/ciel_runtime_support/provider_policy.py +1 -1
  79. package/ciel_runtime_support/provider_readiness_context.py +189 -0
  80. package/ciel_runtime_support/provider_request_builder.py +64 -28
  81. package/ciel_runtime_support/provider_responses_passthrough.py +21 -2
  82. package/ciel_runtime_support/provider_timeout_policy.py +54 -0
  83. package/ciel_runtime_support/provider_tool_policy.py +9 -1
  84. package/ciel_runtime_support/providers/__init__.py +6 -0
  85. package/ciel_runtime_support/providers/alibaba.py +634 -0
  86. package/ciel_runtime_support/providers/catalog.py +24 -16
  87. package/ciel_runtime_support/providers/deepseek.py +73 -0
  88. package/ciel_runtime_support/providers/github_copilot_oauth.py +22 -1
  89. package/ciel_runtime_support/providers/kimi.py +69 -9
  90. package/ciel_runtime_support/providers/ollama.py +8 -0
  91. package/ciel_runtime_support/providers/ollama_context.py +21 -2
  92. package/ciel_runtime_support/providers/vllm.py +7 -1
  93. package/ciel_runtime_support/response_collection.py +68 -18
  94. package/ciel_runtime_support/response_collection_context.py +391 -0
  95. package/ciel_runtime_support/response_stream_context.py +555 -0
  96. package/ciel_runtime_support/responses_input_compatibility.py +121 -0
  97. package/ciel_runtime_support/responses_usage_observer.py +83 -0
  98. package/ciel_runtime_support/router_client_lifecycle.py +1 -0
  99. package/ciel_runtime_support/router_http.py +245 -3
  100. package/ciel_runtime_support/router_observability_context.py +251 -0
  101. package/ciel_runtime_support/router_process_context.py +200 -0
  102. package/ciel_runtime_support/router_process_lifecycle.py +2 -0
  103. package/ciel_runtime_support/router_request_assembly.py +399 -0
  104. package/ciel_runtime_support/router_request_context.py +215 -0
  105. package/ciel_runtime_support/router_server_context.py +84 -0
  106. package/ciel_runtime_support/runaway_output_guard.py +488 -0
  107. package/ciel_runtime_support/runtime_asset_assembly.py +147 -0
  108. package/ciel_runtime_support/runtime_asset_context.py +297 -0
  109. package/ciel_runtime_support/runtime_constants.py +16 -1
  110. package/ciel_runtime_support/runtime_launch.py +9 -5
  111. package/ciel_runtime_support/runtime_launch_context.py +130 -0
  112. package/ciel_runtime_support/runtime_maintenance_assembly.py +60 -0
  113. package/ciel_runtime_support/runtime_maintenance_context.py +309 -0
  114. package/ciel_runtime_support/runtime_maintenance_services.py +265 -0
  115. package/ciel_runtime_support/runtime_paths.py +60 -40
  116. package/ciel_runtime_support/runtime_primitives.py +78 -0
  117. package/ciel_runtime_support/speech_http_controller.py +335 -0
  118. package/ciel_runtime_support/sse_stream_collection.py +236 -0
  119. package/ciel_runtime_support/statusline_script.py +57 -8
  120. package/ciel_runtime_support/streaming_anthropic.py +361 -24
  121. package/ciel_runtime_support/tool_schema.py +40 -2
  122. package/ciel_runtime_support/tool_side_effect_dedupe.py +117 -12
  123. package/ciel_runtime_support/upstream_dump.py +68 -0
  124. package/ciel_runtime_support/upstream_retry_context.py +259 -0
  125. package/ciel_runtime_support/web_ui.py +248 -1
  126. package/ciel_runtime_support/workspace_router_selection.py +86 -0
  127. package/docs/COLAB_SPEECH.md +32 -0
  128. package/docs/Configuration.md +50 -0
  129. package/docs/Test-Suite.md +1 -0
  130. package/package.json +4 -1
  131. package/scripts/colab/__pycache__/bootstrap_moss_tts.cpython-311.pyc +0 -0
  132. package/scripts/colab/__pycache__/bootstrap_qwen_asr.cpython-311.pyc +0 -0
  133. package/scripts/colab/bootstrap_moss_tts.py +106 -0
  134. package/scripts/colab/bootstrap_qwen_asr.py +106 -0
  135. package/scripts/configure_speech_workers.py +37 -0
  136. package/scripts/deploy_colab_speech.ps1 +47 -0
@@ -0,0 +1,236 @@
1
+ """Durable streamable-HTTP channel session bounded context."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+ from typing import Any, Callable
8
+
9
+ from .channel_session_lifecycle import (
10
+ ChannelSessionLifecycleServices,
11
+ cleanup_stale_channel_sessions,
12
+ delete_channel_session,
13
+ )
14
+ from .channel_session_repository import ChannelSessionRepository
15
+
16
+
17
+ @dataclass(frozen=True, slots=True)
18
+ class ChannelSessionConfigPorts:
19
+ config_dir: Callable[[], Path]
20
+ protocol_version: str
21
+ log: Callable[[str, str], None]
22
+
23
+
24
+ @dataclass(frozen=True, slots=True)
25
+ class ChannelSessionHttpPorts:
26
+ streamable_headers: Callable[..., dict[str, str]]
27
+ error_body: Callable[..., str]
28
+ session_not_found: Callable[..., bool]
29
+
30
+
31
+ @dataclass(frozen=True, slots=True)
32
+ class ChannelSessionStatePorts:
33
+ connections: dict[str, dict[str, Any]]
34
+ lock: Any
35
+
36
+
37
+ @dataclass(frozen=True, slots=True)
38
+ class ChannelSessionContext:
39
+ config: ChannelSessionConfigPorts
40
+ http: ChannelSessionHttpPorts
41
+ state: ChannelSessionStatePorts
42
+
43
+ def path(self) -> Path:
44
+ return self.config.config_dir() / "channel-streamable-sessions.json"
45
+
46
+ def repository(self) -> ChannelSessionRepository:
47
+ return ChannelSessionRepository(
48
+ path=self.path(),
49
+ default_protocol_version=self.config.protocol_version,
50
+ log=self.config.log,
51
+ )
52
+
53
+ def services(self) -> ChannelSessionLifecycleServices:
54
+ return ChannelSessionLifecycleServices(
55
+ streamable_headers=self.http.streamable_headers,
56
+ http_error_body=self.http.error_body,
57
+ session_not_found=self.http.session_not_found,
58
+ records=self.records,
59
+ forget=self.forget,
60
+ log=self.config.log,
61
+ )
62
+
63
+ def records(self) -> list[dict[str, Any]]:
64
+ return self.repository().records()
65
+
66
+ def write(self, records: list[dict[str, Any]]) -> None:
67
+ self.repository().write(records)
68
+
69
+ def record(
70
+ self,
71
+ name: str,
72
+ url: str,
73
+ session_id: str | None,
74
+ protocol_version: str,
75
+ ) -> None:
76
+ self.repository().record(name, url, session_id, protocol_version)
77
+
78
+ def forget(self, name: str, url: str, session_id: str | None) -> None:
79
+ del name, url
80
+ self.repository().forget(session_id)
81
+
82
+ def delete(
83
+ self,
84
+ name: str,
85
+ endpoint: str,
86
+ headers: dict[str, str],
87
+ protocol_version: str,
88
+ session_id: str | None,
89
+ reason: str,
90
+ *,
91
+ timeout: float = 5.0,
92
+ ) -> bool:
93
+ return delete_channel_session(
94
+ name,
95
+ endpoint,
96
+ headers,
97
+ protocol_version,
98
+ session_id,
99
+ reason,
100
+ self.services(),
101
+ default_protocol_version=self.config.protocol_version,
102
+ timeout=timeout,
103
+ )
104
+
105
+ def close_state(self, state: dict[str, Any], reason: str) -> bool:
106
+ if str(state.get("transport") or "").strip().lower() not in {
107
+ "http",
108
+ "streamable-http",
109
+ }:
110
+ return True
111
+ name = str(state.get("name") or "")
112
+ endpoint = str(state.get("mcp_endpoint") or state.get("url") or "")
113
+ session_id = str(state.get("mcp_session_id") or "").strip() or None
114
+ headers = dict(state.get("headers") or {})
115
+ protocol_version = str(
116
+ state.get("mcp_protocol_version") or self.config.protocol_version
117
+ )
118
+ ok = self.delete(
119
+ name,
120
+ endpoint,
121
+ headers,
122
+ protocol_version,
123
+ session_id,
124
+ reason,
125
+ )
126
+ if ok:
127
+ with self.state.lock:
128
+ current = self.state.connections.get(name)
129
+ same_session = current and str(
130
+ current.get("mcp_session_id") or ""
131
+ ) == str(session_id or "")
132
+ if current is state or same_session:
133
+ current["mcp_session_id"] = None
134
+ current["mcp_initialized"] = False
135
+ return ok
136
+
137
+ def cleanup_stale(
138
+ self,
139
+ name: str,
140
+ url: str,
141
+ headers: dict[str, str],
142
+ protocol_version: str,
143
+ *,
144
+ keep_session_id: str | None = None,
145
+ ) -> None:
146
+ cleanup_stale_channel_sessions(
147
+ name,
148
+ url,
149
+ headers,
150
+ protocol_version,
151
+ self.services(),
152
+ default_protocol_version=self.config.protocol_version,
153
+ keep_session_id=keep_session_id,
154
+ )
155
+
156
+
157
+ @dataclass(frozen=True, slots=True)
158
+ class ChannelSessionCompatibilityApi:
159
+ context: Callable[[], ChannelSessionContext]
160
+
161
+ def path(self) -> Path:
162
+ return self.context().path()
163
+
164
+ def repository(self) -> ChannelSessionRepository:
165
+ return self.context().repository()
166
+
167
+ def services(self) -> ChannelSessionLifecycleServices:
168
+ return self.context().services()
169
+
170
+ def records(self) -> list[dict[str, Any]]:
171
+ return self.context().records()
172
+
173
+ def write(self, records: list[dict[str, Any]]) -> None:
174
+ self.context().write(records)
175
+
176
+ def record(
177
+ self,
178
+ name: str,
179
+ url: str,
180
+ session_id: str | None,
181
+ protocol_version: str,
182
+ ) -> None:
183
+ self.context().record(name, url, session_id, protocol_version)
184
+
185
+ def forget(self, name: str, url: str, session_id: str | None) -> None:
186
+ self.context().forget(name, url, session_id)
187
+
188
+ def delete(
189
+ self,
190
+ name: str,
191
+ endpoint: str,
192
+ headers: dict[str, str],
193
+ protocol_version: str,
194
+ session_id: str | None,
195
+ reason: str,
196
+ *,
197
+ timeout: float = 5.0,
198
+ ) -> bool:
199
+ return self.context().delete(
200
+ name,
201
+ endpoint,
202
+ headers,
203
+ protocol_version,
204
+ session_id,
205
+ reason,
206
+ timeout=timeout,
207
+ )
208
+
209
+ def close_state(self, state: dict[str, Any], reason: str) -> bool:
210
+ return self.context().close_state(state, reason)
211
+
212
+ def cleanup_stale(
213
+ self,
214
+ name: str,
215
+ url: str,
216
+ headers: dict[str, str],
217
+ protocol_version: str,
218
+ *,
219
+ keep_session_id: str | None = None,
220
+ ) -> None:
221
+ self.context().cleanup_stale(
222
+ name,
223
+ url,
224
+ headers,
225
+ protocol_version,
226
+ keep_session_id=keep_session_id,
227
+ )
228
+
229
+
230
+ __all__ = [
231
+ "ChannelSessionCompatibilityApi",
232
+ "ChannelSessionConfigPorts",
233
+ "ChannelSessionContext",
234
+ "ChannelSessionHttpPorts",
235
+ "ChannelSessionStatePorts",
236
+ ]
@@ -0,0 +1,350 @@
1
+ """Terminal channel process orchestration bounded context."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+ from typing import Any, Callable
8
+
9
+ from .channel_terminal_dispatch import (
10
+ ChannelDirectProcessPorts,
11
+ ChannelTerminalDispatchService,
12
+ ChannelTerminalDispatchSettings,
13
+ ChannelTerminalProxyPorts,
14
+ )
15
+ from .channel_terminal_proxy import (
16
+ ChannelTerminalIO,
17
+ ChannelTerminalPolicy,
18
+ ChannelTerminalPolling,
19
+ ChannelTerminalProcess,
20
+ ChannelTerminalServices,
21
+ ChannelWindowsConsole,
22
+ ChannelWindowsServices,
23
+ )
24
+
25
+
26
+ @dataclass(frozen=True, slots=True)
27
+ class ChannelTerminalProcessPorts:
28
+ popen: Callable[..., Any]
29
+ write_record: Callable[[Path | None, int, list[str]], None]
30
+ terminate: Callable[[Any, str], None]
31
+ release_record: Callable[[Path | None, int], None]
32
+
33
+
34
+ @dataclass(frozen=True, slots=True)
35
+ class ChannelTerminalPolicyPorts:
36
+ initial_cursor: Callable[[], int]
37
+ enter_bytes: Callable[[str | bytes | None], bytes]
38
+ enter_label: Callable[[bytes], str]
39
+ enter_is_fixed: Callable[[], bool]
40
+ unseen_retry_seconds: Callable[[], float]
41
+ inflight_is_stale: Callable[..., bool]
42
+ log: Callable[[str, str], None]
43
+
44
+
45
+ @dataclass(frozen=True, slots=True)
46
+ class ChannelTerminalPollingPorts:
47
+ inject_compact: Callable[..., Any]
48
+ file_marker: Callable[[], Any]
49
+ should_check: Callable[..., bool]
50
+ active_tool_call: Callable[..., bool]
51
+ inject_pending: Callable[..., Any]
52
+ wake_state: Callable[[int], Any]
53
+ inflight_effects: Callable[[], Any]
54
+
55
+
56
+ @dataclass(frozen=True, slots=True)
57
+ class ChannelTerminalIoPorts:
58
+ terminal_size: Callable[[int], tuple[int, int]]
59
+ apply_terminal_size: Callable[[int, int, int], bool]
60
+ write_all: Callable[[Any, bytes], None]
61
+ mouse_filter: Callable[[], Any]
62
+ observed_enter: Callable[..., bytes | None]
63
+ reset_input_mode: Callable[[], None]
64
+
65
+
66
+ @dataclass(frozen=True, slots=True)
67
+ class ChannelTerminalWindowsPorts:
68
+ input_supported: Callable[[], bool]
69
+ run_proxy: Callable[..., int]
70
+ reset_input_mode: Callable[[], None]
71
+ mouse_guard: Callable[[], Any]
72
+ input_writer: Callable[[], Any]
73
+ startup_grace_seconds: Callable[[], float]
74
+ reset_interval_seconds: Callable[[float], float]
75
+ active_turn: Callable[[], bool]
76
+ retry_submit: Callable[..., tuple[int, float]]
77
+ sleep: Callable[[float], None]
78
+
79
+
80
+ @dataclass(frozen=True, slots=True)
81
+ class ChannelTerminalDispatchPorts:
82
+ platform_name: str
83
+ stdin_isatty: Callable[[], bool]
84
+ stdout_isatty: Callable[[], bool]
85
+ direct_call: Callable[..., int]
86
+ run_windows: Callable[..., int]
87
+ run_posix: Callable[..., int]
88
+ prepare_delivery: Callable[[], Any]
89
+
90
+
91
+ @dataclass(frozen=True, slots=True)
92
+ class ChannelTerminalContext:
93
+ process: ChannelTerminalProcessPorts
94
+ policy: ChannelTerminalPolicyPorts
95
+ polling: ChannelTerminalPollingPorts
96
+ io: ChannelTerminalIoPorts
97
+ windows: ChannelTerminalWindowsPorts
98
+ dispatch_ports: ChannelTerminalDispatchPorts
99
+
100
+ def process_services(self) -> ChannelTerminalProcess:
101
+ return ChannelTerminalProcess(
102
+ popen=self.process.popen,
103
+ write_child_record=self.process.write_record,
104
+ terminate_child=self.process.terminate,
105
+ release_child_record=self.process.release_record,
106
+ )
107
+
108
+ def policy_services(self) -> ChannelTerminalPolicy:
109
+ return ChannelTerminalPolicy(
110
+ initial_cursor=self.policy.initial_cursor,
111
+ enter_bytes=self.policy.enter_bytes,
112
+ enter_label=self.policy.enter_label,
113
+ enter_is_fixed=self.policy.enter_is_fixed,
114
+ unseen_retry_seconds=self.policy.unseen_retry_seconds,
115
+ inflight_is_stale=self.policy.inflight_is_stale,
116
+ log=self.policy.log,
117
+ )
118
+
119
+ def polling_services(self) -> ChannelTerminalPolling:
120
+ return ChannelTerminalPolling(
121
+ inject_compact=self.polling.inject_compact,
122
+ file_marker=self.polling.file_marker,
123
+ should_check=self.polling.should_check,
124
+ active_tool_call=self.polling.active_tool_call,
125
+ inject_pending=self.polling.inject_pending,
126
+ wake_state=self.polling.wake_state,
127
+ inflight_effects=self.polling.inflight_effects,
128
+ )
129
+
130
+ def posix_services(self) -> ChannelTerminalServices:
131
+ return ChannelTerminalServices(
132
+ process=self.process_services(),
133
+ io=ChannelTerminalIO(
134
+ terminal_size=self.io.terminal_size,
135
+ apply_terminal_size=self.io.apply_terminal_size,
136
+ write_all=self.io.write_all,
137
+ mouse_filter=self.io.mouse_filter,
138
+ observed_enter=self.io.observed_enter,
139
+ reset_input_mode=self.io.reset_input_mode,
140
+ ),
141
+ policy=self.policy_services(),
142
+ polling=self.polling_services(),
143
+ )
144
+
145
+ def windows_services(self) -> ChannelWindowsServices:
146
+ return ChannelWindowsServices(
147
+ process=self.process_services(),
148
+ policy=self.policy_services(),
149
+ polling=self.polling_services(),
150
+ console=ChannelWindowsConsole(
151
+ reset_input_mode=self.windows.reset_input_mode,
152
+ mouse_guard=self.windows.mouse_guard,
153
+ input_writer=self.windows.input_writer,
154
+ startup_grace_seconds=self.windows.startup_grace_seconds,
155
+ reset_interval_seconds=self.windows.reset_interval_seconds,
156
+ active_turn=self.windows.active_turn,
157
+ retry_submit=self.windows.retry_submit,
158
+ sleep=self.windows.sleep,
159
+ ),
160
+ )
161
+
162
+ def run_windows(
163
+ self,
164
+ cmd: list[str],
165
+ env: dict[str, str],
166
+ **options: Any,
167
+ ) -> int:
168
+ options.pop("normalize_bare_cr_for_synthetic_enter", None)
169
+ options.pop("channel_wake_bracketed_paste", None)
170
+ return self.windows.run_proxy(
171
+ cmd,
172
+ env,
173
+ self.windows_services(),
174
+ **options,
175
+ )
176
+
177
+ def dispatch_service(self) -> ChannelTerminalDispatchService:
178
+ return ChannelTerminalDispatchService(
179
+ settings=ChannelTerminalDispatchSettings(
180
+ platform_name=self.dispatch_ports.platform_name,
181
+ stdin_isatty=self.dispatch_ports.stdin_isatty,
182
+ stdout_isatty=self.dispatch_ports.stdout_isatty,
183
+ ),
184
+ proxy=ChannelTerminalProxyPorts(
185
+ windows_supported=self.windows.input_supported,
186
+ run_windows=self.dispatch_ports.run_windows,
187
+ run_posix=self.dispatch_ports.run_posix,
188
+ posix_services=self.posix_services,
189
+ ),
190
+ direct=ChannelDirectProcessPorts(
191
+ call=self.dispatch_ports.direct_call,
192
+ popen=self.process.popen,
193
+ write_record=self.process.write_record,
194
+ terminate=self.process.terminate,
195
+ release_record=self.process.release_record,
196
+ ),
197
+ log=self.policy.log,
198
+ )
199
+
200
+ def dispatch(
201
+ self,
202
+ cmd: list[str],
203
+ env: dict[str, str],
204
+ *,
205
+ inject_channel_messages: bool = True,
206
+ inject_web_chat_only: bool = False,
207
+ wake_for_llm_delivery: bool = False,
208
+ synthetic_enter_bytes: str | bytes | None = None,
209
+ normalize_bare_cr_for_synthetic_enter: bool = True,
210
+ channel_wake_submit_retries: int = 1,
211
+ channel_wake_confirm_submit: bool = False,
212
+ channel_wake_bracketed_paste: bool = False,
213
+ channel_wake_submit_delay_seconds: float | None = None,
214
+ tracked_child_pid_path: Path | None = None,
215
+ ) -> int:
216
+ if inject_channel_messages:
217
+ self.dispatch_ports.prepare_delivery()
218
+ return self.dispatch_service().dispatch(
219
+ cmd,
220
+ env,
221
+ inject_channel_messages=inject_channel_messages,
222
+ inject_web_chat_only=inject_web_chat_only,
223
+ wake_for_llm_delivery=wake_for_llm_delivery,
224
+ synthetic_enter_bytes=synthetic_enter_bytes,
225
+ normalize_bare_cr_for_synthetic_enter=(
226
+ normalize_bare_cr_for_synthetic_enter
227
+ ),
228
+ channel_wake_submit_retries=channel_wake_submit_retries,
229
+ channel_wake_confirm_submit=channel_wake_confirm_submit,
230
+ channel_wake_bracketed_paste=channel_wake_bracketed_paste,
231
+ channel_wake_submit_delay_seconds=channel_wake_submit_delay_seconds,
232
+ tracked_child_pid_path=tracked_child_pid_path,
233
+ )
234
+
235
+ def call_direct(
236
+ self,
237
+ cmd: list[str],
238
+ env: dict[str, str],
239
+ pid_path: Path | None = None,
240
+ ) -> int:
241
+ return self.dispatch_service().call_direct(cmd, env, pid_path)
242
+
243
+
244
+ @dataclass(frozen=True, slots=True)
245
+ class ChannelTerminalCompatibilityApi:
246
+ context: Callable[[], ChannelTerminalContext]
247
+
248
+ def process_services(self) -> ChannelTerminalProcess:
249
+ return self.context().process_services()
250
+
251
+ def policy_services(self) -> ChannelTerminalPolicy:
252
+ return self.context().policy_services()
253
+
254
+ def polling_services(self) -> ChannelTerminalPolling:
255
+ return self.context().polling_services()
256
+
257
+ def posix_services(self) -> ChannelTerminalServices:
258
+ return self.context().posix_services()
259
+
260
+ def windows_services(self) -> ChannelWindowsServices:
261
+ return self.context().windows_services()
262
+
263
+ def dispatch_service(self) -> ChannelTerminalDispatchService:
264
+ return self.context().dispatch_service()
265
+
266
+ def run_windows(
267
+ self,
268
+ cmd: list[str],
269
+ env: dict[str, str],
270
+ *,
271
+ inject_channel_messages: bool = True,
272
+ inject_web_chat_only: bool = False,
273
+ wake_for_llm_delivery: bool = False,
274
+ synthetic_enter_bytes: str | bytes | None = None,
275
+ normalize_bare_cr_for_synthetic_enter: bool = True,
276
+ channel_wake_submit_retries: int = 1,
277
+ channel_wake_confirm_submit: bool = False,
278
+ channel_wake_bracketed_paste: bool = False,
279
+ channel_wake_submit_delay_seconds: float | None = None,
280
+ tracked_child_pid_path: Path | None = None,
281
+ ) -> int:
282
+ return self.context().run_windows(
283
+ cmd,
284
+ env,
285
+ inject_channel_messages=inject_channel_messages,
286
+ inject_web_chat_only=inject_web_chat_only,
287
+ wake_for_llm_delivery=wake_for_llm_delivery,
288
+ synthetic_enter_bytes=synthetic_enter_bytes,
289
+ normalize_bare_cr_for_synthetic_enter=(
290
+ normalize_bare_cr_for_synthetic_enter
291
+ ),
292
+ channel_wake_submit_retries=channel_wake_submit_retries,
293
+ channel_wake_confirm_submit=channel_wake_confirm_submit,
294
+ channel_wake_bracketed_paste=channel_wake_bracketed_paste,
295
+ channel_wake_submit_delay_seconds=channel_wake_submit_delay_seconds,
296
+ tracked_child_pid_path=tracked_child_pid_path,
297
+ )
298
+
299
+ def dispatch(
300
+ self,
301
+ cmd: list[str],
302
+ env: dict[str, str],
303
+ *,
304
+ inject_channel_messages: bool = True,
305
+ inject_web_chat_only: bool = False,
306
+ wake_for_llm_delivery: bool = False,
307
+ synthetic_enter_bytes: str | bytes | None = None,
308
+ normalize_bare_cr_for_synthetic_enter: bool = True,
309
+ channel_wake_submit_retries: int = 1,
310
+ channel_wake_confirm_submit: bool = False,
311
+ channel_wake_bracketed_paste: bool = False,
312
+ channel_wake_submit_delay_seconds: float | None = None,
313
+ tracked_child_pid_path: Path | None = None,
314
+ ) -> int:
315
+ return self.context().dispatch(
316
+ cmd,
317
+ env,
318
+ inject_channel_messages=inject_channel_messages,
319
+ inject_web_chat_only=inject_web_chat_only,
320
+ wake_for_llm_delivery=wake_for_llm_delivery,
321
+ synthetic_enter_bytes=synthetic_enter_bytes,
322
+ normalize_bare_cr_for_synthetic_enter=(
323
+ normalize_bare_cr_for_synthetic_enter
324
+ ),
325
+ channel_wake_submit_retries=channel_wake_submit_retries,
326
+ channel_wake_confirm_submit=channel_wake_confirm_submit,
327
+ channel_wake_bracketed_paste=channel_wake_bracketed_paste,
328
+ channel_wake_submit_delay_seconds=channel_wake_submit_delay_seconds,
329
+ tracked_child_pid_path=tracked_child_pid_path,
330
+ )
331
+
332
+ def call_direct(
333
+ self,
334
+ cmd: list[str],
335
+ env: dict[str, str],
336
+ pid_path: Path | None = None,
337
+ ) -> int:
338
+ return self.context().call_direct(cmd, env, pid_path)
339
+
340
+
341
+ __all__ = [
342
+ "ChannelTerminalCompatibilityApi",
343
+ "ChannelTerminalContext",
344
+ "ChannelTerminalDispatchPorts",
345
+ "ChannelTerminalIoPorts",
346
+ "ChannelTerminalPolicyPorts",
347
+ "ChannelTerminalPollingPorts",
348
+ "ChannelTerminalProcessPorts",
349
+ "ChannelTerminalWindowsPorts",
350
+ ]