code-puppy-core-plugins 0.0.2__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 (313) hide show
  1. code_puppy_core_plugins/__init__.py +5 -0
  2. code_puppy_core_plugins/acp/README.md +266 -0
  3. code_puppy_core_plugins/acp/__init__.py +8 -0
  4. code_puppy_core_plugins/acp/agent.py +494 -0
  5. code_puppy_core_plugins/acp/bridge.py +309 -0
  6. code_puppy_core_plugins/acp/capabilities.py +73 -0
  7. code_puppy_core_plugins/acp/commands.py +116 -0
  8. code_puppy_core_plugins/acp/content.py +115 -0
  9. code_puppy_core_plugins/acp/io_delegation.py +231 -0
  10. code_puppy_core_plugins/acp/mcp_config.py +66 -0
  11. code_puppy_core_plugins/acp/permissions.py +159 -0
  12. code_puppy_core_plugins/acp/persistence.py +197 -0
  13. code_puppy_core_plugins/acp/register_callbacks.py +110 -0
  14. code_puppy_core_plugins/acp/replay.py +114 -0
  15. code_puppy_core_plugins/acp/session.py +329 -0
  16. code_puppy_core_plugins/acp/session_config.py +179 -0
  17. code_puppy_core_plugins/acp/state.py +135 -0
  18. code_puppy_core_plugins/agent_skills/__init__.py +22 -0
  19. code_puppy_core_plugins/agent_skills/config.py +208 -0
  20. code_puppy_core_plugins/agent_skills/discovery.py +337 -0
  21. code_puppy_core_plugins/agent_skills/downloader.py +392 -0
  22. code_puppy_core_plugins/agent_skills/enabled_skills.py +82 -0
  23. code_puppy_core_plugins/agent_skills/installer.py +22 -0
  24. code_puppy_core_plugins/agent_skills/metadata.py +219 -0
  25. code_puppy_core_plugins/agent_skills/prompt_builder.py +49 -0
  26. code_puppy_core_plugins/agent_skills/provider.py +52 -0
  27. code_puppy_core_plugins/agent_skills/register_callbacks.py +309 -0
  28. code_puppy_core_plugins/agent_skills/remote_catalog.py +322 -0
  29. code_puppy_core_plugins/agent_skills/skill_catalog.py +257 -0
  30. code_puppy_core_plugins/agent_skills/skill_commands.py +102 -0
  31. code_puppy_core_plugins/agent_skills/skills_install_menu.py +698 -0
  32. code_puppy_core_plugins/agent_skills/skills_menu.py +794 -0
  33. code_puppy_core_plugins/aws_bedrock/__init__.py +14 -0
  34. code_puppy_core_plugins/aws_bedrock/config.py +101 -0
  35. code_puppy_core_plugins/aws_bedrock/register_callbacks.py +243 -0
  36. code_puppy_core_plugins/aws_bedrock/utils.py +186 -0
  37. code_puppy_core_plugins/azure_foundry/README.md +238 -0
  38. code_puppy_core_plugins/azure_foundry/__init__.py +15 -0
  39. code_puppy_core_plugins/azure_foundry/config.py +126 -0
  40. code_puppy_core_plugins/azure_foundry/discovery.py +189 -0
  41. code_puppy_core_plugins/azure_foundry/register_callbacks.py +506 -0
  42. code_puppy_core_plugins/azure_foundry/token.py +182 -0
  43. code_puppy_core_plugins/azure_foundry/utils.py +375 -0
  44. code_puppy_core_plugins/btw/__init__.py +1 -0
  45. code_puppy_core_plugins/btw/inline_view.py +135 -0
  46. code_puppy_core_plugins/btw/register_callbacks.py +106 -0
  47. code_puppy_core_plugins/btw/side_query.py +91 -0
  48. code_puppy_core_plugins/chatgpt_oauth/IMAGEGEN_SKILL.md +83 -0
  49. code_puppy_core_plugins/chatgpt_oauth/__init__.py +8 -0
  50. code_puppy_core_plugins/chatgpt_oauth/config.py +52 -0
  51. code_puppy_core_plugins/chatgpt_oauth/image_generation.py +253 -0
  52. code_puppy_core_plugins/chatgpt_oauth/image_tool.py +70 -0
  53. code_puppy_core_plugins/chatgpt_oauth/oauth_flow.py +461 -0
  54. code_puppy_core_plugins/chatgpt_oauth/register_callbacks.py +288 -0
  55. code_puppy_core_plugins/chatgpt_oauth/test_plugin.py +663 -0
  56. code_puppy_core_plugins/chatgpt_oauth/usage.py +113 -0
  57. code_puppy_core_plugins/chatgpt_oauth/utils.py +534 -0
  58. code_puppy_core_plugins/claude_code_hooks/__init__.py +1 -0
  59. code_puppy_core_plugins/claude_code_hooks/config.py +137 -0
  60. code_puppy_core_plugins/claude_code_hooks/register_callbacks.py +383 -0
  61. code_puppy_core_plugins/claude_code_oauth/README.md +167 -0
  62. code_puppy_core_plugins/claude_code_oauth/SETUP.md +93 -0
  63. code_puppy_core_plugins/claude_code_oauth/__init__.py +25 -0
  64. code_puppy_core_plugins/claude_code_oauth/config.py +59 -0
  65. code_puppy_core_plugins/claude_code_oauth/fast_mode.py +122 -0
  66. code_puppy_core_plugins/claude_code_oauth/prompt_handler.py +64 -0
  67. code_puppy_core_plugins/claude_code_oauth/register_callbacks.py +719 -0
  68. code_puppy_core_plugins/claude_code_oauth/test_plugin.py +285 -0
  69. code_puppy_core_plugins/claude_code_oauth/token_refresh_heartbeat.py +240 -0
  70. code_puppy_core_plugins/claude_code_oauth/utils.py +663 -0
  71. code_puppy_core_plugins/code_puppy_agent/AGENTS_AND_TOOLS.md +155 -0
  72. code_puppy_core_plugins/code_puppy_agent/MODELS_AND_MCP.md +104 -0
  73. code_puppy_core_plugins/code_puppy_agent/PLUGINS_AND_CALLBACKS.md +147 -0
  74. code_puppy_core_plugins/code_puppy_agent/SESSIONS_AND_HISTORY.md +39 -0
  75. code_puppy_core_plugins/code_puppy_agent/SKILL.md +180 -0
  76. code_puppy_core_plugins/code_puppy_agent/SKILLS_SYSTEM.md +136 -0
  77. code_puppy_core_plugins/code_puppy_agent/SYSTEM_PROMPT_CONFIG_AND_I18N.md +160 -0
  78. code_puppy_core_plugins/code_puppy_agent/__init__.py +5 -0
  79. code_puppy_core_plugins/code_puppy_agent/register_callbacks.py +46 -0
  80. code_puppy_core_plugins/computer_use/NativeCapture.swift +123 -0
  81. code_puppy_core_plugins/computer_use/README.md +109 -0
  82. code_puppy_core_plugins/computer_use/__init__.py +1 -0
  83. code_puppy_core_plugins/computer_use/accessibility.py +163 -0
  84. code_puppy_core_plugins/computer_use/activation.py +75 -0
  85. code_puppy_core_plugins/computer_use/backend.py +584 -0
  86. code_puppy_core_plugins/computer_use/backend_types.py +5 -0
  87. code_puppy_core_plugins/computer_use/capture.py +110 -0
  88. code_puppy_core_plugins/computer_use/commands.py +62 -0
  89. code_puppy_core_plugins/computer_use/geometry.py +81 -0
  90. code_puppy_core_plugins/computer_use/inline_image.py +56 -0
  91. code_puppy_core_plugins/computer_use/keycodes.py +47 -0
  92. code_puppy_core_plugins/computer_use/policy.py +144 -0
  93. code_puppy_core_plugins/computer_use/register_callbacks.py +96 -0
  94. code_puppy_core_plugins/computer_use/safety.py +18 -0
  95. code_puppy_core_plugins/computer_use/settle.py +64 -0
  96. code_puppy_core_plugins/computer_use/state.py +86 -0
  97. code_puppy_core_plugins/computer_use/tools.py +469 -0
  98. code_puppy_core_plugins/context_indicator/__init__.py +14 -0
  99. code_puppy_core_plugins/context_indicator/register_callbacks.py +249 -0
  100. code_puppy_core_plugins/context_indicator/usage.py +40 -0
  101. code_puppy_core_plugins/copilot_auth/__init__.py +11 -0
  102. code_puppy_core_plugins/copilot_auth/config.py +93 -0
  103. code_puppy_core_plugins/copilot_auth/reasoning_client.py +409 -0
  104. code_puppy_core_plugins/copilot_auth/register_callbacks.py +460 -0
  105. code_puppy_core_plugins/copilot_auth/utils.py +587 -0
  106. code_puppy_core_plugins/customizable_commands/__init__.py +0 -0
  107. code_puppy_core_plugins/customizable_commands/register_callbacks.py +464 -0
  108. code_puppy_core_plugins/dbos_durable_exec/__init__.py +1 -0
  109. code_puppy_core_plugins/dbos_durable_exec/cancel.py +15 -0
  110. code_puppy_core_plugins/dbos_durable_exec/commands.py +31 -0
  111. code_puppy_core_plugins/dbos_durable_exec/config.py +31 -0
  112. code_puppy_core_plugins/dbos_durable_exec/lifecycle.py +83 -0
  113. code_puppy_core_plugins/dbos_durable_exec/register_callbacks.py +45 -0
  114. code_puppy_core_plugins/dbos_durable_exec/runtime.py +65 -0
  115. code_puppy_core_plugins/dbos_durable_exec/startup_lock.py +155 -0
  116. code_puppy_core_plugins/dbos_durable_exec/workflow_ids.py +16 -0
  117. code_puppy_core_plugins/dbos_durable_exec/wrapper.py +58 -0
  118. code_puppy_core_plugins/destructive_command_guard/AGENTS.md +79 -0
  119. code_puppy_core_plugins/destructive_command_guard/__init__.py +14 -0
  120. code_puppy_core_plugins/destructive_command_guard/detector.py +374 -0
  121. code_puppy_core_plugins/destructive_command_guard/register_callbacks.py +165 -0
  122. code_puppy_core_plugins/emoji_filter/__init__.py +12 -0
  123. code_puppy_core_plugins/emoji_filter/config.py +26 -0
  124. code_puppy_core_plugins/emoji_filter/register_callbacks.py +238 -0
  125. code_puppy_core_plugins/emoji_filter/stripper.py +45 -0
  126. code_puppy_core_plugins/empty_html_comment_filter/register_callbacks.py +116 -0
  127. code_puppy_core_plugins/example_custom_command/README.md +328 -0
  128. code_puppy_core_plugins/example_custom_command/register_callbacks.py +66 -0
  129. code_puppy_core_plugins/file_permission_handler/__init__.py +4 -0
  130. code_puppy_core_plugins/file_permission_handler/register_callbacks.py +575 -0
  131. code_puppy_core_plugins/flux_bootstrap/__init__.py +5 -0
  132. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/README.md +173 -0
  133. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/cheatsheet.md +86 -0
  134. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/pipeline.md +167 -0
  135. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/synopsis.md +392 -0
  136. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/about.md +51 -0
  137. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/address-feedback.md +129 -0
  138. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/ask.md +211 -0
  139. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/aug.md +181 -0
  140. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/auto-pilot.md +71 -0
  141. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/cheatsheet.md +16 -0
  142. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/commit.md +103 -0
  143. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/config.md +108 -0
  144. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/create-pr.md +195 -0
  145. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/exec.md +221 -0
  146. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/new.md +151 -0
  147. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/qa.md +208 -0
  148. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/rebase.md +180 -0
  149. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/review.md +304 -0
  150. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/split.md +120 -0
  151. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/squash-commits.md +179 -0
  152. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/status.md +46 -0
  153. code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/tests.md +155 -0
  154. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_about.py +154 -0
  155. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_cheatsheet.py +248 -0
  156. code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_status.py +345 -0
  157. code_puppy_core_plugins/flux_bootstrap/installer.py +256 -0
  158. code_puppy_core_plugins/flux_bootstrap/register_callbacks.py +71 -0
  159. code_puppy_core_plugins/force_push_guard/__init__.py +5 -0
  160. code_puppy_core_plugins/force_push_guard/detector.py +96 -0
  161. code_puppy_core_plugins/force_push_guard/register_callbacks.py +161 -0
  162. code_puppy_core_plugins/fork/__init__.py +7 -0
  163. code_puppy_core_plugins/fork/register_callbacks.py +445 -0
  164. code_puppy_core_plugins/frontend_emitter/__init__.py +25 -0
  165. code_puppy_core_plugins/frontend_emitter/emitter.py +249 -0
  166. code_puppy_core_plugins/frontend_emitter/register_callbacks.py +412 -0
  167. code_puppy_core_plugins/frontend_emitter/session_context.py +50 -0
  168. code_puppy_core_plugins/grok_oauth/__init__.py +6 -0
  169. code_puppy_core_plugins/grok_oauth/config.py +49 -0
  170. code_puppy_core_plugins/grok_oauth/oauth_flow.py +213 -0
  171. code_puppy_core_plugins/grok_oauth/register_callbacks.py +132 -0
  172. code_puppy_core_plugins/grok_oauth/utils.py +195 -0
  173. code_puppy_core_plugins/herdr/README.md +168 -0
  174. code_puppy_core_plugins/herdr/__init__.py +9 -0
  175. code_puppy_core_plugins/herdr/client.py +339 -0
  176. code_puppy_core_plugins/herdr/register_callbacks.py +140 -0
  177. code_puppy_core_plugins/herdr/reporter.py +212 -0
  178. code_puppy_core_plugins/herdr/smoke.py +143 -0
  179. code_puppy_core_plugins/herdr/sources.py +133 -0
  180. code_puppy_core_plugins/hook_creator/__init__.py +1 -0
  181. code_puppy_core_plugins/hook_creator/register_callbacks.py +33 -0
  182. code_puppy_core_plugins/hook_manager/__init__.py +1 -0
  183. code_puppy_core_plugins/hook_manager/config.py +290 -0
  184. code_puppy_core_plugins/hook_manager/hooks_menu.py +571 -0
  185. code_puppy_core_plugins/hook_manager/register_callbacks.py +227 -0
  186. code_puppy_core_plugins/meta_oauth/__init__.py +1 -0
  187. code_puppy_core_plugins/meta_oauth/config.py +55 -0
  188. code_puppy_core_plugins/meta_oauth/oauth_flow.py +109 -0
  189. code_puppy_core_plugins/meta_oauth/register_callbacks.py +154 -0
  190. code_puppy_core_plugins/meta_oauth/utils.py +252 -0
  191. code_puppy_core_plugins/namespace_skill_search/README.md +118 -0
  192. code_puppy_core_plugins/namespace_skill_search/__init__.py +15 -0
  193. code_puppy_core_plugins/namespace_skill_search/namespaces.py +125 -0
  194. code_puppy_core_plugins/namespace_skill_search/register_callbacks.py +99 -0
  195. code_puppy_core_plugins/namespace_skill_search/search_tool.py +158 -0
  196. code_puppy_core_plugins/no_tools/__init__.py +0 -0
  197. code_puppy_core_plugins/no_tools/register_callbacks.py +42 -0
  198. code_puppy_core_plugins/oauth_pasteback.py +110 -0
  199. code_puppy_core_plugins/oauth_puppy_html.py +194 -0
  200. code_puppy_core_plugins/obsidian_agent/README.md +52 -0
  201. code_puppy_core_plugins/obsidian_agent/__init__.py +8 -0
  202. code_puppy_core_plugins/obsidian_agent/agent_obsidian.py +147 -0
  203. code_puppy_core_plugins/obsidian_agent/register_callbacks.py +13 -0
  204. code_puppy_core_plugins/ollama/__init__.py +1 -0
  205. code_puppy_core_plugins/ollama/register_callbacks.py +127 -0
  206. code_puppy_core_plugins/ollama_setup/__init__.py +5 -0
  207. code_puppy_core_plugins/ollama_setup/completer.py +38 -0
  208. code_puppy_core_plugins/ollama_setup/register_callbacks.py +419 -0
  209. code_puppy_core_plugins/plugin_list/__init__.py +0 -0
  210. code_puppy_core_plugins/plugin_list/plugin_contributions.py +348 -0
  211. code_puppy_core_plugins/plugin_list/plugin_meta.py +67 -0
  212. code_puppy_core_plugins/plugin_list/plugin_text_utils.py +173 -0
  213. code_puppy_core_plugins/plugin_list/plugins_menu.py +455 -0
  214. code_puppy_core_plugins/plugin_list/plugins_menu_layout.py +194 -0
  215. code_puppy_core_plugins/plugin_list/plugins_menu_render.py +431 -0
  216. code_puppy_core_plugins/plugin_list/project_trust_flow.py +162 -0
  217. code_puppy_core_plugins/plugin_list/register_callbacks.py +281 -0
  218. code_puppy_core_plugins/pop_command/__init__.py +1 -0
  219. code_puppy_core_plugins/pop_command/register_callbacks.py +191 -0
  220. code_puppy_core_plugins/prompt_newline/__init__.py +13 -0
  221. code_puppy_core_plugins/prompt_newline/config.py +21 -0
  222. code_puppy_core_plugins/prompt_newline/register_callbacks.py +163 -0
  223. code_puppy_core_plugins/prune/__init__.py +1 -0
  224. code_puppy_core_plugins/prune/prune_menu.py +308 -0
  225. code_puppy_core_plugins/prune/prune_model.py +517 -0
  226. code_puppy_core_plugins/prune/prune_render.py +354 -0
  227. code_puppy_core_plugins/prune/register_callbacks.py +388 -0
  228. code_puppy_core_plugins/puppy_kennel/README.md +203 -0
  229. code_puppy_core_plugins/puppy_kennel/__init__.py +6 -0
  230. code_puppy_core_plugins/puppy_kennel/commands.py +206 -0
  231. code_puppy_core_plugins/puppy_kennel/config.py +36 -0
  232. code_puppy_core_plugins/puppy_kennel/kennel.py +368 -0
  233. code_puppy_core_plugins/puppy_kennel/packer.py +152 -0
  234. code_puppy_core_plugins/puppy_kennel/recorder.py +83 -0
  235. code_puppy_core_plugins/puppy_kennel/register_callbacks.py +109 -0
  236. code_puppy_core_plugins/puppy_kennel/retriever.py +28 -0
  237. code_puppy_core_plugins/puppy_kennel/schema.py +78 -0
  238. code_puppy_core_plugins/puppy_kennel/state.py +68 -0
  239. code_puppy_core_plugins/puppy_kennel/tools.py +449 -0
  240. code_puppy_core_plugins/puppy_kennel/wings.py +110 -0
  241. code_puppy_core_plugins/puppy_spinner/README.md +93 -0
  242. code_puppy_core_plugins/puppy_spinner/__init__.py +11 -0
  243. code_puppy_core_plugins/puppy_spinner/builtin_frames.py +172 -0
  244. code_puppy_core_plugins/puppy_spinner/commands.py +116 -0
  245. code_puppy_core_plugins/puppy_spinner/picker.py +379 -0
  246. code_puppy_core_plugins/puppy_spinner/register_callbacks.py +193 -0
  247. code_puppy_core_plugins/puppy_spinner/spinners.py +444 -0
  248. code_puppy_core_plugins/quick_resume/__init__.py +0 -0
  249. code_puppy_core_plugins/quick_resume/register_callbacks.py +77 -0
  250. code_puppy_core_plugins/review_pr/__init__.py +1 -0
  251. code_puppy_core_plugins/review_pr/register_callbacks.py +162 -0
  252. code_puppy_core_plugins/shell_safety/__init__.py +6 -0
  253. code_puppy_core_plugins/shell_safety/agent_shell_safety.py +69 -0
  254. code_puppy_core_plugins/shell_safety/command_cache.py +156 -0
  255. code_puppy_core_plugins/shell_safety/register_callbacks.py +203 -0
  256. code_puppy_core_plugins/stack_dump/__init__.py +1 -0
  257. code_puppy_core_plugins/stack_dump/register_callbacks.py +79 -0
  258. code_puppy_core_plugins/statusline/__init__.py +14 -0
  259. code_puppy_core_plugins/statusline/config.py +68 -0
  260. code_puppy_core_plugins/statusline/payload.py +138 -0
  261. code_puppy_core_plugins/statusline/prompt_patch.py +76 -0
  262. code_puppy_core_plugins/statusline/register_callbacks.py +45 -0
  263. code_puppy_core_plugins/statusline/runner.py +122 -0
  264. code_puppy_core_plugins/statusline/statusline_command.py +197 -0
  265. code_puppy_core_plugins/steer_queue/__init__.py +10 -0
  266. code_puppy_core_plugins/steer_queue/queue_menu.py +444 -0
  267. code_puppy_core_plugins/steer_queue/register_callbacks.py +138 -0
  268. code_puppy_core_plugins/subagent_panel/README.md +102 -0
  269. code_puppy_core_plugins/subagent_panel/__init__.py +24 -0
  270. code_puppy_core_plugins/subagent_panel/coalesce_patch.py +242 -0
  271. code_puppy_core_plugins/subagent_panel/panel_render.py +145 -0
  272. code_puppy_core_plugins/subagent_panel/register_callbacks.py +535 -0
  273. code_puppy_core_plugins/subagent_panel/resume_repaint.py +112 -0
  274. code_puppy_core_plugins/subagent_panel/state.py +231 -0
  275. code_puppy_core_plugins/switch_agent_resume/__init__.py +5 -0
  276. code_puppy_core_plugins/switch_agent_resume/register_callbacks.py +314 -0
  277. code_puppy_core_plugins/theme/README.md +86 -0
  278. code_puppy_core_plugins/theme/__init__.py +14 -0
  279. code_puppy_core_plugins/theme/bundled_palettes.py +346 -0
  280. code_puppy_core_plugins/theme/content_styles.py +146 -0
  281. code_puppy_core_plugins/theme/osc_palette.py +178 -0
  282. code_puppy_core_plugins/theme/picker.py +358 -0
  283. code_puppy_core_plugins/theme/prompt_toolkit_theme.py +113 -0
  284. code_puppy_core_plugins/theme/register_callbacks.py +348 -0
  285. code_puppy_core_plugins/theme/rich_themes.py +202 -0
  286. code_puppy_core_plugins/theme/themes.py +720 -0
  287. code_puppy_core_plugins/timestamp_heartbeat/__init__.py +1 -0
  288. code_puppy_core_plugins/timestamp_heartbeat/register_callbacks.py +114 -0
  289. code_puppy_core_plugins/token_ratio_learner/__init__.py +5 -0
  290. code_puppy_core_plugins/token_ratio_learner/ratios.py +208 -0
  291. code_puppy_core_plugins/token_ratio_learner/register_callbacks.py +213 -0
  292. code_puppy_core_plugins/universal_constructor/__init__.py +13 -0
  293. code_puppy_core_plugins/universal_constructor/models.py +138 -0
  294. code_puppy_core_plugins/universal_constructor/provider.py +62 -0
  295. code_puppy_core_plugins/universal_constructor/register_callbacks.py +75 -0
  296. code_puppy_core_plugins/universal_constructor/registry.py +302 -0
  297. code_puppy_core_plugins/universal_constructor/sandbox.py +584 -0
  298. code_puppy_core_plugins/wide_completion_menu/__init__.py +6 -0
  299. code_puppy_core_plugins/wide_completion_menu/register_callbacks.py +165 -0
  300. code_puppy_core_plugins/wiggum/__init__.py +1 -0
  301. code_puppy_core_plugins/wiggum/goal_runs.py +91 -0
  302. code_puppy_core_plugins/wiggum/judge.py +299 -0
  303. code_puppy_core_plugins/wiggum/judge_config.py +249 -0
  304. code_puppy_core_plugins/wiggum/judges_menu.py +814 -0
  305. code_puppy_core_plugins/wiggum/register_callbacks.py +569 -0
  306. code_puppy_core_plugins/wiggum/state.py +89 -0
  307. code_puppy_core_plugins/yolo_cli/__init__.py +1 -0
  308. code_puppy_core_plugins/yolo_cli/register_callbacks.py +41 -0
  309. code_puppy_core_plugins-0.0.2.dist-info/METADATA +64 -0
  310. code_puppy_core_plugins-0.0.2.dist-info/RECORD +313 -0
  311. code_puppy_core_plugins-0.0.2.dist-info/WHEEL +4 -0
  312. code_puppy_core_plugins-0.0.2.dist-info/entry_points.txt +55 -0
  313. code_puppy_core_plugins-0.0.2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,535 @@
