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,445 @@
|
|
|
1
|
+
"""``/fork`` — spawn a sub-agent in the background and keep working.
|
|
2
|
+
|
|
3
|
+
Unlike the ``invoke_agent`` tool (where the *agent* delegates and awaits),
|
|
4
|
+
``/fork`` lets the *user* launch a sub-agent as a fire-and-forget asyncio
|
|
5
|
+
task. It works at the idle prompt AND mid-run: both command dispatch paths
|
|
6
|
+
execute on the main thread with the event loop running, so the fork runs
|
|
7
|
+
concurrently with whatever the foreground agent is doing.
|
|
8
|
+
|
|
9
|
+
A fork is a true fork: the current conversation history is snapshotted at
|
|
10
|
+
the moment ``/fork`` is typed, persisted as a sub-agent session, and the
|
|
11
|
+
child resumes from that copy (mirroring ACP's ``fork_session``). The
|
|
12
|
+
snapshot is point-in-time — later foreground turns never leak into the
|
|
13
|
+
fork, and the fork's turns never mutate the main history. When there is no
|
|
14
|
+
history yet (or the snapshot fails), the fork starts with a fresh context.
|
|
15
|
+
|
|
16
|
+
Commands:
|
|
17
|
+
/fork <prompt> fork the current agent with the prompt
|
|
18
|
+
/fork @<agent> <prompt> fork a named agent
|
|
19
|
+
/fork @<agent> @<model> <prompt> fork with a specific model
|
|
20
|
+
/fork cancel <id> cancel a running fork
|
|
21
|
+
/forks show status of all forks this session
|
|
22
|
+
|
|
23
|
+
Results are explicitly bridged onto the interactive transcript queue when
|
|
24
|
+
the fork completes. Forks suppress ``_invoke_agent_impl``'s generic structured
|
|
25
|
+
response and emit one fork-specific, theme-aware banner plus Markdown body;
|
|
26
|
+
detached tasks cannot rely on the invocation renderer still owning the active
|
|
27
|
+
transcript. The completion banner then adds duration and resumable session id.
|
|
28
|
+
|
|
29
|
+
Note: Ctrl+C's cancel sweep clears ``_active_subagent_tasks``, which
|
|
30
|
+
includes forked runs — cancelling the agent takes forks down with it.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
import asyncio
|
|
36
|
+
import itertools
|
|
37
|
+
import time
|
|
38
|
+
from dataclasses import dataclass, field
|
|
39
|
+
from typing import Dict, List, Optional, Tuple
|
|
40
|
+
|
|
41
|
+
from code_puppy.callbacks import register_callback
|
|
42
|
+
|
|
43
|
+
_FORK = "fork"
|
|
44
|
+
_FORKS = "forks"
|
|
45
|
+
_PROMPT_PREVIEW_LEN = 60
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class _ForkRecord:
|
|
50
|
+
"""Bookkeeping for one background sub-agent run."""
|
|
51
|
+
|
|
52
|
+
fork_id: int
|
|
53
|
+
agent_name: str
|
|
54
|
+
prompt: str
|
|
55
|
+
task: asyncio.Task
|
|
56
|
+
started_at: float = field(default_factory=time.monotonic)
|
|
57
|
+
status: str = "running" # running | done | failed | cancelled
|
|
58
|
+
elapsed: Optional[float] = None
|
|
59
|
+
session_id: Optional[str] = None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
_forks: Dict[int, _ForkRecord] = {}
|
|
63
|
+
_fork_ids = itertools.count(1)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
# Messaging shims (lazy imports keep plugin load cheap and cycle-free)
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
def _emit_info(content) -> None:
|
|
70
|
+
from code_puppy.messaging import emit_info
|
|
71
|
+
|
|
72
|
+
emit_info(content)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _emit_success(content) -> None:
|
|
76
|
+
from code_puppy.messaging import emit_success
|
|
77
|
+
|
|
78
|
+
emit_success(content)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _emit_agent_response(content) -> None:
|
|
82
|
+
from code_puppy.messaging import emit_agent_response
|
|
83
|
+
|
|
84
|
+
emit_agent_response(content)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _emit_warning(content) -> None:
|
|
88
|
+
from code_puppy.messaging import emit_warning
|
|
89
|
+
|
|
90
|
+
emit_warning(content)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _emit_error(content) -> None:
|
|
94
|
+
from code_puppy.messaging import emit_error
|
|
95
|
+
|
|
96
|
+
emit_error(content)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _started_message(fork_id: int, agent_name: str):
|
|
100
|
+
"""Build a Rich, theme-aware acknowledgement for a newly started fork."""
|
|
101
|
+
from rich.text import Text
|
|
102
|
+
|
|
103
|
+
from code_puppy.messaging.messages import MessageLevel
|
|
104
|
+
from code_puppy.messaging.rich_renderer import DEFAULT_STYLES
|
|
105
|
+
|
|
106
|
+
message = Text(f"fork #{fork_id}: ", style=DEFAULT_STYLES[MessageLevel.INFO])
|
|
107
|
+
message.append(agent_name, style="bold")
|
|
108
|
+
message.append(" started in the background — results land when it finishes (")
|
|
109
|
+
message.append("/forks", style=DEFAULT_STYLES[MessageLevel.DEBUG])
|
|
110
|
+
message.append(" for status)")
|
|
111
|
+
return message
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _response_message(fork_id: int, agent_name: str, response: str):
|
|
115
|
+
"""Build the distinct, theme-aware banner and Markdown body for a fork."""
|
|
116
|
+
from rich.console import Group
|
|
117
|
+
from rich.markdown import Markdown
|
|
118
|
+
from rich.text import Text
|
|
119
|
+
|
|
120
|
+
from code_puppy.config import get_banner_color
|
|
121
|
+
from code_puppy.messaging.messages import MessageLevel
|
|
122
|
+
from code_puppy.messaging.rich_renderer import DEFAULT_STYLES
|
|
123
|
+
|
|
124
|
+
header = Text(
|
|
125
|
+
f" FORK #{fork_id} RESPONSE ",
|
|
126
|
+
style=f"bold white on {get_banner_color('subagent_response')}",
|
|
127
|
+
)
|
|
128
|
+
header.append(" ")
|
|
129
|
+
header.append(agent_name, style=f"bold {DEFAULT_STYLES[MessageLevel.INFO]}")
|
|
130
|
+
return Group(Text(""), header, Markdown(response))
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# ---------------------------------------------------------------------------
|
|
134
|
+
# Parsing
|
|
135
|
+
# ---------------------------------------------------------------------------
|
|
136
|
+
def _parse_fork_args(
|
|
137
|
+
args: str,
|
|
138
|
+
) -> Tuple[Optional[str], Optional[str], str]:
|
|
139
|
+
"""Split ``@agent [@model] prompt...`` into (agent_name, model_name, prompt).
|
|
140
|
+
|
|
141
|
+
Returns (None, None, args) when no ``@agent`` prefix is present, meaning
|
|
142
|
+
"fork the current agent".
|
|
143
|
+
"""
|
|
144
|
+
if args.startswith("@"):
|
|
145
|
+
head, _, rest = args.partition(" ")
|
|
146
|
+
agent_name = head[1:]
|
|
147
|
+
rest = rest.strip()
|
|
148
|
+
# Check for a second @ as model prefix
|
|
149
|
+
if rest.startswith("@"):
|
|
150
|
+
model_head, _, prompt = rest.partition(" ")
|
|
151
|
+
return agent_name, model_head[1:], prompt.strip()
|
|
152
|
+
return agent_name, None, rest
|
|
153
|
+
return None, None, args
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _resolve_agent_name(requested: Optional[str]) -> Optional[str]:
|
|
157
|
+
"""Validate the requested agent (or default to the current one).
|
|
158
|
+
|
|
159
|
+
Returns the resolved name, or ``None`` after emitting a warning.
|
|
160
|
+
"""
|
|
161
|
+
from code_puppy.agents.agent_manager import (
|
|
162
|
+
get_available_agents,
|
|
163
|
+
get_current_agent_name,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if requested is None:
|
|
167
|
+
return get_current_agent_name()
|
|
168
|
+
|
|
169
|
+
available = get_available_agents()
|
|
170
|
+
if requested in available:
|
|
171
|
+
return requested
|
|
172
|
+
|
|
173
|
+
_emit_warning(
|
|
174
|
+
f"Unknown agent '@{requested}'. Available: " + ", ".join(sorted(available)),
|
|
175
|
+
)
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# ---------------------------------------------------------------------------
|
|
180
|
+
# Fork lifecycle
|
|
181
|
+
# ---------------------------------------------------------------------------
|
|
182
|
+
def _seed_fork_session(agent_name: str, prompt: str) -> Optional[str]:
|
|
183
|
+
"""Snapshot the current conversation so the fork continues from it.
|
|
184
|
+
|
|
185
|
+
Copies the main agent's live message history (the point-in-time state at
|
|
186
|
+
the moment ``/fork`` is typed) into a persisted sub-agent session and
|
|
187
|
+
returns its id. ``_invoke_agent_impl`` then loads that non-empty history
|
|
188
|
+
and treats the fork as a *continuing* session — a real fork, not a blank
|
|
189
|
+
slate.
|
|
190
|
+
|
|
191
|
+
Returns ``None`` when there is nothing to copy (fresh conversation) or
|
|
192
|
+
the snapshot fails; the fork then starts with a fresh context.
|
|
193
|
+
"""
|
|
194
|
+
try:
|
|
195
|
+
from code_puppy.agents.agent_manager import get_current_agent
|
|
196
|
+
from code_puppy.tools.agent_tools import (
|
|
197
|
+
_generate_session_hash_suffix,
|
|
198
|
+
_sanitize_for_session_id,
|
|
199
|
+
_save_session_history,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
history = list(get_current_agent().get_message_history() or [])
|
|
203
|
+
if not history:
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
safe_agent = _sanitize_for_session_id(agent_name) or "agent"
|
|
207
|
+
session_id = f"{safe_agent}-fork-{_generate_session_hash_suffix()}"
|
|
208
|
+
_save_session_history(
|
|
209
|
+
session_id=session_id,
|
|
210
|
+
message_history=history,
|
|
211
|
+
agent_name=agent_name,
|
|
212
|
+
initial_prompt=prompt,
|
|
213
|
+
)
|
|
214
|
+
return session_id
|
|
215
|
+
except Exception: # pragma: no cover - snapshot must never block the fork
|
|
216
|
+
_emit_warning(
|
|
217
|
+
"/fork couldn't snapshot the current conversation — "
|
|
218
|
+
"forking with a fresh context."
|
|
219
|
+
)
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
async def _run_fork(
|
|
224
|
+
agent_name: str,
|
|
225
|
+
prompt: str,
|
|
226
|
+
model_name: str | None = None,
|
|
227
|
+
session_id: str | None = None,
|
|
228
|
+
):
|
|
229
|
+
"""Run the sub-agent and publish its tool-equivalent completion signal."""
|
|
230
|
+
from code_puppy.callbacks import on_post_tool_call
|
|
231
|
+
from code_puppy.tools.subagent_invocation import _invoke_agent_impl
|
|
232
|
+
|
|
233
|
+
started_at = time.monotonic()
|
|
234
|
+
result = None
|
|
235
|
+
try:
|
|
236
|
+
# ``context`` is unused by the implementation; forks have no RunContext.
|
|
237
|
+
result = await _invoke_agent_impl(
|
|
238
|
+
context=None,
|
|
239
|
+
agent_name=agent_name,
|
|
240
|
+
prompt=prompt,
|
|
241
|
+
session_id=session_id,
|
|
242
|
+
model_name=model_name,
|
|
243
|
+
emit_response_message=False,
|
|
244
|
+
)
|
|
245
|
+
return result
|
|
246
|
+
finally:
|
|
247
|
+
# /fork calls the shared implementation directly (not via pydantic-ai's tool
|
|
248
|
+
# wrapper); still publish the completion event so the sub-agent panel can
|
|
249
|
+
# retire the row instead of showing "writing response" forever.
|
|
250
|
+
if result is not None:
|
|
251
|
+
await on_post_tool_call(
|
|
252
|
+
"invoke_agent",
|
|
253
|
+
{"agent_name": agent_name, "prompt": prompt},
|
|
254
|
+
result,
|
|
255
|
+
(time.monotonic() - started_at) * 1000,
|
|
256
|
+
{"detached_fork": True},
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _on_fork_done(fork_id: int, task: asyncio.Task) -> None:
|
|
261
|
+
"""Done-callback (runs in-loop): record outcome + emit a banner."""
|
|
262
|
+
record = _forks.get(fork_id)
|
|
263
|
+
if record is None: # pragma: no cover - defensive
|
|
264
|
+
return
|
|
265
|
+
record.elapsed = time.monotonic() - record.started_at
|
|
266
|
+
tag = f"fork #{fork_id} ({record.agent_name})"
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
if task.cancelled():
|
|
270
|
+
record.status = "cancelled"
|
|
271
|
+
_emit_warning(f"{tag} cancelled after {record.elapsed:.1f}s")
|
|
272
|
+
return
|
|
273
|
+
exc = task.exception()
|
|
274
|
+
if exc is not None:
|
|
275
|
+
record.status = "failed"
|
|
276
|
+
_emit_error(f"{tag} crashed after {record.elapsed:.1f}s: {exc}")
|
|
277
|
+
return
|
|
278
|
+
result = task.result()
|
|
279
|
+
record.session_id = getattr(result, "session_id", None)
|
|
280
|
+
if getattr(result, "error", None):
|
|
281
|
+
record.status = "failed"
|
|
282
|
+
first_line = str(result.error).strip().splitlines()[0]
|
|
283
|
+
_emit_error(f"{tag} failed after {record.elapsed:.1f}s: {first_line}")
|
|
284
|
+
return
|
|
285
|
+
record.status = "done"
|
|
286
|
+
response = getattr(result, "response", None)
|
|
287
|
+
if response:
|
|
288
|
+
_emit_agent_response(
|
|
289
|
+
_response_message(fork_id, record.agent_name, response)
|
|
290
|
+
)
|
|
291
|
+
_emit_success(
|
|
292
|
+
f"{tag} finished in {record.elapsed:.1f}s"
|
|
293
|
+
+ (f" — session '{record.session_id}'" if record.session_id else "")
|
|
294
|
+
)
|
|
295
|
+
except Exception: # pragma: no cover - banner must never break the loop
|
|
296
|
+
record.status = "failed"
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _start_fork(agent_name: str, prompt: str, model_name: str | None = None) -> None:
|
|
300
|
+
try:
|
|
301
|
+
loop = asyncio.get_running_loop()
|
|
302
|
+
except RuntimeError:
|
|
303
|
+
_emit_warning("/fork needs a running event loop — can't fork here.")
|
|
304
|
+
return
|
|
305
|
+
|
|
306
|
+
# Snapshot BEFORE scheduling the task: the copy must reflect the exact
|
|
307
|
+
# moment /fork was typed, not whenever the event loop gets around to it.
|
|
308
|
+
session_id = _seed_fork_session(agent_name, prompt)
|
|
309
|
+
|
|
310
|
+
fork_id = next(_fork_ids)
|
|
311
|
+
task = loop.create_task(
|
|
312
|
+
_run_fork(agent_name, prompt, model_name, session_id), name=f"fork-{fork_id}"
|
|
313
|
+
)
|
|
314
|
+
_forks[fork_id] = _ForkRecord(
|
|
315
|
+
fork_id=fork_id, agent_name=agent_name, prompt=prompt, task=task
|
|
316
|
+
)
|
|
317
|
+
task.add_done_callback(lambda t, fid=fork_id: _on_fork_done(fid, t))
|
|
318
|
+
_emit_info(_started_message(fork_id, agent_name))
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _cancel_fork(raw_id: str) -> None:
|
|
322
|
+
try:
|
|
323
|
+
fork_id = int(raw_id)
|
|
324
|
+
except ValueError:
|
|
325
|
+
_emit_warning(f"Usage: /fork cancel <id> — '{raw_id}' isn't a fork id.")
|
|
326
|
+
return
|
|
327
|
+
record = _forks.get(fork_id)
|
|
328
|
+
if record is None:
|
|
329
|
+
_emit_warning(f"No fork #{fork_id}. Try /forks.")
|
|
330
|
+
return
|
|
331
|
+
if record.task.done():
|
|
332
|
+
_emit_info(f"fork #{fork_id} already {record.status}.")
|
|
333
|
+
return
|
|
334
|
+
record.task.cancel()
|
|
335
|
+
_emit_info(f"cancelling fork #{fork_id} ({record.agent_name})...")
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
# ---------------------------------------------------------------------------
|
|
339
|
+
# Command handlers
|
|
340
|
+
# ---------------------------------------------------------------------------
|
|
341
|
+
def _handle_fork(command: str) -> bool:
|
|
342
|
+
args = command.split(" ", 1)[1].strip() if " " in command else ""
|
|
343
|
+
if not args:
|
|
344
|
+
_emit_info(
|
|
345
|
+
"Usage: /fork [@agent] [@model] <prompt> — run a sub-agent in the background.\n"
|
|
346
|
+
" /fork cancel <id> — stop a running fork.\n"
|
|
347
|
+
" /forks — list forks."
|
|
348
|
+
)
|
|
349
|
+
return True
|
|
350
|
+
|
|
351
|
+
head, _, rest = args.partition(" ")
|
|
352
|
+
if head == "cancel":
|
|
353
|
+
_cancel_fork(rest.strip())
|
|
354
|
+
return True
|
|
355
|
+
|
|
356
|
+
requested_agent, requested_model, prompt = _parse_fork_args(args)
|
|
357
|
+
if not prompt:
|
|
358
|
+
_emit_warning("Fork what, exactly? Usage: /fork [@agent] [@model] <prompt>")
|
|
359
|
+
return True
|
|
360
|
+
|
|
361
|
+
agent_name = _resolve_agent_name(requested_agent)
|
|
362
|
+
if agent_name is None:
|
|
363
|
+
return True
|
|
364
|
+
|
|
365
|
+
_start_fork(agent_name, prompt, requested_model)
|
|
366
|
+
return True
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _handle_forks(command: str) -> bool:
|
|
370
|
+
if not _forks:
|
|
371
|
+
_emit_info("No forks yet — /fork [@agent] [@model] <prompt> to start one.")
|
|
372
|
+
return True
|
|
373
|
+
|
|
374
|
+
from rich.table import Table
|
|
375
|
+
|
|
376
|
+
table = Table(title="Forks", show_lines=False)
|
|
377
|
+
table.add_column("id", justify="right")
|
|
378
|
+
table.add_column("agent", style="cyan")
|
|
379
|
+
table.add_column("status")
|
|
380
|
+
table.add_column("time", justify="right")
|
|
381
|
+
table.add_column("prompt", overflow="ellipsis", max_width=_PROMPT_PREVIEW_LEN)
|
|
382
|
+
|
|
383
|
+
style_by_status = {
|
|
384
|
+
"running": "yellow",
|
|
385
|
+
"done": "green",
|
|
386
|
+
"failed": "red",
|
|
387
|
+
"cancelled": "dim",
|
|
388
|
+
}
|
|
389
|
+
for record in _forks.values():
|
|
390
|
+
elapsed = (
|
|
391
|
+
record.elapsed
|
|
392
|
+
if record.elapsed is not None
|
|
393
|
+
else time.monotonic() - record.started_at
|
|
394
|
+
)
|
|
395
|
+
status_style = style_by_status.get(record.status, "")
|
|
396
|
+
table.add_row(
|
|
397
|
+
str(record.fork_id),
|
|
398
|
+
record.agent_name,
|
|
399
|
+
f"[{status_style}]{record.status}[/{status_style}]"
|
|
400
|
+
if status_style
|
|
401
|
+
else record.status,
|
|
402
|
+
f"{elapsed:.1f}s",
|
|
403
|
+
record.prompt.replace("\n", " "),
|
|
404
|
+
)
|
|
405
|
+
_emit_info(table)
|
|
406
|
+
return True
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
# ---------------------------------------------------------------------------
|
|
410
|
+
# Registration
|
|
411
|
+
# ---------------------------------------------------------------------------
|
|
412
|
+
def _handle_custom_command(command: str, name: str) -> Optional[bool]:
|
|
413
|
+
if name == _FORK:
|
|
414
|
+
return _handle_fork(command)
|
|
415
|
+
if name == _FORKS:
|
|
416
|
+
return _handle_forks(command)
|
|
417
|
+
return None
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _custom_help() -> List[Tuple[str, str]]:
|
|
421
|
+
return [
|
|
422
|
+
(
|
|
423
|
+
_FORK,
|
|
424
|
+
"Spawn a sub-agent in the background: /fork [@agent] [@model] <prompt>",
|
|
425
|
+
),
|
|
426
|
+
(_FORKS, "Show status of background forks"),
|
|
427
|
+
]
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
register_callback("custom_command", _handle_custom_command)
|
|
431
|
+
register_callback("custom_command_help", _custom_help)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
__all__ = [
|
|
435
|
+
"_cancel_fork",
|
|
436
|
+
"_seed_fork_session",
|
|
437
|
+
"_custom_help",
|
|
438
|
+
"_handle_custom_command",
|
|
439
|
+
"_handle_fork",
|
|
440
|
+
"_handle_forks",
|
|
441
|
+
"_on_fork_done",
|
|
442
|
+
"_parse_fork_args",
|
|
443
|
+
"_resolve_agent_name",
|
|
444
|
+
"_start_fork",
|
|
445
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Frontend emitter plugin for Code Puppy.
|
|
2
|
+
|
|
3
|
+
This plugin provides event emission capabilities for frontend integration,
|
|
4
|
+
allowing WebSocket handlers to subscribe to real-time events from the
|
|
5
|
+
agent system including tool calls, streaming events, and agent invocations.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from code_puppy_core_plugins.frontend_emitter.emitter import (
|
|
9
|
+
emit_event,
|
|
10
|
+
subscribe,
|
|
11
|
+
unsubscribe,
|
|
12
|
+
get_recent_events,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Subscribe to events
|
|
16
|
+
queue = subscribe()
|
|
17
|
+
|
|
18
|
+
# Process events in your WebSocket handler
|
|
19
|
+
while True:
|
|
20
|
+
event = await queue.get()
|
|
21
|
+
await websocket.send_json(event)
|
|
22
|
+
|
|
23
|
+
# Clean up
|
|
24
|
+
unsubscribe(queue)
|
|
25
|
+
"""
|