code-muse 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (394) hide show
  1. code_muse/__init__.py +26 -0
  2. code_muse/__main__.py +10 -0
  3. code_muse/agents/__init__.py +31 -0
  4. code_muse/agents/_builder.py +214 -0
  5. code_muse/agents/_compaction.py +506 -0
  6. code_muse/agents/_diagnostics.py +171 -0
  7. code_muse/agents/_history.py +382 -0
  8. code_muse/agents/_key_listeners.py +148 -0
  9. code_muse/agents/_non_streaming_render.py +148 -0
  10. code_muse/agents/_runtime.py +596 -0
  11. code_muse/agents/agent_creator_agent.py +603 -0
  12. code_muse/agents/agent_helios.py +47 -0
  13. code_muse/agents/agent_manager.py +740 -0
  14. code_muse/agents/agent_muse.py +78 -0
  15. code_muse/agents/agent_planning.py +44 -0
  16. code_muse/agents/agent_qa_melpomene.py +207 -0
  17. code_muse/agents/base_agent.py +194 -0
  18. code_muse/agents/event_stream_handler.py +361 -0
  19. code_muse/agents/json_agent.py +201 -0
  20. code_muse/agents/prompt_v3.py +521 -0
  21. code_muse/agents/subagent_stream_handler.py +273 -0
  22. code_muse/callbacks.py +941 -0
  23. code_muse/chatgpt_codex_client.py +333 -0
  24. code_muse/claude_cache_client.py +853 -0
  25. code_muse/cli_runner/__init__.py +319 -0
  26. code_muse/cli_runner/args.py +63 -0
  27. code_muse/cli_runner/loop.py +510 -0
  28. code_muse/cli_runner/resume.py +72 -0
  29. code_muse/cli_runner/runner.py +161 -0
  30. code_muse/command_line/__init__.py +1 -0
  31. code_muse/command_line/add_model_menu.py +1331 -0
  32. code_muse/command_line/agent_menu.py +674 -0
  33. code_muse/command_line/attachments.py +397 -0
  34. code_muse/command_line/autosave_menu.py +709 -0
  35. code_muse/command_line/clipboard.py +528 -0
  36. code_muse/command_line/colors_menu.py +530 -0
  37. code_muse/command_line/command_handler.py +262 -0
  38. code_muse/command_line/command_registry.py +150 -0
  39. code_muse/command_line/config_commands.py +711 -0
  40. code_muse/command_line/core_commands.py +740 -0
  41. code_muse/command_line/diff_menu.py +865 -0
  42. code_muse/command_line/file_path_completion.py +73 -0
  43. code_muse/command_line/load_context_completion.py +57 -0
  44. code_muse/command_line/model_picker_completion.py +512 -0
  45. code_muse/command_line/model_settings_menu.py +983 -0
  46. code_muse/command_line/onboarding_slides.py +162 -0
  47. code_muse/command_line/onboarding_wizard.py +337 -0
  48. code_muse/command_line/pagination.py +41 -0
  49. code_muse/command_line/pin_command_completion.py +329 -0
  50. code_muse/command_line/prompt_toolkit_completion.py +886 -0
  51. code_muse/command_line/session_commands.py +304 -0
  52. code_muse/command_line/shell_passthrough.py +145 -0
  53. code_muse/command_line/skills_completion.py +158 -0
  54. code_muse/command_line/types.py +18 -0
  55. code_muse/command_line/uc_menu.py +908 -0
  56. code_muse/command_line/utils.py +105 -0
  57. code_muse/command_line/wiggum_state.py +77 -0
  58. code_muse/config.py +1138 -0
  59. code_muse/config_agent.py +168 -0
  60. code_muse/config_appearance.py +241 -0
  61. code_muse/config_model.py +357 -0
  62. code_muse/config_security.py +73 -0
  63. code_muse/error_logging.py +132 -0
  64. code_muse/evals/__init__.py +35 -0
  65. code_muse/evals/eval_helpers.py +81 -0
  66. code_muse/evals/eval_runner.py +299 -0
  67. code_muse/evals/sample_evals/__init__.py +1 -0
  68. code_muse/evals/sample_evals/eval_frugal_reads.py +59 -0
  69. code_muse/evals/sample_evals/eval_memory_planning.py +31 -0
  70. code_muse/evals/sample_evals/eval_shell_efficiency.py +39 -0
  71. code_muse/evals/sample_evals/eval_tool_masking.py +33 -0
  72. code_muse/fs_scan_cache/__init__.py +31 -0
  73. code_muse/fs_scan_cache/invalidation_hooks.py +89 -0
  74. code_muse/fs_scan_cache/scan_cache_core.cpython-314-darwin.so +0 -0
  75. code_muse/fs_scan_cache/scan_cache_core.pyx +203 -0
  76. code_muse/fs_scan_cache/tool_integration.py +309 -0
  77. code_muse/fs_scan_cache/ttl_policy.py +44 -0
  78. code_muse/gemini_code_assist.py +383 -0
  79. code_muse/gemini_model.py +838 -0
  80. code_muse/hook_engine/README.md +105 -0
  81. code_muse/hook_engine/__init__.py +21 -0
  82. code_muse/hook_engine/aliases.py +153 -0
  83. code_muse/hook_engine/engine.py +221 -0
  84. code_muse/hook_engine/executor.py +347 -0
  85. code_muse/hook_engine/matcher.py +154 -0
  86. code_muse/hook_engine/models.py +245 -0
  87. code_muse/hook_engine/registry.py +114 -0
  88. code_muse/hook_engine/trust.py +268 -0
  89. code_muse/hook_engine/validator.py +144 -0
  90. code_muse/http_utils.py +360 -0
  91. code_muse/keymap.py +128 -0
  92. code_muse/list_filtering.py +26 -0
  93. code_muse/main.py +10 -0
  94. code_muse/messaging/__init__.py +259 -0
  95. code_muse/messaging/bus.py +621 -0
  96. code_muse/messaging/commands.py +166 -0
  97. code_muse/messaging/markdown_patches.py +57 -0
  98. code_muse/messaging/message_queue.py +397 -0
  99. code_muse/messaging/messages.py +591 -0
  100. code_muse/messaging/queue_console.py +269 -0
  101. code_muse/messaging/renderers.py +308 -0
  102. code_muse/messaging/rich_renderer.py +1158 -0
  103. code_muse/messaging/shimmer.py +154 -0
  104. code_muse/messaging/spinner/__init__.py +87 -0
  105. code_muse/messaging/spinner/console_spinner.py +250 -0
  106. code_muse/messaging/spinner/spinner_base.py +82 -0
  107. code_muse/messaging/subagent_console.py +458 -0
  108. code_muse/model_factory.py +1203 -0
  109. code_muse/model_switching.py +59 -0
  110. code_muse/model_utils.py +156 -0
  111. code_muse/models.json +66 -0
  112. code_muse/models_cache/__init__.py +26 -0
  113. code_muse/models_cache/blocking_lru_cache.py +98 -0
  114. code_muse/models_cache/cache_writer.py +86 -0
  115. code_muse/models_cache/sha256_hash.cpython-314-darwin.so +0 -0
  116. code_muse/models_cache/sha256_hash.pyx +34 -0
  117. code_muse/models_cache/startup_integration.py +75 -0
  118. code_muse/models_dev_api.json +1 -0
  119. code_muse/models_dev_parser.py +590 -0
  120. code_muse/motion.py +126 -0
  121. code_muse/plugins/__init__.py +471 -0
  122. code_muse/plugins/agent_skills/__init__.py +32 -0
  123. code_muse/plugins/agent_skills/config.py +176 -0
  124. code_muse/plugins/agent_skills/discovery.py +309 -0
  125. code_muse/plugins/agent_skills/downloader.py +389 -0
  126. code_muse/plugins/agent_skills/installer.py +19 -0
  127. code_muse/plugins/agent_skills/metadata.py +293 -0
  128. code_muse/plugins/agent_skills/prompt_builder.py +66 -0
  129. code_muse/plugins/agent_skills/register_callbacks.py +298 -0
  130. code_muse/plugins/agent_skills/remote_catalog.py +320 -0
  131. code_muse/plugins/agent_skills/skill_catalog.py +254 -0
  132. code_muse/plugins/agent_skills/skills_install_menu.py +690 -0
  133. code_muse/plugins/agent_skills/skills_menu.py +791 -0
  134. code_muse/plugins/autonomous_memory/__init__.py +39 -0
  135. code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-darwin.so +0 -0
  136. code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-x86_64-linux-gnu.so +0 -0
  137. code_muse/plugins/autonomous_memory/bm25_scorer.pyx +291 -0
  138. code_muse/plugins/autonomous_memory/consolidation.py +82 -0
  139. code_muse/plugins/autonomous_memory/extraction.py +382 -0
  140. code_muse/plugins/autonomous_memory/lease_lock.py +105 -0
  141. code_muse/plugins/autonomous_memory/memory_injection.py +59 -0
  142. code_muse/plugins/autonomous_memory/register_callbacks.py +268 -0
  143. code_muse/plugins/autonomous_memory/secret_scanner.py +62 -0
  144. code_muse/plugins/autonomous_memory/session_scanner.py +163 -0
  145. code_muse/plugins/aws_bedrock/__init__.py +14 -0
  146. code_muse/plugins/aws_bedrock/config.py +99 -0
  147. code_muse/plugins/aws_bedrock/register_callbacks.py +241 -0
  148. code_muse/plugins/aws_bedrock/utils.py +153 -0
  149. code_muse/plugins/azure_foundry/README.md +238 -0
  150. code_muse/plugins/azure_foundry/__init__.py +15 -0
  151. code_muse/plugins/azure_foundry/config.py +125 -0
  152. code_muse/plugins/azure_foundry/discovery.py +187 -0
  153. code_muse/plugins/azure_foundry/register_callbacks.py +495 -0
  154. code_muse/plugins/azure_foundry/token.py +180 -0
  155. code_muse/plugins/azure_foundry/utils.py +345 -0
  156. code_muse/plugins/build_filter/__init__.py +1 -0
  157. code_muse/plugins/build_filter/register_callbacks.py +201 -0
  158. code_muse/plugins/build_filter/strategies/__init__.py +1 -0
  159. code_muse/plugins/build_filter/strategies/build.py +397 -0
  160. code_muse/plugins/chatgpt_oauth/__init__.py +6 -0
  161. code_muse/plugins/chatgpt_oauth/config.py +52 -0
  162. code_muse/plugins/chatgpt_oauth/oauth_flow.py +338 -0
  163. code_muse/plugins/chatgpt_oauth/register_callbacks.py +172 -0
  164. code_muse/plugins/chatgpt_oauth/test_plugin.py +301 -0
  165. code_muse/plugins/chatgpt_oauth/utils.py +538 -0
  166. code_muse/plugins/checkpointing/__init__.py +29 -0
  167. code_muse/plugins/checkpointing/checkpoint_hook.py +51 -0
  168. code_muse/plugins/checkpointing/conversation_snapshots.py +117 -0
  169. code_muse/plugins/checkpointing/register_callbacks.py +51 -0
  170. code_muse/plugins/checkpointing/restore_command.py +263 -0
  171. code_muse/plugins/checkpointing/rewind_shortcut.py +88 -0
  172. code_muse/plugins/checkpointing/shadow_git.py +90 -0
  173. code_muse/plugins/claude_code_hooks/__init__.py +1 -0
  174. code_muse/plugins/claude_code_hooks/config.py +188 -0
  175. code_muse/plugins/claude_code_hooks/register_callbacks.py +208 -0
  176. code_muse/plugins/claude_code_oauth/README.md +167 -0
  177. code_muse/plugins/claude_code_oauth/SETUP.md +93 -0
  178. code_muse/plugins/claude_code_oauth/__init__.py +25 -0
  179. code_muse/plugins/claude_code_oauth/config.py +52 -0
  180. code_muse/plugins/claude_code_oauth/fast_mode.py +124 -0
  181. code_muse/plugins/claude_code_oauth/prompt_handler.py +63 -0
  182. code_muse/plugins/claude_code_oauth/register_callbacks.py +547 -0
  183. code_muse/plugins/claude_code_oauth/test_fast_mode.py +165 -0
  184. code_muse/plugins/claude_code_oauth/test_plugin.py +283 -0
  185. code_muse/plugins/claude_code_oauth/token_refresh_heartbeat.py +237 -0
  186. code_muse/plugins/claude_code_oauth/utils.py +664 -0
  187. code_muse/plugins/copilot_auth/__init__.py +11 -0
  188. code_muse/plugins/copilot_auth/config.py +91 -0
  189. code_muse/plugins/copilot_auth/reasoning_client.py +409 -0
  190. code_muse/plugins/copilot_auth/register_callbacks.py +461 -0
  191. code_muse/plugins/copilot_auth/utils.py +584 -0
  192. code_muse/plugins/custom_commands/__init__.py +14 -0
  193. code_muse/plugins/custom_commands/args_injection.py +82 -0
  194. code_muse/plugins/custom_commands/command_discovery.py +89 -0
  195. code_muse/plugins/custom_commands/command_toml_schema.py +71 -0
  196. code_muse/plugins/custom_commands/register_callbacks.py +176 -0
  197. code_muse/plugins/customizable_commands/__init__.py +0 -0
  198. code_muse/plugins/customizable_commands/register_callbacks.py +136 -0
  199. code_muse/plugins/destructive_command_guard/__init__.py +14 -0
  200. code_muse/plugins/destructive_command_guard/detector.py +375 -0
  201. code_muse/plugins/destructive_command_guard/register_callbacks.py +148 -0
  202. code_muse/plugins/example_custom_command/README.md +280 -0
  203. code_muse/plugins/example_custom_command/register_callbacks.py +51 -0
  204. code_muse/plugins/file_permission_handler/__init__.py +4 -0
  205. code_muse/plugins/file_permission_handler/register_callbacks.py +441 -0
  206. code_muse/plugins/filter_engine/__init__.py +30 -0
  207. code_muse/plugins/filter_engine/classifier.py +153 -0
  208. code_muse/plugins/filter_engine/content_detector.py +184 -0
  209. code_muse/plugins/filter_engine/dispatcher.py +244 -0
  210. code_muse/plugins/filter_engine/register_callbacks.py +188 -0
  211. code_muse/plugins/filter_engine/registry.py +279 -0
  212. code_muse/plugins/filter_engine/strategies/__init__.py +8 -0
  213. code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-darwin.so +0 -0
  214. code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-x86_64-linux-gnu.so +0 -0
  215. code_muse/plugins/filter_engine/strategies/ast_compressor.pyx +348 -0
  216. code_muse/plugins/filter_engine/strategies/ast_parser.py +167 -0
  217. code_muse/plugins/filter_engine/strategies/code.cpython-314-darwin.so +0 -0
  218. code_muse/plugins/filter_engine/strategies/code.cpython-314-x86_64-linux-gnu.so +0 -0
  219. code_muse/plugins/filter_engine/strategies/code.pyx +584 -0
  220. code_muse/plugins/filter_engine/strategies/git.cpython-314-darwin.so +0 -0
  221. code_muse/plugins/filter_engine/strategies/git.cpython-314-x86_64-linux-gnu.so +0 -0
  222. code_muse/plugins/filter_engine/strategies/git.pyx +438 -0
  223. code_muse/plugins/filter_engine/strategies/json_compressor.cpython-314-darwin.so +0 -0
  224. code_muse/plugins/filter_engine/strategies/json_compressor.pyx +253 -0
  225. code_muse/plugins/filter_engine/strategies/json_patterns.cpython-314-darwin.so +0 -0
  226. code_muse/plugins/filter_engine/strategies/json_patterns.pyx +178 -0
  227. code_muse/plugins/filter_engine/strategies/lint.cpython-314-darwin.so +0 -0
  228. code_muse/plugins/filter_engine/strategies/lint.cpython-314-x86_64-linux-gnu.so +0 -0
  229. code_muse/plugins/filter_engine/strategies/lint.pyx +626 -0
  230. code_muse/plugins/filter_engine/strategies/test.cpython-314-darwin.so +0 -0
  231. code_muse/plugins/filter_engine/strategies/test.cpython-314-x86_64-linux-gnu.so +0 -0
  232. code_muse/plugins/filter_engine/strategies/test.pyx +431 -0
  233. code_muse/plugins/filter_engine/verbosity.py +63 -0
  234. code_muse/plugins/force_push_guard/__init__.py +5 -0
  235. code_muse/plugins/force_push_guard/detector.py +96 -0
  236. code_muse/plugins/force_push_guard/register_callbacks.py +144 -0
  237. code_muse/plugins/force_push_guard/test_detector.py +143 -0
  238. code_muse/plugins/frontend_emitter/__init__.py +25 -0
  239. code_muse/plugins/frontend_emitter/emitter.py +121 -0
  240. code_muse/plugins/frontend_emitter/register_callbacks.py +259 -0
  241. code_muse/plugins/gac/__init__.py +4 -0
  242. code_muse/plugins/gac/git_ops.py +136 -0
  243. code_muse/plugins/gac/prompt.py +191 -0
  244. code_muse/plugins/gac/register_callbacks.py +82 -0
  245. code_muse/plugins/hook_creator/__init__.py +1 -0
  246. code_muse/plugins/hook_creator/register_callbacks.py +34 -0
  247. code_muse/plugins/hook_manager/__init__.py +1 -0
  248. code_muse/plugins/hook_manager/config.py +289 -0
  249. code_muse/plugins/hook_manager/hooks_menu.py +563 -0
  250. code_muse/plugins/hook_manager/register_callbacks.py +227 -0
  251. code_muse/plugins/hook_monitor/register_callbacks.py +36 -0
  252. code_muse/plugins/mindpack/__init__.py +0 -0
  253. code_muse/plugins/mindpack/factory.py +930 -0
  254. code_muse/plugins/mindpack/judge.py +573 -0
  255. code_muse/plugins/mindpack/memory.py +100 -0
  256. code_muse/plugins/mindpack/mindpack_menu.py +1552 -0
  257. code_muse/plugins/mindpack/orchestration.py +605 -0
  258. code_muse/plugins/mindpack/register_callbacks.py +175 -0
  259. code_muse/plugins/mindpack/schemas.py +358 -0
  260. code_muse/plugins/mindpack/tools.py +387 -0
  261. code_muse/plugins/oauth_muse_html.py +226 -0
  262. code_muse/plugins/ollama_setup/__init__.py +5 -0
  263. code_muse/plugins/ollama_setup/completer.py +36 -0
  264. code_muse/plugins/ollama_setup/register_callbacks.py +410 -0
  265. code_muse/plugins/plan_command/__init__.py +0 -0
  266. code_muse/plugins/plan_command/register_callbacks.py +206 -0
  267. code_muse/plugins/plan_mode/__init__.py +37 -0
  268. code_muse/plugins/plan_mode/mode_cycling.py +40 -0
  269. code_muse/plugins/plan_mode/plan_generation.py +68 -0
  270. code_muse/plugins/plan_mode/plan_hooks.py +74 -0
  271. code_muse/plugins/plan_mode/plan_mode_tools.py +138 -0
  272. code_muse/plugins/plan_mode/register_callbacks.py +121 -0
  273. code_muse/plugins/plugin_trust/register_callbacks.py +140 -0
  274. code_muse/plugins/policy_engine/__init__.py +46 -0
  275. code_muse/plugins/policy_engine/approval_flow_integration.py +59 -0
  276. code_muse/plugins/policy_engine/policy_evaluator.py +75 -0
  277. code_muse/plugins/policy_engine/policy_file_discovery.py +90 -0
  278. code_muse/plugins/policy_engine/policy_toml_schema.py +115 -0
  279. code_muse/plugins/policy_engine/register_callbacks.py +112 -0
  280. code_muse/plugins/pop_command/__init__.py +1 -0
  281. code_muse/plugins/pop_command/register_callbacks.py +189 -0
  282. code_muse/plugins/prompt_newline/__init__.py +13 -0
  283. code_muse/plugins/prompt_newline/config.py +19 -0
  284. code_muse/plugins/prompt_newline/register_callbacks.py +159 -0
  285. code_muse/plugins/safety_status/__init__.py +0 -0
  286. code_muse/plugins/safety_status/register_callbacks.py +113 -0
  287. code_muse/plugins/semantic_compression/__init__.py +6 -0
  288. code_muse/plugins/semantic_compression/compressor.py +295 -0
  289. code_muse/plugins/semantic_compression/config.py +123 -0
  290. code_muse/plugins/semantic_compression/register_callbacks.py +320 -0
  291. code_muse/plugins/shell_minimizer/__init__.py +50 -0
  292. code_muse/plugins/shell_minimizer/builtin_filters.toml +393 -0
  293. code_muse/plugins/shell_minimizer/pipeline.py +556 -0
  294. code_muse/plugins/shell_minimizer/primitives.py +482 -0
  295. code_muse/plugins/shell_minimizer/register_callbacks.py +276 -0
  296. code_muse/plugins/shell_safety/__init__.py +6 -0
  297. code_muse/plugins/shell_safety/agent_shell_safety.py +69 -0
  298. code_muse/plugins/shell_safety/command_cache.py +149 -0
  299. code_muse/plugins/shell_safety/register_callbacks.py +202 -0
  300. code_muse/plugins/synthetic_status/__init__.py +1 -0
  301. code_muse/plugins/synthetic_status/register_callbacks.py +128 -0
  302. code_muse/plugins/synthetic_status/status_api.py +145 -0
  303. code_muse/plugins/token_caching/__init__.py +21 -0
  304. code_muse/plugins/token_caching/cache_hit_tracking.py +128 -0
  305. code_muse/plugins/token_caching/cacheable_prefix_detection.py +28 -0
  306. code_muse/plugins/token_caching/register_callbacks.py +54 -0
  307. code_muse/plugins/token_caching/stats_display.py +35 -0
  308. code_muse/plugins/token_tracking/__init__.py +26 -0
  309. code_muse/plugins/token_tracking/database.py +381 -0
  310. code_muse/plugins/token_tracking/edit_analyzer.py +97 -0
  311. code_muse/plugins/token_tracking/record.py +55 -0
  312. code_muse/plugins/token_tracking/register_callbacks.py +277 -0
  313. code_muse/plugins/token_tracking/reports.py +329 -0
  314. code_muse/plugins/universal_constructor/__init__.py +13 -0
  315. code_muse/plugins/universal_constructor/models.py +136 -0
  316. code_muse/plugins/universal_constructor/register_callbacks.py +47 -0
  317. code_muse/plugins/universal_constructor/registry.py +390 -0
  318. code_muse/plugins/universal_constructor/runner.py +474 -0
  319. code_muse/plugins/universal_constructor/safety.py +440 -0
  320. code_muse/plugins/universal_constructor/sandbox.py +584 -0
  321. code_muse/provider_identity.py +105 -0
  322. code_muse/pydantic_patches.py +410 -0
  323. code_muse/reopenable_async_client.py +233 -0
  324. code_muse/round_robin_model.py +151 -0
  325. code_muse/secret_storage.py +74 -0
  326. code_muse/security/__init__.py +1 -0
  327. code_muse/security/redaction.cpython-314-darwin.so +0 -0
  328. code_muse/security/redaction.cpython-314-x86_64-linux-gnu.so +0 -0
  329. code_muse/security/redaction.pyx +135 -0
  330. code_muse/session_storage.py +565 -0
  331. code_muse/status_display.py +261 -0
  332. code_muse/stream_parser/__init__.py +76 -0
  333. code_muse/stream_parser/assistant_text_parser.py +90 -0
  334. code_muse/stream_parser/citation_parser.py +76 -0
  335. code_muse/stream_parser/inline_hidden_tag_parser.py +236 -0
  336. code_muse/stream_parser/proposed_plan_parser.py +158 -0
  337. code_muse/stream_parser/stream_text_chunk.py +23 -0
  338. code_muse/stream_parser/stream_text_parser.py +27 -0
  339. code_muse/stream_parser/tagged_line_parser.cpython-314-darwin.so +0 -0
  340. code_muse/stream_parser/tagged_line_parser.pyx +251 -0
  341. code_muse/stream_parser/utf8_stream_parser.cpython-314-darwin.so +0 -0
  342. code_muse/stream_parser/utf8_stream_parser.pyx +206 -0
  343. code_muse/summarization_agent.py +308 -0
  344. code_muse/terminal_utils.cpython-314-darwin.so +0 -0
  345. code_muse/terminal_utils.cpython-314-x86_64-linux-gnu.so +0 -0
  346. code_muse/terminal_utils.pyx +483 -0
  347. code_muse/tools/__init__.py +459 -0
  348. code_muse/tools/agent_tools.py +613 -0
  349. code_muse/tools/ask_user_question/__init__.py +26 -0
  350. code_muse/tools/ask_user_question/constants.py +73 -0
  351. code_muse/tools/ask_user_question/demo_tui.py +55 -0
  352. code_muse/tools/ask_user_question/handler.py +232 -0
  353. code_muse/tools/ask_user_question/models.py +302 -0
  354. code_muse/tools/ask_user_question/registration.py +37 -0
  355. code_muse/tools/ask_user_question/renderers.py +336 -0
  356. code_muse/tools/ask_user_question/terminal_ui.py +327 -0
  357. code_muse/tools/ask_user_question/theme.py +156 -0
  358. code_muse/tools/ask_user_question/tui_loop.py +422 -0
  359. code_muse/tools/background_jobs.py +99 -0
  360. code_muse/tools/browser/__init__.py +37 -0
  361. code_muse/tools/browser/browser_control.py +289 -0
  362. code_muse/tools/browser/browser_interactions.py +545 -0
  363. code_muse/tools/browser/browser_locators.py +640 -0
  364. code_muse/tools/browser/browser_manager.py +376 -0
  365. code_muse/tools/browser/browser_navigation.py +251 -0
  366. code_muse/tools/browser/browser_screenshot.py +180 -0
  367. code_muse/tools/browser/browser_scripts.py +462 -0
  368. code_muse/tools/browser/browser_workflows.py +222 -0
  369. code_muse/tools/chrome_cdp/__init__.py +1070 -0
  370. code_muse/tools/chrome_cdp/register_callbacks.py +61 -0
  371. code_muse/tools/command_runner.py +1401 -0
  372. code_muse/tools/common.py +1407 -0
  373. code_muse/tools/display.py +87 -0
  374. code_muse/tools/file_modifications.py +1099 -0
  375. code_muse/tools/file_operations.py +860 -0
  376. code_muse/tools/image_tools.py +185 -0
  377. code_muse/tools/meetin_proxy/__init__.py +243 -0
  378. code_muse/tools/meetin_proxy/capture_addon.py +82 -0
  379. code_muse/tools/meetin_proxy/proxy_manager.py +326 -0
  380. code_muse/tools/meetin_proxy/register_callbacks.py +45 -0
  381. code_muse/tools/path_policy.py +219 -0
  382. code_muse/tools/skills_tools.py +586 -0
  383. code_muse/tools/subagent_context.py +158 -0
  384. code_muse/tools/tools_content.py +50 -0
  385. code_muse/tools/universal_constructor.py +965 -0
  386. code_muse/uvx_detection.py +241 -0
  387. code_muse/version_checker.py +86 -0
  388. code_muse-0.0.1.data/data/code_muse/models.json +66 -0
  389. code_muse-0.0.1.data/data/code_muse/models_dev_api.json +1 -0
  390. code_muse-0.0.1.dist-info/METADATA +845 -0
  391. code_muse-0.0.1.dist-info/RECORD +394 -0
  392. code_muse-0.0.1.dist-info/WHEEL +4 -0
  393. code_muse-0.0.1.dist-info/entry_points.txt +2 -0
  394. code_muse-0.0.1.dist-info/licenses/LICENSE +21 -0
