@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,248 @@
1
+ from __future__ import annotations
2
+
3
+ import html
4
+ import json
5
+ import re
6
+ from dataclasses import dataclass
7
+ from typing import Any, Callable
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class PseudoToolHistoryServices:
12
+ tool_names: Callable[[dict[str, Any]], set[str]]
13
+ match_tool_name: Callable[[str, set[str]], str | None]
14
+ resolve_emitted_name: Callable[[str, dict[str, Any]], str]
15
+ normalize_arguments: Callable[[str, Any], dict[str, Any]]
16
+ log: Callable[[str, str], None]
17
+
18
+
19
+ _INVOKE_RE = re.compile(
20
+ r"(?is)(?:^|\n)[ \t]*(?:court[ \t]*(?:\r?\n)+)?<invoke\s+name=[\"'][^\"']+[\"'][\s\S]*?</invoke>[ \t]*(?=\n|$)"
21
+ )
22
+ _INVOKE_CALL_RE = re.compile(
23
+ r"(?is)(?:^|\n)[ \t]*(?:court[ \t]*(?:\r?\n)+)?"
24
+ r"<invoke\s+name=[\"'](?P<name>[^\"']+)[\"'][^>]*>(?P<body>[\s\S]*?)</invoke>[ \t]*(?=\n|$)"
25
+ )
26
+ _INVOKE_OPEN_RE = re.compile(
27
+ r"(?is)(?:^|\n)[ \t]*(?:court[ \t]*(?:\r?\n)+)?<invoke\s+name=[\"'](?P<name>[^\"']+)[\"'][^>]*>"
28
+ )
29
+ _XML_RE = re.compile(
30
+ r"(?is)(?:^|\n)[ \t]*(?:court[ \t]*(?:\r?\n)+)?<(?P<name>[A-Za-z_][\w.-]{0,96})\b[^>]*>[\s\S]*?</(?P=name)>[ \t]*(?=\n|$)"
31
+ )
32
+ _XML_CALL_RE = re.compile(
33
+ r"(?is)(?:^|\n)[ \t]*(?:court[ \t]*(?:\r?\n)+)?"
34
+ r"<(?P<name>[A-Za-z_][\w.-]{0,96})\b[^>]*>(?P<body>[\s\S]*?)</(?P=name)>[ \t]*(?=\n|$)"
35
+ )
36
+ _XML_OPEN_RE = re.compile(
37
+ r"(?is)(?:^|\n)[ \t]*(?:court[ \t]*(?:\r?\n)+)?<(?P<name>[A-Za-z_][\w.-]{0,96})\b[^>]*>"
38
+ )
39
+ _PARAMETER_RE = re.compile(
40
+ r"(?is)<parameter\s+name=[\"'](?P<name>[^\"']+)[\"'][^>]*>(?P<value>[\s\S]*?)</parameter>"
41
+ )
42
+ _CHILD_ARG_RE = re.compile(
43
+ r"(?is)<(?P<name>[A-Za-z_][\w.-]{0,96})\b[^>]*>(?P<value>[\s\S]*?)</(?P=name)>"
44
+ )
45
+
46
+
47
+ def request_tool_name_aliases(body: dict[str, Any]) -> set[str]:
48
+ aliases: set[str] = set()
49
+ tools = body.get("tools")
50
+ if not isinstance(tools, list):
51
+ return aliases
52
+ for tool in tools:
53
+ if not isinstance(tool, dict):
54
+ continue
55
+ name = str(tool.get("name") or "").strip()
56
+ if not name:
57
+ continue
58
+ lowered = name.lower()
59
+ aliases.add(lowered)
60
+ aliases.add(lowered.replace("-", "_"))
61
+ if "__" in lowered:
62
+ short = lowered.rsplit("__", 1)[-1]
63
+ aliases.add(short)
64
+ aliases.add(short.replace("-", "_"))
65
+ return aliases
66
+
67
+
68
+ def resolve_pseudo_xml_tool_name(
69
+ raw_name: str,
70
+ source_body: dict[str, Any] | None,
71
+ services: PseudoToolHistoryServices,
72
+ ) -> str | None:
73
+ if not isinstance(source_body, dict):
74
+ return None
75
+ raw = str(raw_name or "").strip()
76
+ if not raw:
77
+ return None
78
+ available = services.tool_names(source_body)
79
+ if not available:
80
+ return None
81
+ matched = services.match_tool_name(raw, available)
82
+ if matched:
83
+ return matched
84
+ aliases = request_tool_name_aliases(source_body)
85
+ lowered = raw.lower()
86
+ if lowered not in aliases and lowered.replace("-", "_") not in aliases:
87
+ return None
88
+ return services.resolve_emitted_name(raw, source_body)
89
+
90
+
91
+ def parse_pseudo_xml_tool_args(
92
+ tool_name: str,
93
+ body: str,
94
+ services: PseudoToolHistoryServices,
95
+ ) -> dict[str, Any]:
96
+ inner = str(body or "").strip()
97
+ args: dict[str, Any] = {}
98
+ for match in _PARAMETER_RE.finditer(inner):
99
+ key = str(match.group("name") or "").strip()
100
+ if key:
101
+ args[key] = html.unescape(str(match.group("value") or "")).strip()
102
+ if args:
103
+ return args
104
+ for match in _CHILD_ARG_RE.finditer(inner):
105
+ key = str(match.group("name") or "").strip()
106
+ if key and key.lower() not in {"invoke", tool_name.lower()}:
107
+ args[key] = html.unescape(str(match.group("value") or "")).strip()
108
+ if args:
109
+ return args
110
+ try:
111
+ parsed = json.loads(inner)
112
+ except json.JSONDecodeError:
113
+ parsed = None
114
+ if isinstance(parsed, dict):
115
+ return parsed
116
+ return services.normalize_arguments(tool_name, html.unescape(inner).strip())
117
+
118
+
119
+ def find_pseudo_xml_tool_start(
120
+ text: str,
121
+ source_body: dict[str, Any] | None,
122
+ services: PseudoToolHistoryServices,
123
+ ) -> int:
124
+ if not isinstance(source_body, dict) or "<" not in text:
125
+ return -1
126
+ matches = list(_INVOKE_CALL_RE.finditer(text)) + list(_XML_CALL_RE.finditer(text))
127
+ matches += list(_INVOKE_OPEN_RE.finditer(text)) + list(_XML_OPEN_RE.finditer(text))
128
+ for match in sorted(matches, key=lambda item: item.start()):
129
+ raw_name = str(match.group("name") or "")
130
+ if raw_name.lower() == "invoke":
131
+ continue
132
+ if resolve_pseudo_xml_tool_name(raw_name, source_body, services):
133
+ return match.start()
134
+ return -1
135
+
136
+
137
+ def parse_xml_pseudo_tool_calls(
138
+ text: str,
139
+ source_body: dict[str, Any] | None,
140
+ services: PseudoToolHistoryServices,
141
+ ) -> tuple[str, list[dict[str, Any]]]:
142
+ if not isinstance(source_body, dict) or "<" not in text:
143
+ return text, []
144
+ matches: list[tuple[int, int, str, str]] = []
145
+ for match in _INVOKE_CALL_RE.finditer(text):
146
+ matches.append(
147
+ (match.start(), match.end(), str(match.group("name") or ""), str(match.group("body") or ""))
148
+ )
149
+ for match in _XML_CALL_RE.finditer(text):
150
+ raw_name = str(match.group("name") or "")
151
+ if raw_name.lower() != "invoke":
152
+ matches.append(
153
+ (match.start(), match.end(), raw_name, str(match.group("body") or ""))
154
+ )
155
+ if not matches:
156
+ return text, []
157
+ visible_parts: list[str] = []
158
+ calls: list[dict[str, Any]] = []
159
+ position = 0
160
+ for start, end, raw_name, body in sorted(matches, key=lambda item: item[0]):
161
+ if start < position:
162
+ continue
163
+ matched_name = resolve_pseudo_xml_tool_name(raw_name, source_body, services)
164
+ if not matched_name:
165
+ continue
166
+ visible_parts.append(text[position:start])
167
+ args = parse_pseudo_xml_tool_args(matched_name, body, services)
168
+ calls.append(
169
+ {"function": {"name": matched_name, "arguments": args}, "id": f"xml:{raw_name}:{start}"}
170
+ )
171
+ position = end
172
+ if not calls:
173
+ return text, []
174
+ visible_parts.append(text[position:])
175
+ return "".join(visible_parts), calls
176
+
177
+
178
+ def sanitize_assistant_pseudo_tool_history(
179
+ body: dict[str, Any],
180
+ services: PseudoToolHistoryServices,
181
+ ) -> dict[str, Any]:
182
+ messages = body.get("messages")
183
+ if not isinstance(messages, list):
184
+ return body
185
+ aliases = request_tool_name_aliases(body)
186
+ sanitized: list[Any] = []
187
+ removed_blocks = 0
188
+ for message in messages:
189
+ next_message, removed = _sanitize_assistant_message(message, aliases)
190
+ sanitized.append(next_message)
191
+ removed_blocks += removed
192
+ if not removed_blocks:
193
+ return body
194
+ output = dict(body)
195
+ output["messages"] = sanitized
196
+ services.log("INFO", f"sanitized assistant pseudo tool-call text blocks={removed_blocks}")
197
+ return output
198
+
199
+
200
+ def _sanitize_assistant_message(message: Any, aliases: set[str]) -> tuple[Any, int]:
201
+ if not isinstance(message, dict) or str(message.get("role") or "") != "assistant":
202
+ return message, 0
203
+ content = message.get("content")
204
+ if isinstance(content, str):
205
+ text, count = _sanitize_text(content, aliases)
206
+ if not count:
207
+ return message, 0
208
+ output = dict(message)
209
+ output["content"] = text.strip()
210
+ return output, count
211
+ if not isinstance(content, list):
212
+ return message, 0
213
+ output_blocks: list[Any] = []
214
+ removed = 0
215
+ for block in content:
216
+ if not isinstance(block, dict) or block.get("type") != "text":
217
+ output_blocks.append(block)
218
+ continue
219
+ text, count = _sanitize_text(str(block.get("text") or ""), aliases)
220
+ if count:
221
+ next_block = dict(block)
222
+ next_block["text"] = text.strip()
223
+ output_blocks.append(next_block)
224
+ removed += count
225
+ else:
226
+ output_blocks.append(block)
227
+ if not removed:
228
+ return message, 0
229
+ output = dict(message)
230
+ output["content"] = output_blocks
231
+ return output, removed
232
+
233
+
234
+ def _sanitize_text(text: str, aliases: set[str]) -> tuple[str, int]:
235
+ output, count = _INVOKE_RE.subn("\n", text)
236
+ if not aliases:
237
+ return output, count
238
+ removed = 0
239
+
240
+ def replace_xml(match: re.Match[str]) -> str:
241
+ nonlocal removed
242
+ name = str(match.group("name") or "").strip().lower()
243
+ if name not in aliases and name.replace("-", "_") not in aliases:
244
+ return match.group(0)
245
+ removed += 1
246
+ return "\n"
247
+
248
+ return _XML_RE.sub(replace_xml, output), count + removed
@@ -0,0 +1,112 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Callable
5
+
6
+
7
+ @dataclass(frozen=True, slots=True)
8
+ class ToolResultProjectionServices:
9
+ is_read_unchanged: Callable[[str, str], bool]
10
+ truncate: Callable[[str, int], str]
11
+ result_limit: int
12
+
13
+
14
+ def project_tool_result(
15
+ tool_name: str,
16
+ tool_input_text: str,
17
+ result_text: str,
18
+ is_error: bool,
19
+ services: ToolResultProjectionServices,
20
+ *,
21
+ prior_success_text: str = "",
22
+ include_prior_success: bool = False,
23
+ in_plan_mode: bool = False,
24
+ ) -> tuple[str, str]:
25
+ if is_error:
26
+ tool_text = (
27
+ f"Tool `{tool_name}` failed.\n"
28
+ f"Input:\n{tool_input_text}\n\n"
29
+ f"Error:\n{result_text}"
30
+ )
31
+ tool_summary = (
32
+ f"The `{tool_name}` tool call above failed. Its input was {tool_input_text}. "
33
+ "Use the error output to choose a different next step; do not blindly repeat it."
34
+ )
35
+ return tool_text, tool_summary
36
+ if services.is_read_unchanged(tool_name, result_text):
37
+ return _project_unchanged_read(
38
+ tool_input_text,
39
+ result_text,
40
+ services,
41
+ prior_success_text=prior_success_text,
42
+ include_prior_success=include_prior_success,
43
+ in_plan_mode=in_plan_mode,
44
+ )
45
+ tool_text = (
46
+ f"Tool `{tool_name}` completed successfully.\n"
47
+ f"Input:\n{tool_input_text}\n\n"
48
+ f"Result:\n{result_text}\n\n"
49
+ "If this result satisfies the user's request, provide the final answer now. "
50
+ f"Do not call `{tool_name}` again with the same arguments."
51
+ )
52
+ tool_summary = (
53
+ f"The `{tool_name}` tool call above already completed successfully. "
54
+ "Treat its tool output as authoritative. Do not repeat the same or equivalent "
55
+ f"`{tool_name}` call; continue with the next required concrete tool call or final answer."
56
+ )
57
+ return tool_text, tool_summary
58
+
59
+
60
+ def _project_unchanged_read(
61
+ tool_input_text: str,
62
+ result_text: str,
63
+ services: ToolResultProjectionServices,
64
+ *,
65
+ prior_success_text: str,
66
+ include_prior_success: bool,
67
+ in_plan_mode: bool,
68
+ ) -> tuple[str, str]:
69
+ if not include_prior_success:
70
+ return (
71
+ "Tool `Read` returned an unchanged/no-op cache result for content that was already "
72
+ "available earlier in this conversation. No new file content was produced by this "
73
+ "historical duplicate observation.",
74
+ "",
75
+ )
76
+ plan_hint = (
77
+ " If you are in Plan Mode and the plan file is already complete, call ExitPlanMode."
78
+ if in_plan_mode
79
+ else ""
80
+ )
81
+ if not prior_success_text:
82
+ tool_text = (
83
+ "Tool `Read` returned a no-op unchanged result.\n"
84
+ f"Input:\n{tool_input_text}\n\n"
85
+ f"Result:\n{result_text}\n\n"
86
+ "No previous successful Read result for this exact input is available in the converted context. "
87
+ "Do not repeat the same Read. If the content is still needed, read a different or broader range once; "
88
+ "otherwise proceed with the next distinct step."
89
+ )
90
+ tool_summary = (
91
+ "The latest `Read` result says the file/range is unchanged, but no prior exact Read content is available "
92
+ "in this converted context. Do not loop on the same Read; either read a different range once or continue "
93
+ f"with the next distinct step.{plan_hint}"
94
+ )
95
+ return tool_text, tool_summary
96
+ previous = (
97
+ "\n\nPrevious successful Read result for this exact input remains authoritative:\n"
98
+ f"{services.truncate(prior_success_text, services.result_limit)}"
99
+ )
100
+ tool_text = (
101
+ "Tool `Read` returned a no-op unchanged result.\n"
102
+ f"Input:\n{tool_input_text}\n\n"
103
+ f"Result:\n{result_text}"
104
+ f"{previous}\n\n"
105
+ "This is not new file content. It means the previous successful Read result for the same input is still current."
106
+ )
107
+ tool_summary = (
108
+ "The latest `Read` result is a Claude Code no-op/cache result: no file content changed and no new observation was produced. "
109
+ "Use the previous successful Read result for this exact input as the current observation, then choose the next distinct step."
110
+ f"{plan_hint}"
111
+ )
112
+ return tool_text, tool_summary
@@ -0,0 +1,160 @@
1
+ """Production provider adapters for common HTTP authentication dialects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from .architecture import (
8
+ ProviderAdapter,
9
+ )
10
+ from .provider_descriptor import ProviderDescriptor, ProviderDescriptorRegistry
11
+ from .registry import AdapterRegistry
12
+ from .runtime_constants import PROVIDER_ALIASES as LEGACY_PROVIDER_ALIASES
13
+ from .providers.base import (
14
+ HttpBearerProviderAdapter,
15
+ NoAuthProviderAdapter,
16
+ )
17
+ from .providers.anthropic import AnthropicProviderAdapter
18
+ from .providers.constants import PROVIDER_DEFAULT_BASE_URLS, ZAI_MODEL_FALLBACK_IDS
19
+ from .providers.native import AgyProviderAdapter, CodexProviderAdapter
20
+ from .providers.ollama import OllamaCloudProviderAdapter, OllamaProviderAdapter
21
+ from .providers.openrouter import OpenRouterProviderAdapter
22
+ from .providers.lm_studio import LMStudioProviderAdapter
23
+ from .providers.nim import SelfHostedNimProviderAdapter
24
+ from .providers.nvidia import NvidiaHostedProviderAdapter
25
+ from .providers.vllm import VllmProviderAdapter
26
+ from .providers.deepseek import DeepSeekProviderAdapter
27
+ from .providers.zai import ZaiProviderAdapter
28
+ from .providers.kimi import KimiProviderAdapter
29
+ from .providers.fireworks import FireworksProviderAdapter
30
+ from .providers.meta import MetaModelProviderAdapter
31
+ from .providers.github_copilot_oauth import (
32
+ GitHubCopilotOAuthProviderAdapter,
33
+ )
34
+ from .providers.opencode import OpenCodeProviderAdapter
35
+ from .providers.opencode_go import OpenCodeGoProviderAdapter
36
+ from .providers.catalog import COMPATIBLE_PROVIDER_SPECS, catalog_provider_factory
37
+ from .providers.anthropic_catalog import (
38
+ ANTHROPIC_COMPATIBLE_PROVIDER_SPECS,
39
+ anthropic_catalog_provider_factory,
40
+ )
41
+ from .providers.cloud import AzureOpenAIProviderAdapter, CodeBuddyCnProviderAdapter
42
+
43
+
44
+ PROVIDER_DESCRIPTORS = ProviderDescriptorRegistry(
45
+ (
46
+ ProviderDescriptor("anthropic", "Claude Native", AnthropicProviderAdapter),
47
+ ProviderDescriptor("agy", "AGY", AgyProviderAdapter),
48
+ ProviderDescriptor("codex", "Codex Native", CodexProviderAdapter),
49
+ ProviderDescriptor("ollama", "Ollama", OllamaProviderAdapter),
50
+ ProviderDescriptor("ollama-cloud", "Ollama Cloud", OllamaCloudProviderAdapter),
51
+ ProviderDescriptor("deepseek", "DeepSeek.com", DeepSeekProviderAdapter),
52
+ ProviderDescriptor("opencode", "OpenCode Zen", OpenCodeProviderAdapter),
53
+ ProviderDescriptor("opencode-go", "OpenCode Go", OpenCodeGoProviderAdapter),
54
+ ProviderDescriptor("kimi", "Kimi.com", KimiProviderAdapter),
55
+ ProviderDescriptor("zai", "Z.AI GLM", ZaiProviderAdapter),
56
+ ProviderDescriptor("vllm", "vLLM", VllmProviderAdapter),
57
+ ProviderDescriptor("lm-studio", "LM Studio", LMStudioProviderAdapter),
58
+ ProviderDescriptor("nvidia-hosted", "Nvidia Hosted", NvidiaHostedProviderAdapter),
59
+ ProviderDescriptor("self-hosted-nim", "Self Hosted NIM", SelfHostedNimProviderAdapter),
60
+ ProviderDescriptor("openrouter", "OpenRouter", OpenRouterProviderAdapter),
61
+ ProviderDescriptor("fireworks", "Fireworks.ai", FireworksProviderAdapter),
62
+ ProviderDescriptor(
63
+ "meta",
64
+ "Meta Model API",
65
+ MetaModelProviderAdapter,
66
+ aliases=("muse", "muse-spark", "meta-model-api"),
67
+ ),
68
+ ProviderDescriptor(
69
+ "github-copilot-oauth",
70
+ "GitHub Copilot OAuth",
71
+ GitHubCopilotOAuthProviderAdapter,
72
+ aliases=("copilot-oauth", "github-oauth"),
73
+ ),
74
+ ProviderDescriptor(
75
+ "azure",
76
+ "Azure OpenAI",
77
+ AzureOpenAIProviderAdapter,
78
+ aliases=("azure-openai",),
79
+ ),
80
+ ProviderDescriptor(
81
+ "codebuddy-cn",
82
+ "CodeBuddy CN",
83
+ CodeBuddyCnProviderAdapter,
84
+ aliases=("cbcn", "codebuddy"),
85
+ ),
86
+ *(
87
+ ProviderDescriptor(
88
+ spec.name,
89
+ spec.label,
90
+ catalog_provider_factory(spec),
91
+ aliases=spec.aliases,
92
+ )
93
+ for spec in COMPATIBLE_PROVIDER_SPECS
94
+ ),
95
+ *(
96
+ ProviderDescriptor(
97
+ spec.name,
98
+ spec.label,
99
+ anthropic_catalog_provider_factory(spec),
100
+ aliases=spec.aliases,
101
+ )
102
+ for spec in ANTHROPIC_COMPATIBLE_PROVIDER_SPECS
103
+ ),
104
+ )
105
+ )
106
+ PROVIDER_ADAPTERS: AdapterRegistry[ProviderAdapter] = AdapterRegistry()
107
+ PROVIDER_LABELS: dict[str, str] = {
108
+ descriptor.normalized_name: descriptor.label
109
+ for descriptor in PROVIDER_DESCRIPTORS.descriptors()
110
+ }
111
+ PROVIDER_ALIASES: dict[str, str] = {
112
+ **LEGACY_PROVIDER_ALIASES,
113
+ **PROVIDER_DESCRIPTORS.aliases(),
114
+ }
115
+
116
+ for _descriptor in PROVIDER_DESCRIPTORS.descriptors():
117
+ PROVIDER_ADAPTERS.register(
118
+ _descriptor.normalized_name,
119
+ _descriptor.create,
120
+ )
121
+
122
+
123
+ def provider_default_configurations() -> dict[str, dict[str, Any]]:
124
+ """Build fresh provider defaults from the registered concrete adapters."""
125
+
126
+ return {
127
+ name: dict(PROVIDER_ADAPTERS.create(name).default_configuration())
128
+ for name in PROVIDER_ADAPTERS.names()
129
+ }
130
+
131
+
132
+ __all__ = [
133
+ "PROVIDER_ADAPTERS",
134
+ "PROVIDER_ALIASES",
135
+ "PROVIDER_DEFAULT_BASE_URLS",
136
+ "PROVIDER_DESCRIPTORS",
137
+ "PROVIDER_LABELS",
138
+ "provider_default_configurations",
139
+ "HttpBearerProviderAdapter",
140
+ "NoAuthProviderAdapter",
141
+ "AgyProviderAdapter",
142
+ "AnthropicProviderAdapter",
143
+ "CodexProviderAdapter",
144
+ "DeepSeekProviderAdapter",
145
+ "FireworksProviderAdapter",
146
+ "KimiProviderAdapter",
147
+ "GitHubCopilotOAuthProviderAdapter",
148
+ "LMStudioProviderAdapter",
149
+ "MetaModelProviderAdapter",
150
+ "NvidiaHostedProviderAdapter",
151
+ "OllamaCloudProviderAdapter",
152
+ "OllamaProviderAdapter",
153
+ "OpenCodeGoProviderAdapter",
154
+ "OpenCodeProviderAdapter",
155
+ "OpenRouterProviderAdapter",
156
+ "SelfHostedNimProviderAdapter",
157
+ "VllmProviderAdapter",
158
+ "ZaiProviderAdapter",
159
+ "ZAI_MODEL_FALLBACK_IDS",
160
+ ]