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.
- code_puppy_core_plugins/__init__.py +5 -0
- code_puppy_core_plugins/acp/README.md +266 -0
- code_puppy_core_plugins/acp/__init__.py +8 -0
- code_puppy_core_plugins/acp/agent.py +494 -0
- code_puppy_core_plugins/acp/bridge.py +309 -0
- code_puppy_core_plugins/acp/capabilities.py +73 -0
- code_puppy_core_plugins/acp/commands.py +116 -0
- code_puppy_core_plugins/acp/content.py +115 -0
- code_puppy_core_plugins/acp/io_delegation.py +231 -0
- code_puppy_core_plugins/acp/mcp_config.py +66 -0
- code_puppy_core_plugins/acp/permissions.py +159 -0
- code_puppy_core_plugins/acp/persistence.py +197 -0
- code_puppy_core_plugins/acp/register_callbacks.py +110 -0
- code_puppy_core_plugins/acp/replay.py +114 -0
- code_puppy_core_plugins/acp/session.py +329 -0
- code_puppy_core_plugins/acp/session_config.py +179 -0
- code_puppy_core_plugins/acp/state.py +135 -0
- code_puppy_core_plugins/agent_skills/__init__.py +22 -0
- code_puppy_core_plugins/agent_skills/config.py +208 -0
- code_puppy_core_plugins/agent_skills/discovery.py +337 -0
- code_puppy_core_plugins/agent_skills/downloader.py +392 -0
- code_puppy_core_plugins/agent_skills/enabled_skills.py +82 -0
- code_puppy_core_plugins/agent_skills/installer.py +22 -0
- code_puppy_core_plugins/agent_skills/metadata.py +219 -0
- code_puppy_core_plugins/agent_skills/prompt_builder.py +49 -0
- code_puppy_core_plugins/agent_skills/provider.py +52 -0
- code_puppy_core_plugins/agent_skills/register_callbacks.py +309 -0
- code_puppy_core_plugins/agent_skills/remote_catalog.py +322 -0
- code_puppy_core_plugins/agent_skills/skill_catalog.py +257 -0
- code_puppy_core_plugins/agent_skills/skill_commands.py +102 -0
- code_puppy_core_plugins/agent_skills/skills_install_menu.py +698 -0
- code_puppy_core_plugins/agent_skills/skills_menu.py +794 -0
- code_puppy_core_plugins/aws_bedrock/__init__.py +14 -0
- code_puppy_core_plugins/aws_bedrock/config.py +101 -0
- code_puppy_core_plugins/aws_bedrock/register_callbacks.py +243 -0
- code_puppy_core_plugins/aws_bedrock/utils.py +186 -0
- code_puppy_core_plugins/azure_foundry/README.md +238 -0
- code_puppy_core_plugins/azure_foundry/__init__.py +15 -0
- code_puppy_core_plugins/azure_foundry/config.py +126 -0
- code_puppy_core_plugins/azure_foundry/discovery.py +189 -0
- code_puppy_core_plugins/azure_foundry/register_callbacks.py +506 -0
- code_puppy_core_plugins/azure_foundry/token.py +182 -0
- code_puppy_core_plugins/azure_foundry/utils.py +375 -0
- code_puppy_core_plugins/btw/__init__.py +1 -0
- code_puppy_core_plugins/btw/inline_view.py +135 -0
- code_puppy_core_plugins/btw/register_callbacks.py +106 -0
- code_puppy_core_plugins/btw/side_query.py +91 -0
- code_puppy_core_plugins/chatgpt_oauth/IMAGEGEN_SKILL.md +83 -0
- code_puppy_core_plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy_core_plugins/chatgpt_oauth/config.py +52 -0
- code_puppy_core_plugins/chatgpt_oauth/image_generation.py +253 -0
- code_puppy_core_plugins/chatgpt_oauth/image_tool.py +70 -0
- code_puppy_core_plugins/chatgpt_oauth/oauth_flow.py +461 -0
- code_puppy_core_plugins/chatgpt_oauth/register_callbacks.py +288 -0
- code_puppy_core_plugins/chatgpt_oauth/test_plugin.py +663 -0
- code_puppy_core_plugins/chatgpt_oauth/usage.py +113 -0
- code_puppy_core_plugins/chatgpt_oauth/utils.py +534 -0
- code_puppy_core_plugins/claude_code_hooks/__init__.py +1 -0
- code_puppy_core_plugins/claude_code_hooks/config.py +137 -0
- code_puppy_core_plugins/claude_code_hooks/register_callbacks.py +383 -0
- code_puppy_core_plugins/claude_code_oauth/README.md +167 -0
- code_puppy_core_plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy_core_plugins/claude_code_oauth/__init__.py +25 -0
- code_puppy_core_plugins/claude_code_oauth/config.py +59 -0
- code_puppy_core_plugins/claude_code_oauth/fast_mode.py +122 -0
- code_puppy_core_plugins/claude_code_oauth/prompt_handler.py +64 -0
- code_puppy_core_plugins/claude_code_oauth/register_callbacks.py +719 -0
- code_puppy_core_plugins/claude_code_oauth/test_plugin.py +285 -0
- code_puppy_core_plugins/claude_code_oauth/token_refresh_heartbeat.py +240 -0
- code_puppy_core_plugins/claude_code_oauth/utils.py +663 -0
- code_puppy_core_plugins/code_puppy_agent/AGENTS_AND_TOOLS.md +155 -0
- code_puppy_core_plugins/code_puppy_agent/MODELS_AND_MCP.md +104 -0
- code_puppy_core_plugins/code_puppy_agent/PLUGINS_AND_CALLBACKS.md +147 -0
- code_puppy_core_plugins/code_puppy_agent/SESSIONS_AND_HISTORY.md +39 -0
- code_puppy_core_plugins/code_puppy_agent/SKILL.md +180 -0
- code_puppy_core_plugins/code_puppy_agent/SKILLS_SYSTEM.md +136 -0
- code_puppy_core_plugins/code_puppy_agent/SYSTEM_PROMPT_CONFIG_AND_I18N.md +160 -0
- code_puppy_core_plugins/code_puppy_agent/__init__.py +5 -0
- code_puppy_core_plugins/code_puppy_agent/register_callbacks.py +46 -0
- code_puppy_core_plugins/computer_use/NativeCapture.swift +123 -0
- code_puppy_core_plugins/computer_use/README.md +109 -0
- code_puppy_core_plugins/computer_use/__init__.py +1 -0
- code_puppy_core_plugins/computer_use/accessibility.py +163 -0
- code_puppy_core_plugins/computer_use/activation.py +75 -0
- code_puppy_core_plugins/computer_use/backend.py +584 -0
- code_puppy_core_plugins/computer_use/backend_types.py +5 -0
- code_puppy_core_plugins/computer_use/capture.py +110 -0
- code_puppy_core_plugins/computer_use/commands.py +62 -0
- code_puppy_core_plugins/computer_use/geometry.py +81 -0
- code_puppy_core_plugins/computer_use/inline_image.py +56 -0
- code_puppy_core_plugins/computer_use/keycodes.py +47 -0
- code_puppy_core_plugins/computer_use/policy.py +144 -0
- code_puppy_core_plugins/computer_use/register_callbacks.py +96 -0
- code_puppy_core_plugins/computer_use/safety.py +18 -0
- code_puppy_core_plugins/computer_use/settle.py +64 -0
- code_puppy_core_plugins/computer_use/state.py +86 -0
- code_puppy_core_plugins/computer_use/tools.py +469 -0
- code_puppy_core_plugins/context_indicator/__init__.py +14 -0
- code_puppy_core_plugins/context_indicator/register_callbacks.py +249 -0
- code_puppy_core_plugins/context_indicator/usage.py +40 -0
- code_puppy_core_plugins/copilot_auth/__init__.py +11 -0
- code_puppy_core_plugins/copilot_auth/config.py +93 -0
- code_puppy_core_plugins/copilot_auth/reasoning_client.py +409 -0
- code_puppy_core_plugins/copilot_auth/register_callbacks.py +460 -0
- code_puppy_core_plugins/copilot_auth/utils.py +587 -0
- code_puppy_core_plugins/customizable_commands/__init__.py +0 -0
- code_puppy_core_plugins/customizable_commands/register_callbacks.py +464 -0
- code_puppy_core_plugins/dbos_durable_exec/__init__.py +1 -0
- code_puppy_core_plugins/dbos_durable_exec/cancel.py +15 -0
- code_puppy_core_plugins/dbos_durable_exec/commands.py +31 -0
- code_puppy_core_plugins/dbos_durable_exec/config.py +31 -0
- code_puppy_core_plugins/dbos_durable_exec/lifecycle.py +83 -0
- code_puppy_core_plugins/dbos_durable_exec/register_callbacks.py +45 -0
- code_puppy_core_plugins/dbos_durable_exec/runtime.py +65 -0
- code_puppy_core_plugins/dbos_durable_exec/startup_lock.py +155 -0
- code_puppy_core_plugins/dbos_durable_exec/workflow_ids.py +16 -0
- code_puppy_core_plugins/dbos_durable_exec/wrapper.py +58 -0
- code_puppy_core_plugins/destructive_command_guard/AGENTS.md +79 -0
- code_puppy_core_plugins/destructive_command_guard/__init__.py +14 -0
- code_puppy_core_plugins/destructive_command_guard/detector.py +374 -0
- code_puppy_core_plugins/destructive_command_guard/register_callbacks.py +165 -0
- code_puppy_core_plugins/emoji_filter/__init__.py +12 -0
- code_puppy_core_plugins/emoji_filter/config.py +26 -0
- code_puppy_core_plugins/emoji_filter/register_callbacks.py +238 -0
- code_puppy_core_plugins/emoji_filter/stripper.py +45 -0
- code_puppy_core_plugins/empty_html_comment_filter/register_callbacks.py +116 -0
- code_puppy_core_plugins/example_custom_command/README.md +328 -0
- code_puppy_core_plugins/example_custom_command/register_callbacks.py +66 -0
- code_puppy_core_plugins/file_permission_handler/__init__.py +4 -0
- code_puppy_core_plugins/file_permission_handler/register_callbacks.py +575 -0
- code_puppy_core_plugins/flux_bootstrap/__init__.py +5 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/README.md +173 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/cheatsheet.md +86 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/pipeline.md +167 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/_docs/synopsis.md +392 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/about.md +51 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/address-feedback.md +129 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/ask.md +211 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/aug.md +181 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/auto-pilot.md +71 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/cheatsheet.md +16 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/commit.md +103 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/config.md +108 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/create-pr.md +195 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/exec.md +221 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/new.md +151 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/qa.md +208 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/rebase.md +180 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/review.md +304 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/split.md +120 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/squash-commits.md +179 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/status.md +46 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/commands/flux/tests.md +155 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_about.py +154 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_cheatsheet.py +248 -0
- code_puppy_core_plugins/flux_bootstrap/bundled/scripts/flux_status.py +345 -0
- code_puppy_core_plugins/flux_bootstrap/installer.py +256 -0
- code_puppy_core_plugins/flux_bootstrap/register_callbacks.py +71 -0
- code_puppy_core_plugins/force_push_guard/__init__.py +5 -0
- code_puppy_core_plugins/force_push_guard/detector.py +96 -0
- code_puppy_core_plugins/force_push_guard/register_callbacks.py +161 -0
- code_puppy_core_plugins/fork/__init__.py +7 -0
- code_puppy_core_plugins/fork/register_callbacks.py +445 -0
- code_puppy_core_plugins/frontend_emitter/__init__.py +25 -0
- code_puppy_core_plugins/frontend_emitter/emitter.py +249 -0
- code_puppy_core_plugins/frontend_emitter/register_callbacks.py +412 -0
- code_puppy_core_plugins/frontend_emitter/session_context.py +50 -0
- code_puppy_core_plugins/grok_oauth/__init__.py +6 -0
- code_puppy_core_plugins/grok_oauth/config.py +49 -0
- code_puppy_core_plugins/grok_oauth/oauth_flow.py +213 -0
- code_puppy_core_plugins/grok_oauth/register_callbacks.py +132 -0
- code_puppy_core_plugins/grok_oauth/utils.py +195 -0
- code_puppy_core_plugins/herdr/README.md +168 -0
- code_puppy_core_plugins/herdr/__init__.py +9 -0
- code_puppy_core_plugins/herdr/client.py +339 -0
- code_puppy_core_plugins/herdr/register_callbacks.py +140 -0
- code_puppy_core_plugins/herdr/reporter.py +212 -0
- code_puppy_core_plugins/herdr/smoke.py +143 -0
- code_puppy_core_plugins/herdr/sources.py +133 -0
- code_puppy_core_plugins/hook_creator/__init__.py +1 -0
- code_puppy_core_plugins/hook_creator/register_callbacks.py +33 -0
- code_puppy_core_plugins/hook_manager/__init__.py +1 -0
- code_puppy_core_plugins/hook_manager/config.py +290 -0
- code_puppy_core_plugins/hook_manager/hooks_menu.py +571 -0
- code_puppy_core_plugins/hook_manager/register_callbacks.py +227 -0
- code_puppy_core_plugins/meta_oauth/__init__.py +1 -0
- code_puppy_core_plugins/meta_oauth/config.py +55 -0
- code_puppy_core_plugins/meta_oauth/oauth_flow.py +109 -0
- code_puppy_core_plugins/meta_oauth/register_callbacks.py +154 -0
- code_puppy_core_plugins/meta_oauth/utils.py +252 -0
- code_puppy_core_plugins/namespace_skill_search/README.md +118 -0
- code_puppy_core_plugins/namespace_skill_search/__init__.py +15 -0
- code_puppy_core_plugins/namespace_skill_search/namespaces.py +125 -0
- code_puppy_core_plugins/namespace_skill_search/register_callbacks.py +99 -0
- code_puppy_core_plugins/namespace_skill_search/search_tool.py +158 -0
- code_puppy_core_plugins/no_tools/__init__.py +0 -0
- code_puppy_core_plugins/no_tools/register_callbacks.py +42 -0
- code_puppy_core_plugins/oauth_pasteback.py +110 -0
- code_puppy_core_plugins/oauth_puppy_html.py +194 -0
- code_puppy_core_plugins/obsidian_agent/README.md +52 -0
- code_puppy_core_plugins/obsidian_agent/__init__.py +8 -0
- code_puppy_core_plugins/obsidian_agent/agent_obsidian.py +147 -0
- code_puppy_core_plugins/obsidian_agent/register_callbacks.py +13 -0
- code_puppy_core_plugins/ollama/__init__.py +1 -0
- code_puppy_core_plugins/ollama/register_callbacks.py +127 -0
- code_puppy_core_plugins/ollama_setup/__init__.py +5 -0
- code_puppy_core_plugins/ollama_setup/completer.py +38 -0
- code_puppy_core_plugins/ollama_setup/register_callbacks.py +419 -0
- code_puppy_core_plugins/plugin_list/__init__.py +0 -0
- code_puppy_core_plugins/plugin_list/plugin_contributions.py +348 -0
- code_puppy_core_plugins/plugin_list/plugin_meta.py +67 -0
- code_puppy_core_plugins/plugin_list/plugin_text_utils.py +173 -0
- code_puppy_core_plugins/plugin_list/plugins_menu.py +455 -0
- code_puppy_core_plugins/plugin_list/plugins_menu_layout.py +194 -0
- code_puppy_core_plugins/plugin_list/plugins_menu_render.py +431 -0
- code_puppy_core_plugins/plugin_list/project_trust_flow.py +162 -0
- code_puppy_core_plugins/plugin_list/register_callbacks.py +281 -0
- code_puppy_core_plugins/pop_command/__init__.py +1 -0
- code_puppy_core_plugins/pop_command/register_callbacks.py +191 -0
- code_puppy_core_plugins/prompt_newline/__init__.py +13 -0
- code_puppy_core_plugins/prompt_newline/config.py +21 -0
- code_puppy_core_plugins/prompt_newline/register_callbacks.py +163 -0
- code_puppy_core_plugins/prune/__init__.py +1 -0
- code_puppy_core_plugins/prune/prune_menu.py +308 -0
- code_puppy_core_plugins/prune/prune_model.py +517 -0
- code_puppy_core_plugins/prune/prune_render.py +354 -0
- code_puppy_core_plugins/prune/register_callbacks.py +388 -0
- code_puppy_core_plugins/puppy_kennel/README.md +203 -0
- code_puppy_core_plugins/puppy_kennel/__init__.py +6 -0
- code_puppy_core_plugins/puppy_kennel/commands.py +206 -0
- code_puppy_core_plugins/puppy_kennel/config.py +36 -0
- code_puppy_core_plugins/puppy_kennel/kennel.py +368 -0
- code_puppy_core_plugins/puppy_kennel/packer.py +152 -0
- code_puppy_core_plugins/puppy_kennel/recorder.py +83 -0
- code_puppy_core_plugins/puppy_kennel/register_callbacks.py +109 -0
- code_puppy_core_plugins/puppy_kennel/retriever.py +28 -0
- code_puppy_core_plugins/puppy_kennel/schema.py +78 -0
- code_puppy_core_plugins/puppy_kennel/state.py +68 -0
- code_puppy_core_plugins/puppy_kennel/tools.py +449 -0
- code_puppy_core_plugins/puppy_kennel/wings.py +110 -0
- code_puppy_core_plugins/puppy_spinner/README.md +93 -0
- code_puppy_core_plugins/puppy_spinner/__init__.py +11 -0
- code_puppy_core_plugins/puppy_spinner/builtin_frames.py +172 -0
- code_puppy_core_plugins/puppy_spinner/commands.py +116 -0
- code_puppy_core_plugins/puppy_spinner/picker.py +379 -0
- code_puppy_core_plugins/puppy_spinner/register_callbacks.py +193 -0
- code_puppy_core_plugins/puppy_spinner/spinners.py +444 -0
- code_puppy_core_plugins/quick_resume/__init__.py +0 -0
- code_puppy_core_plugins/quick_resume/register_callbacks.py +77 -0
- code_puppy_core_plugins/review_pr/__init__.py +1 -0
- code_puppy_core_plugins/review_pr/register_callbacks.py +162 -0
- code_puppy_core_plugins/shell_safety/__init__.py +6 -0
- code_puppy_core_plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy_core_plugins/shell_safety/command_cache.py +156 -0
- code_puppy_core_plugins/shell_safety/register_callbacks.py +203 -0
- code_puppy_core_plugins/stack_dump/__init__.py +1 -0
- code_puppy_core_plugins/stack_dump/register_callbacks.py +79 -0
- code_puppy_core_plugins/statusline/__init__.py +14 -0
- code_puppy_core_plugins/statusline/config.py +68 -0
- code_puppy_core_plugins/statusline/payload.py +138 -0
- code_puppy_core_plugins/statusline/prompt_patch.py +76 -0
- code_puppy_core_plugins/statusline/register_callbacks.py +45 -0
- code_puppy_core_plugins/statusline/runner.py +122 -0
- code_puppy_core_plugins/statusline/statusline_command.py +197 -0
- code_puppy_core_plugins/steer_queue/__init__.py +10 -0
- code_puppy_core_plugins/steer_queue/queue_menu.py +444 -0
- code_puppy_core_plugins/steer_queue/register_callbacks.py +138 -0
- code_puppy_core_plugins/subagent_panel/README.md +102 -0
- code_puppy_core_plugins/subagent_panel/__init__.py +24 -0
- code_puppy_core_plugins/subagent_panel/coalesce_patch.py +242 -0
- code_puppy_core_plugins/subagent_panel/panel_render.py +145 -0
- code_puppy_core_plugins/subagent_panel/register_callbacks.py +535 -0
- code_puppy_core_plugins/subagent_panel/resume_repaint.py +112 -0
- code_puppy_core_plugins/subagent_panel/state.py +231 -0
- code_puppy_core_plugins/switch_agent_resume/__init__.py +5 -0
- code_puppy_core_plugins/switch_agent_resume/register_callbacks.py +314 -0
- code_puppy_core_plugins/theme/README.md +86 -0
- code_puppy_core_plugins/theme/__init__.py +14 -0
- code_puppy_core_plugins/theme/bundled_palettes.py +346 -0
- code_puppy_core_plugins/theme/content_styles.py +146 -0
- code_puppy_core_plugins/theme/osc_palette.py +178 -0
- code_puppy_core_plugins/theme/picker.py +358 -0
- code_puppy_core_plugins/theme/prompt_toolkit_theme.py +113 -0
- code_puppy_core_plugins/theme/register_callbacks.py +348 -0
- code_puppy_core_plugins/theme/rich_themes.py +202 -0
- code_puppy_core_plugins/theme/themes.py +720 -0
- code_puppy_core_plugins/timestamp_heartbeat/__init__.py +1 -0
- code_puppy_core_plugins/timestamp_heartbeat/register_callbacks.py +114 -0
- code_puppy_core_plugins/token_ratio_learner/__init__.py +5 -0
- code_puppy_core_plugins/token_ratio_learner/ratios.py +208 -0
- code_puppy_core_plugins/token_ratio_learner/register_callbacks.py +213 -0
- code_puppy_core_plugins/universal_constructor/__init__.py +13 -0
- code_puppy_core_plugins/universal_constructor/models.py +138 -0
- code_puppy_core_plugins/universal_constructor/provider.py +62 -0
- code_puppy_core_plugins/universal_constructor/register_callbacks.py +75 -0
- code_puppy_core_plugins/universal_constructor/registry.py +302 -0
- code_puppy_core_plugins/universal_constructor/sandbox.py +584 -0
- code_puppy_core_plugins/wide_completion_menu/__init__.py +6 -0
- code_puppy_core_plugins/wide_completion_menu/register_callbacks.py +165 -0
- code_puppy_core_plugins/wiggum/__init__.py +1 -0
- code_puppy_core_plugins/wiggum/goal_runs.py +91 -0
- code_puppy_core_plugins/wiggum/judge.py +299 -0
- code_puppy_core_plugins/wiggum/judge_config.py +249 -0
- code_puppy_core_plugins/wiggum/judges_menu.py +814 -0
- code_puppy_core_plugins/wiggum/register_callbacks.py +569 -0
- code_puppy_core_plugins/wiggum/state.py +89 -0
- code_puppy_core_plugins/yolo_cli/__init__.py +1 -0
- code_puppy_core_plugins/yolo_cli/register_callbacks.py +41 -0
- code_puppy_core_plugins-0.0.2.dist-info/METADATA +64 -0
- code_puppy_core_plugins-0.0.2.dist-info/RECORD +313 -0
- code_puppy_core_plugins-0.0.2.dist-info/WHEEL +4 -0
- code_puppy_core_plugins-0.0.2.dist-info/entry_points.txt +55 -0
- 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
|