code_muse/callbacks.py ADDED
@@ -0,0 +1,941 @@
1
+ import asyncio
2
+ import atexit
3
+ import concurrent.futures
4
+ import logging
5
+ import traceback
6
+ from collections.abc import Callable
7
+ from typing import Any, Literal
8
+
9
+ PhaseType = Literal[
10
+ "startup",
11
+ "shutdown",
12
+ "invoke_agent",
13
+ "agent_exception",
14
+ "version_check",
15
+ "edit_file",
16
+ "create_file",
17
+ "replace_in_file",
18
+ "delete_snippet",
19
+ "delete_file",
20
+ "run_shell_command",
21
+ "load_model_config",
22
+ "load_models_config",
23
+ "load_prompt",
24
+ "agent_reload",
25
+ "custom_command",
26
+ "custom_command_help",
27
+ "file_permission",
28
+ "pre_tool_call",
29
+ "post_tool_call",
30
+ "stream_event",
31
+ "register_tools",
32
+ "register_agents",
33
+ "register_model_type",
34
+ "get_model_system_prompt",
35
+ "prepare_model_prompt",
36
+ "agent_run_start",
37
+ "agent_run_end",
38
+ "agent_run_result",
39
+ "register_browser_types",
40
+ "register_model_providers",
41
+ "message_history_processor_start",
42
+ "message_history_processor_end",
43
+ "on_message",
44
+ "agent_run_context",
45
+ "agent_run_cancel",
46
+ "should_skip_fallback_render",
47
+ ]
48
+ CallbackFunc = Callable[..., Any]
49
+
50
+ # (priority, func) tuples — higher priority runs first.
51
+ _callbacks: dict[PhaseType, list[tuple[int, CallbackFunc]]] = {
52
+ "startup": [],
53
+ "shutdown": [],
54
+ "invoke_agent": [],
55
+ "agent_exception": [],
56
+ "version_check": [],
57
+ "edit_file": [],
58
+ "create_file": [],
59
+ "replace_in_file": [],
60
+ "delete_snippet": [],
61
+ "delete_file": [],
62
+ "run_shell_command": [],
63
+ "load_model_config": [],
64
+ "load_models_config": [],
65
+ "load_prompt": [],
66
+ "agent_reload": [],
67
+ "custom_command": [],
68
+ "custom_command_help": [],
69
+ "file_permission": [],
70
+ "pre_tool_call": [],
71
+ "post_tool_call": [],
72
+ "stream_event": [],
73
+ "register_tools": [],
74
+ "register_agents": [],
75
+ "register_model_type": [],
76
+ "get_model_system_prompt": [],
77
+ "prepare_model_prompt": [],
78
+ "agent_run_start": [],
79
+ "agent_run_end": [],
80
+ "agent_run_result": [],
81
+ "register_browser_types": [],
82
+ "register_model_providers": [],
83
+ "message_history_processor_start": [],
84
+ "message_history_processor_end": [],
85
+ "on_message": [],
86
+ "agent_run_context": [],
87
+ "agent_run_cancel": [],
88
+ "should_skip_fallback_render": [],
89
+ }
90
+
91
+ logger = logging.getLogger(__name__)
92
+ # FREE-THREADED: ThreadPoolExecutor is compatible with free-threaded Python 3.14 —
93
+ # no GIL contention for I/O-bound callback work.
94
+ _executor = concurrent.futures.ThreadPoolExecutor(max_workers=8)
95
+
96
+
97
+ def _shutdown_executor() -> None:
98
+ """Gracefully shut down the callback ThreadPoolExecutor.
99
+
100
+ Called automatically at interpreter exit via ``atexit``. Plugins that
101
+ spawn long-running threads inside callbacks should clean themselves up
102
+ in response to the ``shutdown`` hook; this function only terminates the
103
+ executor that runs those callbacks.
104
+ """
105
+ try:
106
+ _executor.shutdown(wait=True, cancel_futures=True)
107
+ logger.debug("Callback executor shut down cleanly")
108
+ except Exception as exc:
109
+ logger.warning(f"Callback executor shutdown error: {exc}")
110
+
111
+
112
+ atexit.register(_shutdown_executor)
113
+
114
+
115
+ def register_callback(phase: PhaseType, func: CallbackFunc, priority: int = 0) -> None:
116
+ if phase not in _callbacks:
117
+ raise ValueError(
118
+ f"Unsupported phase: {phase}. Supported phases: {list(_callbacks.keys())}"
119
+ )
120
+
121
+ if not callable(func):
122
+ raise TypeError(f"Callback must be callable, got {type(func)}")
123
+
124
+ # Prevent duplicate registration of the same callback function
125
+ # This can happen if plugins are accidentally loaded multiple times
126
+ for existing_priority, existing_func in _callbacks[phase]:
127
+ if existing_func is func:
128
+ logger.debug(
129
+ f"Callback {func.__name__} already registered for phase '{phase}', skipping"
130
+ )
131
+ return
132
+
133
+ _callbacks[phase].append((priority, func))
134
+ logger.debug(
135
+ f"Registered async callback {func.__name__} for phase '{phase}' (priority={priority})"
136
+ )
137
+
138
+
139
+ def unregister_callback(phase: PhaseType, func: CallbackFunc) -> bool:
140
+ if phase not in _callbacks:
141
+ return False
142
+
143
+ for idx, (existing_priority, existing_func) in enumerate(_callbacks[phase]):
144
+ if existing_func is func:
145
+ del _callbacks[phase][idx]
146
+ logger.debug(
147
+ f"Unregistered async callback {func.__name__} from phase '{phase}'"
148
+ )
149
+ return True
150
+ return False
151
+
152
+
153
+ def clear_callbacks(phase: PhaseType | None = None) -> None:
154
+ if phase is None:
155
+ for p in _callbacks:
156
+ _callbacks[p].clear()
157
+ logger.debug("Cleared all async callbacks")
158
+ else:
159
+ if phase in _callbacks:
160
+ _callbacks[phase].clear()
161
+ logger.debug(f"Cleared async callbacks for phase '{phase}'")
162
+
163
+
164
+ def get_callbacks(phase: PhaseType) -> list[CallbackFunc]:
165
+ """Return callbacks for *phase* sorted by priority (highest first)."""
166
+ callbacks = _callbacks.get(phase, [])
167
+ # Sort by priority descending, then by registration order (stable sort)
168
+ sorted_callbacks = sorted(callbacks, key=lambda item: item[0], reverse=True)
169
+ return [func for _priority, func in sorted_callbacks]
170
+
171
+
172
+ def count_callbacks(phase: PhaseType | None = None) -> int:
173
+ if phase is None:
174
+ return sum(len(callbacks) for callbacks in _callbacks.values())
175
+ return len(_callbacks.get(phase, []))
176
+
177
+
178
+ def fire_callbacks(phase: PhaseType, *args, **kwargs) -> None:
179
+ """Fire callbacks without blocking; async callbacks run in the background.
180
+
181
+ Useful for observation hooks (like ``on_message``) that must not delay
182
+ the caller. Exceptions are logged and swallowed.
183
+ """
184
+ callbacks = get_callbacks(phase)
185
+ if not callbacks:
186
+ return
187
+
188
+ for callback in callbacks:
189
+ try:
190
+ result = callback(*args, **kwargs)
191
+ if asyncio.iscoroutine(result):
192
+ try:
193
+ loop = asyncio.get_running_loop()
194
+ task = loop.create_task(result)
195
+ task.add_done_callback(
196
+ lambda t: logger.debug("Fire-and-forget callback completed")
197
+ )
198
+ except RuntimeError:
199
+ _executor.submit(asyncio.run, result)
200
+ except Exception as e:
201
+ logger.debug(f"Fire-and-forget callback {callback.__name__} failed: {e}")
202
+
203
+
204
+ def _trigger_callbacks_sync(phase: PhaseType, *args, **kwargs) -> list[Any]:
205
+ callbacks = get_callbacks(phase)
206
+ if not callbacks:
207
+ logger.debug(f"No callbacks registered for phase '{phase}'")
208
+ return []
209
+
210
+ results = []
211
+ for callback in callbacks:
212
+ try:
213
+ result = callback(*args, **kwargs)
214
+ # Handle async callbacks - if we get a coroutine, run it
215
+ if asyncio.iscoroutine(result):
216
+ try:
217
+ asyncio.get_running_loop()
218
+ # We're in an async context (e.g., the TUI).
219
+ # Run the coroutine in a separate thread so it gets
220
+ # its own event loop. No timeout — long-running
221
+ # commands (MindPack, menus) need unlimited time.
222
+ future = _executor.submit(asyncio.run, result)
223
+ result = future.result(timeout=None)
224
+ except RuntimeError:
225
+ # No running loop — we're in a sync/worker thread.
226
+ result = asyncio.run(result)
227
+ results.append(result)
228
+ logger.debug(f"Successfully executed callback {callback.__name__}")
229
+ except Exception as e:
230
+ logger.error(
231
+ f"Callback {callback.__name__} failed in phase '{phase}': {e}\n"
232
+ f"{traceback.format_exc()}"
233
+ )
234
+ results.append(None)
235
+
236
+ return results
237
+
238
+
239
+ async def _trigger_callbacks(phase: PhaseType, *args, **kwargs) -> list[Any]:
240
+ """Trigger all registered callbacks for *phase* in priority order.
241
+
242
+ Hook execution order: callbacks run from highest priority to lowest.
243
+ For ``run_shell_command``, the caller (``command_runner.py``) iterates
244
+ results and the first non-None dict with ``pre_executed: True`` wins.
245
+ By default priority values, ``filter_engine`` runs before
246
+ ``policy_engine`` before ``shell_minimizer``.
247
+ """
248
+ callbacks = get_callbacks(phase)
249
+
250
+ if not callbacks:
251
+ logger.debug(f"No callbacks registered for phase '{phase}'")
252
+ return []
253
+
254
+ logger.debug(f"Triggering {len(callbacks)} async callbacks for phase '{phase}'")
255
+
256
+ results = []
257
+ for callback in callbacks:
258
+ try:
259
+ result = callback(*args, **kwargs)
260
+ if asyncio.iscoroutine(result):
261
+ result = await result
262
+ results.append(result)
263
+ logger.debug(f"Successfully executed async callback {callback.__name__}")
264
+ except Exception as e:
265
+ logger.error(
266
+ f"Async callback {callback.__name__} failed in phase '{phase}': {e}\n"
267
+ f"{traceback.format_exc()}"
268
+ )
269
+ results.append(None)
270
+
271
+ return results
272
+
273
+
274
+ async def on_startup() -> list[Any]:
275
+ callbacks = get_callbacks("startup")
276
+ results: list[Any] = []
277
+ failed_names: list[str] = []
278
+ for callback in callbacks:
279
+ try:
280
+ result = callback()
281
+ if asyncio.iscoroutine(result):
282
+ result = await result
283
+ results.append(result)
284
+ logger.debug(f"Successfully executed async callback {callback.__name__}")
285
+ except Exception as e:
286
+ logger.error(
287
+ f"Async callback {callback.__name__} failed in phase 'startup': {e}\n"
288
+ f"{traceback.format_exc()}"
289
+ )
290
+ failed_names.append(callback.__name__)
291
+ results.append(None)
292
+ if failed_names:
293
+ count = len(failed_names)
294
+ names_str = ", ".join(failed_names)
295
+ logger.warning(f"{count} startup callback(s) failed: {names_str}")
296
+ from code_muse.messaging import emit_warning
297
+
298
+ emit_warning(f"⚠️ {count} plugin(s) failed to load: {names_str}")
299
+
300
+ # Report Cython status from the core package
301
+ import code_muse
302
+
303
+ if code_muse.CYTHON_ENABLED:
304
+ from code_muse.messaging import emit_success
305
+
306
+ emit_success(
307
+ f"✅ Cython enabled — {code_muse.PYX_MODULE_COUNT} modules compiled"
308
+ )
309
+ else:
310
+ from code_muse.messaging import emit_warning
311
+
312
+ emit_warning("⚠️ Cython not available — running in pure Python mode")
313
+ return results
314
+
315
+
316
+ async def on_shutdown() -> list[Any]:
317
+ return await _trigger_callbacks("shutdown")
318
+
319
+
320
+ async def on_invoke_agent(*args, **kwargs) -> list[Any]:
321
+ return await _trigger_callbacks("invoke_agent", *args, **kwargs)
322
+
323
+
324
+ async def on_agent_exception(exception: Exception, *args, **kwargs) -> list[Any]:
325
+ return await _trigger_callbacks("agent_exception", exception, *args, **kwargs)
326
+
327
+
328
+ async def on_version_check(*args, **kwargs) -> list[Any]:
329
+ return await _trigger_callbacks("version_check", *args, **kwargs)
330
+
331
+
332
+ def on_load_model_config(*args, **kwargs) -> list[Any]:
333
+ return _trigger_callbacks_sync("load_model_config", *args, **kwargs)
334
+
335
+
336
+ def on_load_models_config() -> list[Any]:
337
+ """Trigger callbacks to load additional model configurations.
338
+
339
+ Plugins can register callbacks that return a dict of model configurations
340
+ to be merged with the built-in models.json. Plugin models override built-in
341
+ models with the same name.
342
+
343
+ Returns:
344
+ List of model config dicts from all registered callbacks.
345
+ """
346
+ return _trigger_callbacks_sync("load_models_config")
347
+
348
+
349
+ def on_edit_file(*args, **kwargs) -> Any:
350
+ return _trigger_callbacks_sync("edit_file", *args, **kwargs)
351
+
352
+
353
+ def on_create_file(*args, **kwargs) -> Any:
354
+ return _trigger_callbacks_sync("create_file", *args, **kwargs)
355
+
356
+
357
+ def on_replace_in_file(*args, **kwargs) -> Any:
358
+ return _trigger_callbacks_sync("replace_in_file", *args, **kwargs)
359
+
360
+
361
+ def on_delete_snippet(*args, **kwargs) -> Any:
362
+ return _trigger_callbacks_sync("delete_snippet", *args, **kwargs)
363
+
364
+
365
+ def on_delete_file(*args, **kwargs) -> Any:
366
+ return _trigger_callbacks_sync("delete_file", *args, **kwargs)
367
+
368
+
369
+ async def on_run_shell_command(*args, **kwargs) -> Any:
370
+ """Trigger callbacks for shell command execution.
371
+
372
+ Multiple plugins can register for this hook. The caller iterates through
373
+ registered callbacks in priority order (highest first). The first callback
374
+ returning ``{"pre_executed": True, "output": ...}`` wins — the rest are
375
+ skipped.
376
+
377
+ Recommended priority chain for ``run_shell_command``:
378
+
379
+ 1. ``filter_engine`` (priority 100) — intercepts and transforms shell output
380
+ 2. ``policy_engine`` / ``shell_safety`` (priority 50) — enforces allow/deny/ask-user rules
381
+ 3. ``shell_minimizer`` (priority 0) — compresses known command output
382
+
383
+ Future handlers should use an explicit ``priority`` argument when calling
384
+ ``register_callback()`` if they need to run earlier or later in the pipeline.
385
+
386
+ Args:
387
+ *args: Positional arguments passed to callbacks (context, command, ...).
388
+ **kwargs: Keyword arguments passed to callbacks (cwd, timeout, ...).
389
+
390
+ Returns:
391
+ List of results from all registered callbacks.
392
+ """
393
+ return await _trigger_callbacks("run_shell_command", *args, **kwargs)
394
+
395
+
396
+ def on_agent_reload(*args, **kwargs) -> Any:
397
+ return _trigger_callbacks_sync("agent_reload", *args, **kwargs)
398
+
399
+
400
+ def on_load_prompt():
401
+ return _trigger_callbacks_sync("load_prompt")
402
+
403
+
404
+ def on_custom_command_help() -> list[Any]:
405
+ """Collect custom command help entries from plugins.
406
+
407
+ Each callback should return a list of tuples [(name, description), ...]
408
+ or a single tuple, or None. We'll flatten and sanitize results.
409
+ """
410
+ return _trigger_callbacks_sync("custom_command_help")
411
+
412
+
413
+ def on_custom_command(command: str, name: str) -> list[Any]:
414
+ """Trigger custom command callbacks.
415
+
416
+ This allows plugins to register handlers for slash commands
417
+ that are not built into the core command handler.
418
+
419
+ Args:
420
+ command: The full command string (e.g., "/foo bar baz").
421
+ name: The primary command name without the leading slash (e.g., "foo").
422
+
423
+ Returns:
424
+ Implementations may return:
425
+ - True if the command was handled (and no further action is needed)
426
+ - A string to be processed as user input by the caller
427
+ - None to indicate not handled
428
+ """
429
+ return _trigger_callbacks_sync("custom_command", command, name)
430
+
431
+
432
+ def on_file_permission(
433
+ context: Any,
434
+ file_path: str,
435
+ operation: str,
436
+ preview: str | None = None,
437
+ message_group: str | None = None,
438
+ operation_data: Any = None,
439
+ ) -> list[Any]:
440
+ """Trigger file permission callbacks.
441
+
442
+ This allows plugins to register handlers for file permission checks
443
+ before file operations are performed.
444
+
445
+ Args:
446
+ context: The operation context
447
+ file_path: Path to the file being operated on
448
+ operation: Description of the operation
449
+ preview: Optional preview of changes (deprecated - use operation_data instead)
450
+ message_group: Optional message group
451
+ operation_data: Operation-specific data for preview generation (recommended)
452
+
453
+ Returns:
454
+ List of boolean results from permission handlers.
455
+ Returns True if permission should be granted, False if denied.
456
+ """
457
+ # PERF-08: Skip callback overhead entirely in yolo mode
458
+ from code_muse.config import get_yolo_mode
459
+
460
+ if get_yolo_mode():
461
+ return [True]
462
+
463
+ # For backward compatibility, if operation_data is provided, prefer it over preview
464
+ if operation_data is not None:
465
+ preview = None
466
+ return _trigger_callbacks_sync(
467
+ "file_permission",
468
+ context,
469
+ file_path,
470
+ operation,
471
+ preview,
472
+ message_group,
473
+ operation_data,
474
+ )
475
+
476
+
477
+ async def on_pre_tool_call(
478
+ tool_name: str, tool_args: dict, context: Any = None
479
+ ) -> list[Any]:
480
+ """Trigger callbacks before a tool is called.
481
+
482
+ This allows plugins to inspect, modify, or log tool calls before
483
+ they are executed.
484
+
485
+ Args:
486
+ tool_name: Name of the tool being called
487
+ tool_args: Arguments being passed to the tool
488
+ context: Optional context data for the tool call
489
+
490
+ Returns:
491
+ List of results from registered callbacks.
492
+ """
493
+ return await _trigger_callbacks("pre_tool_call", tool_name, tool_args, context)
494
+
495
+
496
+ async def on_post_tool_call(
497
+ tool_name: str,
498
+ tool_args: dict,
499
+ result: Any,
500
+ duration_ms: float,
501
+ context: Any = None,
502
+ ) -> list[Any]:
503
+ """Trigger callbacks after a tool completes.
504
+
505
+ This allows plugins to inspect tool results, log execution times,
506
+ or perform post-processing.
507
+
508
+ Args:
509
+ tool_name: Name of the tool that was called
510
+ tool_args: Arguments that were passed to the tool
511
+ result: The result returned by the tool
512
+ duration_ms: Execution time in milliseconds
513
+ context: Optional context data for the tool call
514
+
515
+ Returns:
516
+ List of results from registered callbacks.
517
+ """
518
+ return await _trigger_callbacks(
519
+ "post_tool_call", tool_name, tool_args, result, duration_ms, context
520
+ )
521
+
522
+
523
+ async def on_stream_event(
524
+ event_type: str, event_data: Any, agent_session_id: str | None = None
525
+ ) -> list[Any]:
526
+ """Trigger callbacks for streaming events.
527
+
528
+ This allows plugins to react to streaming events in real-time,
529
+ such as tokens being generated, tool calls starting, etc.
530
+
531
+ Args:
532
+ event_type: Type of the streaming event
533
+ event_data: Data associated with the event
534
+ agent_session_id: Optional session ID of the agent emitting the event
535
+
536
+ Returns:
537
+ List of results from registered callbacks.
538
+ """
539
+ return await _trigger_callbacks(
540
+ "stream_event", event_type, event_data, agent_session_id
541
+ )
542
+
543
+
544
+ def on_register_tools() -> list[dict[str, Any]]:
545
+ """Collect custom tool registrations from plugins.
546
+
547
+ Each callback should return a list of dicts with:
548
+ - "name": str - the tool name
549
+ - "register_func": callable - function that takes an agent and registers the tool
550
+
551
+ Example return: [{"name": "my_tool", "register_func": register_my_tool}]
552
+ """
553
+ return _trigger_callbacks_sync("register_tools")
554
+
555
+
556
+ def on_register_agents() -> list[dict[str, Any]]:
557
+ """Collect custom agent registrations from plugins.
558
+
559
+ Each callback should return a list of dicts with either:
560
+ - "name": str, "class": Type[BaseAgent] - for Python agent classes
561
+ - "name": str, "json_path": str - for JSON agent files
562
+
563
+ Example return: [{"name": "my-agent", "class": MyAgentClass}]
564
+ """
565
+ return _trigger_callbacks_sync("register_agents")
566
+
567
+
568
+ def on_register_model_types() -> list[dict[str, Any]]:
569
+ """Collect custom model type registrations from plugins.
570
+
571
+ This hook allows plugins to register custom model types that can be used
572
+ in model configurations. Each callback should return a list of dicts with:
573
+ - "type": str - the model type name (e.g., "claude_code")
574
+ - "handler": callable - function(model_name, model_config, config) -> model instance
575
+
576
+ The handler function receives:
577
+ - model_name: str - the name of the model being created
578
+ - model_config: dict - the model's configuration from models.json
579
+ - config: dict - the full models configuration
580
+
581
+ The handler should return a model instance or None if creation fails.
582
+
583
+ Example callback:
584
+ def register_my_model_types():
585
+ return [{
586
+ "type": "my_custom_type",
587
+ "handler": create_my_custom_model,
588
+ }]
589
+
590
+ Example return: [{"type": "my_custom_type", "handler": create_my_custom_model}]
591
+ """
592
+ return _trigger_callbacks_sync("register_model_type")
593
+
594
+
595
+ def on_get_model_system_prompt(
596
+ model_name: str, default_system_prompt: str, user_prompt: str
597
+ ) -> list[dict[str, Any]]:
598
+ """Allow plugins to provide custom system prompts for specific model types.
599
+
600
+ This hook allows plugins to override the system prompt handling for custom
601
+ model types (like claude_code models). Each callback receives
602
+ the model name and should return a dict if it handles that model type, or None.
603
+
604
+ Args:
605
+ model_name: The name of the model being used (e.g., "claude-code-sonnet")
606
+ default_system_prompt: The default system prompt from the agent
607
+ user_prompt: The user's prompt/message
608
+
609
+ Each callback should return a dict with:
610
+ - "instructions": str - the system prompt/instructions to use
611
+ - "user_prompt": str - the (possibly modified) user prompt
612
+ - "handled": bool - True if this callback handled the model
613
+
614
+ Or return None if the callback doesn't handle this model type.
615
+
616
+ Example callback:
617
+ def get_my_model_system_prompt(model_name, default_system_prompt, user_prompt):
618
+ if model_name.startswith("my-custom-"):
619
+ return {
620
+ "instructions": "You are MyCustomBot.",
621
+ "user_prompt": f"{default_system_prompt}\n\n{user_prompt}",
622
+ "handled": True,
623
+ }
624
+ return None # Not handled by this callback
625
+
626
+ Returns:
627
+ List of results from registered callbacks (dicts or None values).
628
+ """
629
+ return _trigger_callbacks_sync(
630
+ "get_model_system_prompt", model_name, default_system_prompt, user_prompt
631
+ )
632
+
633
+
634
+ def on_prepare_model_prompt(
635
+ model_name: str,
636
+ system_prompt: str,
637
+ user_prompt: str,
638
+ prepend_system_to_user: bool = True,
639
+ ) -> list[dict[str, Any | None]]:
640
+ """Allow plugins to fully prepare the prompt (instructions + user prompt) for a model.
641
+
642
+ This is the hook fired from ``model_utils.prepare_prompt_for_model`` to let
643
+ plugins take over prompt preparation for specific model families (e.g.
644
+ claude-code OAuth models which need a hard-coded instruction string and
645
+ have the system prompt prepended to the user message).
646
+
647
+ Unlike ``get_model_system_prompt`` (which is used by augmenting plugins like
648
+ agent_skills), this hook is for plugins that want to *fully handle* the
649
+ prompt prep for a given model. The first callback returning ``handled=True``
650
+ wins; the rest are ignored.
651
+
652
+ Args:
653
+ model_name: The name of the model being used.
654
+ system_prompt: The default system prompt from the agent.
655
+ user_prompt: The user's prompt/message.
656
+ prepend_system_to_user: Whether the caller wants system prompt prepended
657
+ to the user prompt (only meaningful for plugins that manipulate the
658
+ user prompt, like claude-code).
659
+
660
+ Each callback should return a dict with:
661
+ - ``"handled"``: bool — True if this callback fully prepared the prompt.
662
+ - ``"instructions"``: str — the system prompt/instructions to use.
663
+ - ``"user_prompt"``: str — the (possibly modified) user prompt.
664
+ - ``"is_claude_code"``: bool — (optional) flag preserved on PreparedPrompt.
665
+
666
+ Or return ``None`` to indicate "I don't handle this model".
667
+
668
+ Returns:
669
+ List of results (dicts or ``None``) from registered callbacks.
670
+ """
671
+ return _trigger_callbacks_sync(
672
+ "prepare_model_prompt",
673
+ model_name,
674
+ system_prompt,
675
+ user_prompt,
676
+ prepend_system_to_user,
677
+ )
678
+
679
+
680
+ async def on_agent_run_start(
681
+ agent_name: str,
682
+ model_name: str,
683
+ session_id: str | None = None,
684
+ ) -> list[Any]:
685
+ """Trigger callbacks when an agent run starts.
686
+
687
+ This fires at the beginning of run, before the agent task is created.
688
+ Useful for:
689
+ - Starting background tasks (like token refresh heartbeats)
690
+ - Logging/analytics
691
+ - Resource allocation
692
+
693
+ Args:
694
+ agent_name: Name of the agent starting
695
+ model_name: Name of the model being used
696
+ session_id: Optional session identifier
697
+
698
+ Returns:
699
+ List of results from registered callbacks.
700
+ """
701
+ return await _trigger_callbacks(
702
+ "agent_run_start", agent_name, model_name, session_id
703
+ )
704
+
705
+
706
+ async def on_agent_run_end(
707
+ agent_name: str,
708
+ model_name: str,
709
+ session_id: str | None = None,
710
+ success: bool = True,
711
+ error: Exception | None = None,
712
+ response_text: str | None = None,
713
+ metadata: dict | None = None,
714
+ ) -> list[Any]:
715
+ """Trigger callbacks when an agent run ends.
716
+
717
+ This fires at the end of run, in the finally block.
718
+ Always fires regardless of success/failure/cancellation.
719
+
720
+ Useful for:
721
+ - Stopping background tasks (like token refresh heartbeats)
722
+ - Workflow orchestration (like Ralph's autonomous loop)
723
+ - Logging/analytics
724
+ - Resource cleanup
725
+ - Detecting completion signals in responses
726
+
727
+ Args:
728
+ agent_name: Name of the agent that finished
729
+ model_name: Name of the model that was used
730
+ session_id: Optional session identifier
731
+ success: Whether the run completed successfully
732
+ error: Exception if the run failed, None otherwise
733
+ response_text: The final text response from the agent (if successful)
734
+ metadata: Optional dict with additional context (tokens used, etc.)
735
+
736
+ Returns:
737
+ List of results from registered callbacks.
738
+ """
739
+ return await _trigger_callbacks(
740
+ "agent_run_end",
741
+ agent_name,
742
+ model_name,
743
+ session_id,
744
+ success,
745
+ error,
746
+ response_text,
747
+ metadata,
748
+ )
749
+
750
+
751
+ async def on_agent_run_result(
752
+ result: Any,
753
+ agent_name: str,
754
+ model_name: str,
755
+ ) -> list[Any]:
756
+ """Trigger callbacks after an agent run returns a result.
757
+
758
+ Fires after ``pydantic_agent.run()`` completes successfully, **before**
759
+ the result is handed back to the caller. Plugins can inspect the result
760
+ and request an automatic retry (e.g. when an upstream content-filter
761
+ produced a false-positive refusal).
762
+
763
+ Callback signature::
764
+
765
+ async def my_callback(result, agent_name: str, model_name: str)
766
+ -> dict | None
767
+
768
+ To request a retry, return a dict with::
769
+
770
+ {
771
+ "retry": True,
772
+ "prompt": "<message to send on retry>",
773
+ "delay": 1.0, # optional, seconds before retry
774
+ }
775
+
776
+ Return ``None`` (or omit a return) to let the result pass through.
777
+ The first callback that returns a retry request wins; the agent
778
+ replays at most a small fixed number of times to prevent runaway loops.
779
+
780
+ Args:
781
+ result: The ``RunResult`` returned by ``pydantic_agent.run()``.
782
+ agent_name: Name of the agent that produced the result.
783
+ model_name: Name of the model that was used.
784
+
785
+ Returns:
786
+ List of results from registered callbacks.
787
+ """
788
+ return await _trigger_callbacks("agent_run_result", result, agent_name, model_name)
789
+
790
+
791
+ def on_register_browser_types() -> list[Any]:
792
+ """Trigger callbacks to register custom browser types/providers.
793
+
794
+ Plugins can register callbacks that return a dict mapping browser type names
795
+ to initialization functions. This allows plugins to provide custom browser
796
+ implementations (like Camoufox for stealth browsing).
797
+
798
+ Each callback should return a dict with:
799
+ - key: str - the browser type name (e.g., "camoufox", "firefox-stealth")
800
+ - value: callable - async initialization function that takes (manager, **kwargs)
801
+ and sets up the browser on the manager instance
802
+
803
+ Example callback:
804
+ def register_my_browser_types():
805
+ return {
806
+ "camoufox": initialize_camoufox,
807
+ "my-stealth-browser": initialize_my_stealth,
808
+ }
809
+
810
+ Returns:
811
+ List of dicts from all registered callbacks.
812
+ """
813
+ return _trigger_callbacks_sync("register_browser_types")
814
+
815
+
816
+ def on_register_model_providers() -> list[Any]:
817
+ """Trigger callbacks to register custom model provider classes.
818
+
819
+ Plugins can register callbacks that return a dict mapping provider names
820
+ to model classes.
821
+
822
+ Returns:
823
+ List of dicts from all registered callbacks.
824
+ """
825
+ return _trigger_callbacks_sync("register_model_providers")
826
+
827
+
828
+ def on_message_history_processor_start(
829
+ agent_name: str,
830
+ session_id: str | None,
831
+ message_history: list[Any],
832
+ incoming_messages: list[Any],
833
+ ) -> list[Any]:
834
+ """Trigger callbacks at the start of message history processing.
835
+
836
+ This hook fires at the beginning of the message_history_accumulator,
837
+ before any deduplication or processing occurs. Useful for:
838
+ - Logging/debugging message flow
839
+ - Observing raw incoming messages
840
+ - Analytics on message history growth
841
+
842
+ Args:
843
+ agent_name: Name of the agent processing messages
844
+ session_id: Optional session identifier
845
+ message_history: Current message history (before processing)
846
+ incoming_messages: New messages being added
847
+
848
+ Returns:
849
+ List of results from registered callbacks.
850
+ """
851
+ return _trigger_callbacks_sync(
852
+ "message_history_processor_start",
853
+ agent_name,
854
+ session_id,
855
+ message_history,
856
+ incoming_messages,
857
+ )
858
+
859
+
860
+ def on_message_history_processor_end(
861
+ agent_name: str,
862
+ session_id: str | None,
863
+ message_history: list[Any],
864
+ messages_added: int,
865
+ messages_filtered: int,
866
+ ) -> list[Any]:
867
+ """Trigger callbacks at the end of message history processing.
868
+
869
+ This hook fires at the end of the message_history_accumulator,
870
+ after deduplication and filtering has been applied. Useful for:
871
+ - Logging/debugging final message state
872
+ - Analytics on deduplication effectiveness
873
+ - Observing what was actually added to history
874
+
875
+ Args:
876
+ agent_name: Name of the agent processing messages
877
+ session_id: Optional session identifier
878
+ message_history: Final message history (after processing)
879
+ messages_added: Count of new messages that were added
880
+ messages_filtered: Count of messages that were filtered out (dupes/empty)
881
+
882
+ Returns:
883
+ List of results from registered callbacks.
884
+ """
885
+ return _trigger_callbacks_sync(
886
+ "message_history_processor_end",
887
+ agent_name,
888
+ session_id,
889
+ message_history,
890
+ messages_added,
891
+ messages_filtered,
892
+ )
893
+
894
+
895
+ async def on_message(message_id: str, message: Any) -> list[Any]:
896
+ """Trigger callbacks when a message is emitted.
897
+
898
+ This is the global observation hook for the messaging system.
899
+ For per-message interception with pattern matching, use
900
+ messaging.interceptors.register_interceptor() instead.
901
+
902
+ This hook is for observation (logging, analytics, WebSocket forwarding),
903
+ while interceptors are for control (silencing, replacing, redirecting).
904
+
905
+ Args:
906
+ message_id: The well-known message identifier (e.g., "tool:edit_file:complete")
907
+ message: The full Pydantic BaseMessage model (or UIMessage for legacy)
908
+
909
+ Returns:
910
+ List of results from registered callbacks.
911
+ """
912
+ return await _trigger_callbacks("on_message", message_id, message)
913
+
914
+
915
+ def on_agent_run_context(agent, pydantic_agent, group_id) -> list[Any]:
916
+ """Collect async context managers that should wrap the ``pydantic_agent.run()`` call.
917
+
918
+ Each callback returns an async CM (with ``__aenter__``/``__aexit__``) or
919
+ ``None``. The caller composes all non-``None`` results via
920
+ ``contextlib.AsyncExitStack``.
921
+
922
+ Returns a list of async context managers (may be empty).
923
+ """
924
+ results = _trigger_callbacks_sync(
925
+ "agent_run_context", agent, pydantic_agent, group_id
926
+ )
927
+ return [r for r in results if r is not None]
928
+
929
+
930
+ async def on_agent_run_cancel(group_id: str) -> list[Any]:
931
+ """Fired when an agent run is cancelled or interrupted.
932
+
933
+ Plugins use this to cancel any external workflow tracking the run.
934
+ """
935
+ return await _trigger_callbacks("agent_run_cancel", group_id)
936
+
937
+
938
+ def on_should_skip_fallback_render(agent) -> bool:
939
+ """Return True if any plugin requests skipping the non-streaming fallback render."""
940
+ results = _trigger_callbacks_sync("should_skip_fallback_render", agent)
941
+ return any(r is True for r in results)