1
+ """subagent_panel -- live per-sub-agent status rows on the bottom bar.
2
+
3
+ While running, the panel renders one aligned row per sub-agent on the
4
+ bottom bar's panel rows (above the status/context row, pinned outside
5
+ the scroll region):
6
+
7
+ \U0001f916 INVOKE AGENT <name> <model> <spin> 00:19 calling read_file
8
+
9
+ On completion, a PERSISTENT frozen record is printed to the transcript that
10
+ mirrors the live look, with the status finalized green + check.
11
+
12
+ Install strategy (startup monkeypatches of seams with no hook + callbacks):
13
+ 1. Live panel -> rendered to PLAIN text lines and pushed to
14
+ ``bottom_bar.set_panel_lines()`` on every state change (event-driven;
15
+ no Rich Live, no dedicated animation thread — the spin frame advances
16
+ whenever a stream event lands).
17
+ 2. RichConsoleRenderer._render_subagent_invocation -> CAPTURE exact metadata
18
+ (name/model/session_id) + SUPPRESS the permanent banner.
19
+ 3. RichConsoleRenderer._do_render -> when a SubAgentResponseMessage arrives
20
+ (core skips it), handle the frozen record. A NESTED child is marked done
21
+ but KEPT in the live tree (shown 'completed') so it never vanishes
22
+ mid-run; the whole subtree flushes to the transcript parent-first only
23
+ when its ROOT finishes, then is removed from the live tree.
24
+ 4. subagent_invocation.emit_success -> suppress the redundant
25
+ "<check> <name> completed successfully" line (it comes from the separate
26
+ message_queue system, NOT the bus, so it must be dropped at its source).
27
+ + stream_event callback feeds the live status (update-only).
28
+
29
+ Startup hard-disable with DISABLE_SUBAGENT_PANEL=1 or SUBAGENT_PANEL=0.
30
+ Runtime toggle with /set subagent_panel off|on.
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import os
36
+ import asyncio
37
+ import threading
38
+ import time
39
+ from contextvars import ContextVar
40
+
41
+ _TRUTHY = {"1", "true", "yes", "on"}
42
+ _FALSEY = {"0", "false", "no", "off"}
43
+
44
+
45
+ def _env_truthy(name: str) -> bool:
46
+ return os.environ.get(name, "").strip().lower() in _TRUTHY
47
+
48
+
49
+ def _env_falsey(name: str) -> bool:
50
+ value = os.environ.get(name)
51
+ return value is not None and value.strip().lower() in _FALSEY
52
+
53
+
54
+ _DISABLED = _env_truthy("DISABLE_SUBAGENT_PANEL") or _env_falsey("SUBAGENT_PANEL")
55
+ _CONFIG_KEY = "subagent_panel"
56
+
57
+
58
+ def _config_value(name: str) -> str | None:
59
+ try:
60
+ from code_puppy.config import get_value
61
+
62
+ value = get_value(name)
63
+ return None if value is None else str(value).strip().lower()
64
+ except Exception:
65
+ return None
66
+
67
+
68
+ def _runtime_enabled() -> bool:
69
+ if _DISABLED:
70
+ return False
71
+ if _config_value(f"disable_{_CONFIG_KEY}") in _TRUTHY:
72
+ enabled = False
73
+ elif _config_value(_CONFIG_KEY) in _FALSEY:
74
+ enabled = False
75
+ else:
76
+ enabled = True
77
+ if not enabled:
78
+ try:
79
+ state.clear()
80
+ except Exception:
81
+ pass
82
+ return enabled
83
+
84
+
85
+ if not _DISABLED:
86
+ from code_puppy.callbacks import register_callback
87
+
88
+ from . import coalesce_patch, resume_repaint, state
89
+
90
+ # ContextVar isolates each task's parent session; the bus's global pointer lets
91
+ # concurrent siblings clobber one another's parent.
92
+ _PARENT_SID: ContextVar = ContextVar("subagent_panel_parent_sid", default=None)
93
+
94
+
95
+ # Shared rendering helpers are re-exported from panel_render for compatibility.
96
+ from .panel_render import ( # noqa: E402
97
+ _model_short as _model_short, # noqa: F401 (re-export)
98
+ _ordered_tree,
99
+ _row_lines,
100
+ )
101
+
102
+
103
+ def _resolve_model(agent_name, override):
104
+ """Resolve the EFFECTIVE model for a sub-agent (override -> pinned ->
105
+ global default), mirroring subagent_invocation's precedence. The invocation
106
+ message only carries the override (usually None), so we ask the agent config
107
+ directly. Returns the override (or None) on any failure."""
108
+ try:
109
+ from code_puppy.agents.agent_manager import load_agent
110
+
111
+ cfg = load_agent(agent_name)
112
+ if override:
113
+ with cfg.temporary_model_name_override(override):
114
+ return cfg.get_model_name()
115
+ return cfg.get_model_name()
116
+ except Exception:
117
+ return override
118
+
119
+
120
+ # Live panel rendering: one aligned, complete row per tracked agent, rendered via
121
+ # _row_lines() and pushed as styled Text to match the frozen transcript.
122
+
123
+ #: Same-shape repaints (spin frame / elapsed churn) at most 10x/second.
124
+ _PUSH_MIN_INTERVAL = 0.1
125
+ # NOTE: Deliberately unsynchronized; a race causes only one extra/skipped repaint,
126
+ # while locking would add contention on the hot stream-event path.
127
+ _push_state = {"t": 0.0, "count": -1}
128
+
129
+
130
+ def _panel_lines():
131
+ """Render the live tree to styled Text rows for the bottom-bar panel."""
132
+ if not _runtime_enabled():
133
+ return []
134
+ rows = state.snapshot()
135
+ if not rows:
136
+ return []
137
+ return list(_row_lines(_ordered_tree(rows), state.spinner_frame()))
138
+
139
+
140
+ # 4 Hz ticker keeps elapsed clocks/spin frames moving during silent model calls;
141
+ # atomic bottom-bar writes keep repaints flicker-safe.
142
+
143
+ _TICK_INTERVAL_S = 0.25
144
+ _ticker_task: "asyncio.Task | None" = None
145
+ _ticker_lock = threading.Lock()
146
+
147
+
148
+ def _start_ticker() -> None:
149
+ """Start the ticker task (idempotent; no-op without a running loop)."""
150
+ global _ticker_task
151
+ try:
152
+ loop = asyncio.get_running_loop()
153
+ except RuntimeError:
154
+ return # no loop (sync/odd context): stay event-driven only
155
+ with _ticker_lock:
156
+ if _ticker_task is not None and not _ticker_task.done():
157
+ return
158
+ _ticker_task = loop.create_task(_tick_loop())
159
+
160
+
161
+ def _stop_ticker() -> None:
162
+ """Cancel the ticker task if it's running. Idempotent."""
163
+ global _ticker_task
164
+ with _ticker_lock:
165
+ task = _ticker_task
166
+ _ticker_task = None
167
+ if task is not None and not task.done():
168
+ task.cancel()
169
+
170
+
171
+ async def _tick_loop() -> None:
172
+ """Repaint ~4x/second so clocks + spin frames advance during silence."""
173
+ global _ticker_task
174
+ try:
175
+ while True:
176
+ await asyncio.sleep(_TICK_INTERVAL_S)
177
+ if not state.has_active():
178
+ break # belt-and-braces: never outlive the swarm
179
+ _push_panel()
180
+ except asyncio.CancelledError:
181
+ pass
182
+ finally:
183
+ with _ticker_lock:
184
+ if _ticker_task is asyncio.current_task():
185
+ _ticker_task = None
186
+
187
+
188
+ def _push_panel(force: bool = False) -> None:
189
+ """Push the rendered panel to the bottom bar (throttled).
190
+
191
+ ``force=True`` bypasses the throttle — used for shape-changing events
192
+ (register / done / clear) so grow/collapse is never delayed.
193
+ """
194
+ try:
195
+ lines = _panel_lines()
196
+ now = time.time()
197
+ if (
198
+ not force
199
+ and len(lines) == _push_state["count"]
200
+ and now - _push_state["t"] < _PUSH_MIN_INTERVAL
201
+ ):
202
+ return # frame/elapsed churn — skip this repaint
203
+ _push_state["t"] = now
204
+ _push_state["count"] = len(lines)
205
+ from code_puppy.messaging.bottom_bar import get_bottom_bar
206
+
207
+ get_bottom_bar().set_panel_lines(lines)
208
+ except Exception:
209
+ pass
210
+
211
+
212
+ # Frozen (persistent) completion record
213
+ def _handle_frozen(console, session_id):
214
+ """A sub-agent finished. Mark it done but KEEP it grouped in the live panel
215
+ (rendered as 'completed'). The ENTIRE panel flushes to the transcript as one
216
+ cohesive group ONLY once the whole swarm is idle (every tracked agent done).
217
+ So a finished root never commits mid-swarm, and nothing -- steer lines or
218
+ otherwise -- can ever be interleaved between the grouped agent rows.
219
+ """
220
+ if not session_id:
221
+ return
222
+ state.mark_done(session_id)
223
+ _maybe_flush_group(console)
224
+ _push_panel(force=True)
225
+
226
+
227
+ def _maybe_flush_group(console):
228
+ """Flush the WHOLE live panel to scrollback as one group (parent-first DFS),
229
+ then clear it -- but ONLY when no agent is still active. While any agent is
230
+ running, completed agents stay grouped in the live panel (shown 'completed'),
231
+ so the panel remains a single cohesive block pinned above the spinner.
232
+ """
233
+ rows = state.snapshot()
234
+ if not rows:
235
+ return
236
+ if any(not e.get("done") for e in rows):
237
+ return # swarm still busy -- keep the panel grouped + live
238
+ if console is None:
239
+ return
240
+ ordered = _ordered_tree(rows)
241
+ console.print() # breathing room between the transcript and the group
242
+ for line in _row_lines(ordered, frame=None):
243
+ console.print(line)
244
+ state.clear()
245
+ _stop_ticker() # last agent flushed — nothing left to clock
246
+ _push_panel(force=True) # collapse the panel rows
247
+
248
+
249
+ # Monkeypatch installers
250
+ def _install_parent_tracking() -> None:
251
+ """Mirror set_session_context() into an async-safe ContextVar.
252
+
253
+ subagent_invocation.py calls set_session_context(child_sid) at :118 (in the
254
+ INVITING agent's task) BEFORE create_task() spawns the child's run at :272.
255
+ create_task copies the current context, so the child task inherits
256
+ _PARENT_SID = its own sid; when the child later invokes a grandchild, the
257
+ emit hook reads _PARENT_SID = the child sid = the true parent. Concurrent
258
+ roots can't clobber each other because each task has its own copy (unlike
259
+ the bus's single global _current_session_id).
260
+ """
261
+ import code_puppy.tools.subagent_invocation as sai
262
+
263
+ original = sai.set_session_context
264
+ if getattr(original, "_subagent_panel", False):
265
+ return
266
+
267
+ def _set(session_id):
268
+ if _runtime_enabled():
269
+ try:
270
+ _PARENT_SID.set(session_id)
271
+ except Exception:
272
+ pass
273
+ return original(session_id)
274
+
275
+ _set._subagent_panel = True
276
+ sai.set_session_context = _set
277
+
278
+
279
+ def _install_emit_hook() -> None:
280
+ """Register sub-agents (with parent + model) at EMIT time.
281
+
282
+ emit() at subagent_invocation.py:105 runs BEFORE set_session_context(child)
283
+ at :118, so at emit time _PARENT_SID still holds the INVITING agent's sid
284
+ (or None in the main agent's task -> a root). We read the async-safe
285
+ ContextVar, NOT the bus's global _current_session_id, so parallel invokes
286
+ don't cross-wire their parents.
287
+ """
288
+ from code_puppy.messaging.bus import MessageBus
289
+
290
+ current = MessageBus.emit
291
+ if getattr(current, "_subagent_panel", False):
292
+ return
293
+
294
+ def _emit(self, message):
295
+ try:
296
+ if (
297
+ _runtime_enabled()
298
+ and type(message).__name__ == "SubAgentInvocationMessage"
299
+ ):
300
+ parent = _PARENT_SID.get()
301
+ model = _resolve_model(
302
+ message.agent_name, getattr(message, "model_name", None)
303
+ )
304
+ state.register(message.session_id, message.agent_name, model, parent)
305
+ _push_panel(force=True)
306
+ _start_ticker() # keeps mm:ss advancing through silence
307
+ except Exception:
308
+ pass
309
+ return current(self, message)
310
+
311
+ _emit._subagent_panel = True
312
+ MessageBus.emit = _emit
313
+
314
+
315
+ def _install_banner_capture() -> None:
316
+ """Suppress the core permanent invocation banner. Registration now happens
317
+ in the emit hook (where the parent is knowable), so this only suppresses."""
318
+ from code_puppy.messaging.rich_renderer import RichConsoleRenderer
319
+
320
+ current = RichConsoleRenderer._render_subagent_invocation
321
+ if getattr(current, "_subagent_panel", False):
322
+ return
323
+
324
+ def _capture(self, msg):
325
+ if not _runtime_enabled():
326
+ return current(self, msg)
327
+ return # suppress the permanent banner -- live block owns it
328
+
329
+ _capture._subagent_panel = True
330
+ RichConsoleRenderer._render_subagent_invocation = _capture
331
+
332
+
333
+ def _install_render_wrapper() -> None:
334
+ """Wrap _do_render to (a) print a frozen record when a sub-agent response
335
+ arrives and (b) suppress the redundant completion text line."""
336
+ from code_puppy.messaging.rich_renderer import RichConsoleRenderer
337
+
338
+ current = RichConsoleRenderer._do_render
339
+ if getattr(current, "_subagent_panel", False):
340
+ return
341
+
342
+ def _wrapped(self, message):
343
+ try:
344
+ if (
345
+ _runtime_enabled()
346
+ and type(message).__name__ == "SubAgentResponseMessage"
347
+ ):
348
+ _handle_frozen(self._console, getattr(message, "session_id", None))
349
+ # Core skips SubAgentResponseMessage anyway -> fully handled.
350
+ return
351
+ except Exception:
352
+ pass
353
+ return current(self, message)
354
+
355
+ _wrapped._subagent_panel = True
356
+ RichConsoleRenderer._do_render = _wrapped
357
+
358
+
359
+ def _install_suppress_completion() -> None:
360
+ """Suppress the redundant '<check> <name> completed successfully' line.
361
+
362
+ That line is emitted by subagent_invocation via message_queue.emit_success
363
+ (a DIFFERENT system than the bus that _do_render renders), so it must be
364
+ suppressed at its source. We patch the name bound in subagent_invocation's
365
+ namespace -- the only caller -- and pass everything else through.
366
+ """
367
+ import code_puppy.tools.subagent_invocation as sai
368
+
369
+ current = getattr(sai, "emit_success", None)
370
+ if current is None or getattr(current, "_subagent_panel", False):
371
+ return
372
+
373
+ def _filtered(content, *args, **kwargs):
374
+ try:
375
+ text = str(content).strip()
376
+ if (
377
+ _runtime_enabled()
378
+ and text.endswith("completed successfully")
379
+ and "\u2713" in text
380
+ ):
381
+ return # the frozen record already says "completed"
382
+ except Exception:
383
+ pass
384
+ return current(content, *args, **kwargs)
385
+
386
+ _filtered._subagent_panel = True
387
+ sai.emit_success = _filtered
388
+
389
+
390
+ # TODO(deferred): replace monkeypatches with subagent lifecycle hooks so this
391
+ # plugin no longer depends on private call signatures.
392
+ def _install() -> None:
393
+ if _DISABLED:
394
+ return
395
+ for installer in (
396
+ lambda: coalesce_patch.install(_runtime_enabled),
397
+ _install_parent_tracking,
398
+ _install_emit_hook,
399
+ _install_banner_capture,
400
+ _install_render_wrapper,
401
+ _install_suppress_completion,
402
+ lambda: resume_repaint.install(_runtime_enabled, state, _push_panel),
403
+ ):
404
+ try:
405
+ installer()
406
+ except Exception:
407
+ pass
408
+
409
+
410
+ # Status callback
411
+ async def _on_stream_event(event_type, event_data, agent_session_id=None):
412
+ if not _runtime_enabled():
413
+ return
414
+ try:
415
+ state.record_event(agent_session_id, event_type, event_data)
416
+ except Exception:
417
+ pass
418
+ # Event-driven animation advances frame/elapsed on each repaint; _push_panel
419
+ # throttles same-shape churn to ~10 fps.
420
+ _push_panel()
421
+
422
+
423
+ async def _on_agent_run_end(
424
+ agent_name=None,
425
+ model_name=None,
426
+ session_id=None,
427
+ success=True,
428
+ error=None,
429
+ response_text=None,
430
+ metadata=None,
431
+ ):
432
+ """When the TOP-LEVEL turn ends, wipe all tracked sub-agents so a root that
433
+ errored or was cancelled (never flushed) can't leak its 'completed' children
434
+ into the next prompt's live block. Only fires for the main agent -- sub-agent
435
+ runs go through temp_agent.run(), not _runtime, and is_subagent() guards the
436
+ rest.
437
+ """
438
+ if not _runtime_enabled():
439
+ return
440
+ try:
441
+ from code_puppy.tools.subagent_context import is_subagent
442
+
443
+ if is_subagent():
444
+ return # a sub-agent finishing -- leave the live tree intact
445
+ except Exception:
446
+ pass
447
+ try:
448
+ state.clear()
449
+ except Exception:
450
+ pass
451
+ _stop_ticker() # run over — never leave an orphan clock task
452
+ _push_panel(force=True)
453
+
454
+
455
+ async def _on_agent_run_cancel(group_id=None):
456
+ """Ctrl+C / cancel path: stop the ticker IMMEDIATELY and collapse.
457
+
458
+ ``_tear_down_live_panels`` (core, sync signal handler) already wipes
459
+ the bar's panel rows; without this hook the ticker's next 1s tick
460
+ would repaint the stale rows right back until ``agent_run_end``
461
+ fires. Clearing state here is idempotent with the run-end cleanup.
462
+ """
463
+ if not _runtime_enabled():
464
+ return
465
+ _stop_ticker()
466
+ try:
467
+ state.clear()
468
+ except Exception:
469
+ pass
470
+ _push_panel(force=True)
471
+
472
+
473
+ async def _on_post_tool_call(tool_name, tool_args, result, duration_ms, context=None):
474
+ """Authoritative completion **state** signal for sub-agent invocations.
475
+
476
+ Background: the primary completion path is ``SubAgentResponseMessage`` ->
477
+ ``_handle_frozen`` -> ``state.mark_done``. But ``_invoke_agent_impl`` in
478
+ ``tools/subagent_invocation.py`` suppresses that emit in high-output mode
479
+ when tokens have already streamed inline (to avoid double-rendering the
480
+ response). Without a fallback the row would sit frozen on its last
481
+ ``stream_event``-derived status forever -- a parent blocked awaiting
482
+ children emits no further ``part_start`` events, so it stays on
483
+ ``"thinking..."`` until the next top-level turn clears the panel.
484
+
485
+ ``post_tool_call`` fires whenever the tool returns -- success OR failure,
486
+ high mode OR not, response message emitted OR suppressed. It carries the
487
+ ``AgentInvokeOutput`` (whose ``.session_id`` and ``.error`` are the durable
488
+ truth). Idempotent with ``_handle_frozen``: ``mark_done`` / ``mark_failed``
489
+ are set-then-keep on the entry, so the dual-fire path stays correct.
490
+
491
+ Why we do NOT call ``_maybe_flush_group`` from here
492
+ ---------------------------------------------------
493
+ This callback runs from the pydantic-ai agent run loop -- OUTSIDE the
494
+ renderer's coordination path. ``_handle_frozen`` can safely call flush
495
+ because it lives inside ``_do_render``: the Rich Live region is paused /
496
+ coordinated for that paint. Printing from an out-of-band task races the
497
+ main agent's streaming tokens and produces character-level collisions in
498
+ the terminal (visible as garbled, interleaved output).
499
+
500
+ Mid-turn flush in high mode is therefore deferred to whichever
501
+ render-serialized hook eventually fires next: ``_handle_frozen`` when a
502
+ later sub-agent response IS emitted, or ``_on_agent_run_end`` /
503
+ ``state.clear()`` at end of turn. The row at least now reads
504
+ ``"completed"`` instead of lying about ``"thinking..."``; finding a
505
+ render-serialized mid-turn flush trigger for high mode is a follow-up.
506
+ """
507
+ if not _runtime_enabled():
508
+ return
509
+ if tool_name not in ("invoke_agent", "invoke_agent_with_model"):
510
+ return
511
+ sid = getattr(result, "session_id", None)
512
+ if not sid:
513
+ return
514
+ try:
515
+ # Keep ordinary calls until their foreground root flushes; detached forks
516
+ # have no root, so finish them at their own boundary.
517
+ if isinstance(context, dict) and context.get("detached_fork"):
518
+ state.finish(sid)
519
+ else:
520
+ err = getattr(result, "error", None)
521
+ if err:
522
+ state.mark_failed(sid)
523
+ else:
524
+ state.mark_done(sid)
525
+ except Exception:
526
+ pass
527
+ _push_panel(force=True)
528
+
529
+
530
+ if not _DISABLED:
531
+ register_callback("startup", _install)
532
+ register_callback("stream_event", _on_stream_event)
533
+ register_callback("agent_run_end", _on_agent_run_end)
534
+ register_callback("agent_run_cancel", _on_agent_run_cancel)
535
+ register_callback("post_tool_call", _on_post_tool_call)
@@ -0,0 +1,112 @@
1
+ """Repaint the sub-agent panel after a pause -> resume transition.
2
+
3
+ During a sub-agent swarm the main agent is blocked inside the
4
+ invoke_agent tool await, so no stream events arrive to trigger the
5
+ normal event-driven ``_push_panel`` repaint. If the PauseController
6
+ paused and then resumed in that window (renderer buffering may have
7
+ scrolled output over the bar), this module gives subagent_panel its own
8
+ tiny wake-up path: force-push the panel lines to the bottom bar on every
9
+ resume, plus on ``ResumeAgentCommand`` from the message bus.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import threading
15
+ from typing import Callable, Protocol
16
+
17
+
18
+ class _State(Protocol):
19
+ def has_active(self) -> bool: ...
20
+
21
+
22
+ _runtime_enabled: Callable[[], bool] | None = None
23
+ _state: _State | None = None
24
+ _repaint: Callable[..., None] | None = None
25
+ _install_lock = threading.Lock()
26
+ _installed = False
27
+
28
+
29
+ def install(
30
+ runtime_enabled: Callable[[], bool],
31
+ state: _State,
32
+ repaint: Callable[..., None],
33
+ ) -> None:
34
+ """Install resume/repaint hooks. Safe to call repeatedly.
35
+
36
+ Args:
37
+ runtime_enabled: The plugin's runtime on/off switch.
38
+ state: The sub-agent registry (for the active-swarm guard).
39
+ repaint: Callable pushing the panel to the bottom bar; invoked
40
+ with ``force=True``.
41
+ """
42
+ global _installed, _runtime_enabled, _state, _repaint
43
+ with _install_lock:
44
+ _runtime_enabled = runtime_enabled
45
+ _state = state
46
+ _repaint = repaint
47
+ if _installed:
48
+ return
49
+ _installed = True
50
+ _install_resume_listener()
51
+ _install_bus_resume_hook()
52
+
53
+
54
+ def _active_swarm() -> bool:
55
+ try:
56
+ if _runtime_enabled is None or _state is None:
57
+ return False
58
+ if not _runtime_enabled():
59
+ return False
60
+ if not _state.has_active():
61
+ return False
62
+ from code_puppy.messaging.pause_controller import get_pause_controller
63
+ from code_puppy.tools.command_runner import is_awaiting_user_input
64
+
65
+ return not get_pause_controller().is_paused() and not is_awaiting_user_input()
66
+ except Exception:
67
+ return False
68
+
69
+
70
+ def _request_repaint(_reason: str = "resume") -> None:
71
+ """Force-push the panel now; no retries needed — set_panel_lines is a
72
+ direct synchronous paint on the reserved rows, not a Live rebuild."""
73
+ if not _active_swarm():
74
+ return
75
+ if _repaint is None:
76
+ return
77
+ try:
78
+ _repaint(force=True)
79
+ except Exception:
80
+ pass
81
+
82
+
83
+ def _install_resume_listener() -> None:
84
+ try:
85
+ from code_puppy.messaging.pause_controller import get_pause_controller
86
+
87
+ get_pause_controller().add_resume_listener(_request_repaint)
88
+ except Exception:
89
+ pass
90
+
91
+
92
+ def _install_bus_resume_hook() -> None:
93
+ try:
94
+ from code_puppy.messaging.bus import MessageBus
95
+ except Exception:
96
+ return
97
+
98
+ current = MessageBus.provide_response
99
+ if getattr(current, "_subagent_panel_resume_repaint", False):
100
+ return
101
+
102
+ def _wrapped(self, command):
103
+ result = current(self, command)
104
+ try:
105
+ if type(command).__name__ == "ResumeAgentCommand":
106
+ _request_repaint("resume-command")
107
+ except Exception:
108
+ pass
109
+ return result
110
+
111
+ _wrapped._subagent_panel_resume_repaint = True
112
+ MessageBus.provide_response = _wrapped