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,494 @@
|
|
|
1
|
+
"""``CodePuppyAgent`` — Code Puppy as a native Agent Client Protocol agent.
|
|
2
|
+
|
|
3
|
+
This implements the official SDK's ``Agent`` interface, so Code Puppy runs as
|
|
4
|
+
an external agent in any ACP client (Zed, and other editors that speak ACP).
|
|
5
|
+
The SDK owns the wire: ``acp.run_agent`` binds stdio, frames JSON-RPC, parses
|
|
6
|
+
params into typed models, and hands us a ``Client`` connection (via
|
|
7
|
+
``on_connect``) for talking back to the client. We own the behaviour: mapping ACP sessions to Code Puppy agents,
|
|
8
|
+
running prompts, and translating events (through ``EventBridge``) and I/O
|
|
9
|
+
(through ``permissions`` + ``io_delegation``).
|
|
10
|
+
|
|
11
|
+
Only capability-gated methods we actually support are implemented; the SDK's
|
|
12
|
+
router resolves unimplemented ones to a clean "method not found", so we never
|
|
13
|
+
have to stub protocol surface we don't mean.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import logging
|
|
20
|
+
import uuid
|
|
21
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
22
|
+
from typing import Any, Dict, List, Optional
|
|
23
|
+
|
|
24
|
+
from acp import Agent
|
|
25
|
+
from acp.schema import (
|
|
26
|
+
AgentCapabilities,
|
|
27
|
+
AuthenticateResponse,
|
|
28
|
+
AvailableCommand,
|
|
29
|
+
AvailableCommandsUpdate,
|
|
30
|
+
ClientCapabilities,
|
|
31
|
+
CloseSessionResponse,
|
|
32
|
+
Implementation,
|
|
33
|
+
InitializeResponse,
|
|
34
|
+
ListSessionsResponse,
|
|
35
|
+
LoadSessionResponse,
|
|
36
|
+
NewSessionResponse,
|
|
37
|
+
PromptResponse,
|
|
38
|
+
SessionInfo,
|
|
39
|
+
SetSessionModeResponse,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
from code_puppy_core_plugins.acp import (
|
|
43
|
+
capabilities,
|
|
44
|
+
io_delegation,
|
|
45
|
+
mcp_config,
|
|
46
|
+
permissions,
|
|
47
|
+
persistence,
|
|
48
|
+
replay,
|
|
49
|
+
session_config,
|
|
50
|
+
state,
|
|
51
|
+
)
|
|
52
|
+
from code_puppy_core_plugins.acp.bridge import EventBridge
|
|
53
|
+
from code_puppy_core_plugins.acp.session import ACPSession
|
|
54
|
+
|
|
55
|
+
logger = logging.getLogger(__name__)
|
|
56
|
+
|
|
57
|
+
# ACP protocol version this plugin targets. Sourced from the SDK (not a literal) so a
|
|
58
|
+
# library upgrade can't silently advertise a stale version; falls back to v1 if absent.
|
|
59
|
+
try:
|
|
60
|
+
from acp import PROTOCOL_VERSION as PROTOCOL_VERSION
|
|
61
|
+
except ImportError: # pragma: no cover - defensive for older SDKs
|
|
62
|
+
PROTOCOL_VERSION = 1
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _code_puppy_version() -> str:
|
|
66
|
+
try:
|
|
67
|
+
return version("code-puppy")
|
|
68
|
+
except PackageNotFoundError:
|
|
69
|
+
return "0.0.0"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class CodePuppyAgent(Agent):
|
|
73
|
+
"""One ACP connection's worth of Code Puppy, spread across client threads."""
|
|
74
|
+
|
|
75
|
+
def __init__(self) -> None:
|
|
76
|
+
self._sessions: Dict[str, ACPSession] = {}
|
|
77
|
+
self._bridge = EventBridge()
|
|
78
|
+
self._client_caps: Optional[ClientCapabilities] = None
|
|
79
|
+
|
|
80
|
+
# ---- Connection lifecycle ---------------------------------------------
|
|
81
|
+
def on_connect(self, conn: Any) -> None:
|
|
82
|
+
"""Store the client handle + loop, wire events, install approvals.
|
|
83
|
+
|
|
84
|
+
Called synchronously by the SDK from inside ``run_agent`` — already on
|
|
85
|
+
the running event loop — so ``get_running_loop`` returns the loop our
|
|
86
|
+
permission and I/O bridges marshal onto.
|
|
87
|
+
"""
|
|
88
|
+
state.set_connection(conn, asyncio.get_running_loop())
|
|
89
|
+
self._bridge.register()
|
|
90
|
+
permissions.install()
|
|
91
|
+
|
|
92
|
+
def shutdown(self) -> None:
|
|
93
|
+
"""Unwire this agent's event hooks. Call once the connection ends.
|
|
94
|
+
|
|
95
|
+
``permissions`` and ``io_delegation`` are torn down by the plugin entry
|
|
96
|
+
point; this drops the ``EventBridge`` hooks it owns so a closed
|
|
97
|
+
connection leaves the global callback registry clean.
|
|
98
|
+
"""
|
|
99
|
+
self._bridge.unregister()
|
|
100
|
+
|
|
101
|
+
# ---- Handshake --------------------------------------------------------
|
|
102
|
+
async def initialize(
|
|
103
|
+
self,
|
|
104
|
+
protocol_version: int,
|
|
105
|
+
client_capabilities: Optional[ClientCapabilities] = None,
|
|
106
|
+
client_info: Optional[Implementation] = None,
|
|
107
|
+
**kwargs: Any,
|
|
108
|
+
) -> InitializeResponse:
|
|
109
|
+
"""Negotiate capabilities and install matching I/O delegation.
|
|
110
|
+
|
|
111
|
+
Once we know what the client can do, reroute Code Puppy's workspace
|
|
112
|
+
file I/O and shell to the client's ``fs/*`` / ``terminal/*`` methods
|
|
113
|
+
(only the edges
|
|
114
|
+
the client advertises). Everything else stays local.
|
|
115
|
+
"""
|
|
116
|
+
self._client_caps = client_capabilities
|
|
117
|
+
io_delegation.install(client_capabilities)
|
|
118
|
+
return InitializeResponse(
|
|
119
|
+
protocol_version=PROTOCOL_VERSION,
|
|
120
|
+
agent_capabilities=self._agent_capabilities(),
|
|
121
|
+
agent_info=Implementation(
|
|
122
|
+
name="code-puppy",
|
|
123
|
+
title="Code Puppy",
|
|
124
|
+
version=_code_puppy_version(),
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
async def authenticate(self, method_id: str, **kwargs: Any) -> AuthenticateResponse:
|
|
129
|
+
"""No auth flow — Code Puppy authenticates via its own model config."""
|
|
130
|
+
return AuthenticateResponse()
|
|
131
|
+
|
|
132
|
+
# ---- Session lifecycle ------------------------------------------------
|
|
133
|
+
async def new_session(
|
|
134
|
+
self,
|
|
135
|
+
cwd: str,
|
|
136
|
+
additional_directories: Optional[List[str]] = None,
|
|
137
|
+
mcp_servers: Optional[List[Any]] = None,
|
|
138
|
+
**kwargs: Any,
|
|
139
|
+
) -> NewSessionResponse:
|
|
140
|
+
"""Create a session bound to a fresh agent instance.
|
|
141
|
+
|
|
142
|
+
Uses ``load_agent`` (not the cached ``get_current_agent``) so each
|
|
143
|
+
client thread gets its own ``_message_history``. ``cwd`` +
|
|
144
|
+
``additional_directories`` anchor the session's tools; ``mcp_servers``
|
|
145
|
+
the client injects are attached to the agent.
|
|
146
|
+
"""
|
|
147
|
+
session_id = f"sess_{uuid.uuid4().hex[:16]}"
|
|
148
|
+
self._make_session(session_id, cwd, additional_directories, mcp_servers)
|
|
149
|
+
self._announce_commands_soon(session_id)
|
|
150
|
+
return NewSessionResponse(
|
|
151
|
+
session_id=session_id,
|
|
152
|
+
config_options=session_config.config_options() or None,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
async def load_session(
|
|
156
|
+
self,
|
|
157
|
+
cwd: str,
|
|
158
|
+
session_id: str,
|
|
159
|
+
additional_directories: Optional[List[str]] = None,
|
|
160
|
+
mcp_servers: Optional[List[Any]] = None,
|
|
161
|
+
**kwargs: Any,
|
|
162
|
+
) -> LoadSessionResponse:
|
|
163
|
+
"""Re-open a thread the client remembers, replaying persisted history.
|
|
164
|
+
|
|
165
|
+
If we have the session's history on disk (persisted after each turn),
|
|
166
|
+
it is rehydrated into the fresh agent so the model continues the real
|
|
167
|
+
conversation, AND replayed to the client as ``session/update``
|
|
168
|
+
notifications so the client rebuilds the thread UI (without this the
|
|
169
|
+
client shows -- and may discard -- an empty thread). Otherwise the
|
|
170
|
+
thread is re-created empty but functional.
|
|
171
|
+
"""
|
|
172
|
+
session = self._make_session(
|
|
173
|
+
session_id, cwd, additional_directories, mcp_servers, rehydrate=True
|
|
174
|
+
)
|
|
175
|
+
await replay.replay_history(session_id, session.agent.get_message_history())
|
|
176
|
+
self._announce_commands_soon(session_id)
|
|
177
|
+
return LoadSessionResponse(
|
|
178
|
+
config_options=session_config.config_options() or None,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
async def resume_session(
|
|
182
|
+
self,
|
|
183
|
+
cwd: str,
|
|
184
|
+
session_id: str,
|
|
185
|
+
additional_directories: Optional[List[str]] = None,
|
|
186
|
+
mcp_servers: Optional[List[Any]] = None,
|
|
187
|
+
**kwargs: Any,
|
|
188
|
+
) -> Any:
|
|
189
|
+
"""Resume a session across a restart, rehydrating + replaying history."""
|
|
190
|
+
from acp.schema import ResumeSessionResponse
|
|
191
|
+
|
|
192
|
+
session = self._make_session(
|
|
193
|
+
session_id, cwd, additional_directories, mcp_servers, rehydrate=True
|
|
194
|
+
)
|
|
195
|
+
await replay.replay_history(session_id, session.agent.get_message_history())
|
|
196
|
+
self._announce_commands_soon(session_id)
|
|
197
|
+
return ResumeSessionResponse(
|
|
198
|
+
config_options=session_config.config_options() or None,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
async def fork_session(
|
|
202
|
+
self,
|
|
203
|
+
cwd: str,
|
|
204
|
+
session_id: str,
|
|
205
|
+
additional_directories: Optional[List[str]] = None,
|
|
206
|
+
mcp_servers: Optional[List[Any]] = None,
|
|
207
|
+
**kwargs: Any,
|
|
208
|
+
) -> Any:
|
|
209
|
+
"""Branch a session: duplicate its history into a new session id.
|
|
210
|
+
|
|
211
|
+
The source's history is copied into a fresh agent so the fork continues
|
|
212
|
+
the conversation independently. The source may be a live in-memory
|
|
213
|
+
session OR one persisted by a prior process -- forking must survive a
|
|
214
|
+
restart just like ``load``/``resume`` do, so we fall back to the
|
|
215
|
+
pickled history when the source isn't live.
|
|
216
|
+
"""
|
|
217
|
+
from acp.schema import ForkSessionResponse
|
|
218
|
+
|
|
219
|
+
source = self._sessions.get(session_id)
|
|
220
|
+
if source is not None:
|
|
221
|
+
source_history = list(source.agent.get_message_history())
|
|
222
|
+
source_cwd = source.cwd
|
|
223
|
+
else:
|
|
224
|
+
source_history = persistence.load_history(session_id)
|
|
225
|
+
if source_history is None:
|
|
226
|
+
raise ValueError(f"unknown session to fork: {session_id}")
|
|
227
|
+
source_cwd = None
|
|
228
|
+
new_id = f"sess_{uuid.uuid4().hex[:16]}"
|
|
229
|
+
session = self._make_session(
|
|
230
|
+
new_id,
|
|
231
|
+
cwd or source_cwd or "",
|
|
232
|
+
additional_directories,
|
|
233
|
+
mcp_servers,
|
|
234
|
+
)
|
|
235
|
+
try:
|
|
236
|
+
session.agent.set_message_history(source_history)
|
|
237
|
+
except Exception: # noqa: BLE001
|
|
238
|
+
logger.debug("ACP: fork history copy failed", exc_info=True)
|
|
239
|
+
self._announce_commands_soon(new_id)
|
|
240
|
+
return ForkSessionResponse(
|
|
241
|
+
session_id=new_id,
|
|
242
|
+
config_options=session_config.config_options() or None,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
async def set_session_mode(
|
|
246
|
+
self, mode_id: str, session_id: str, **kwargs: Any
|
|
247
|
+
) -> SetSessionModeResponse:
|
|
248
|
+
"""No-op mode handler.
|
|
249
|
+
|
|
250
|
+
Code Puppy has no ACP *session modes* (e.g. plan vs default); model
|
|
251
|
+
selection is exposed as a ``model`` config option instead (that is what
|
|
252
|
+
clients bind their model picker to -- see ``session_config``). This
|
|
253
|
+
method exists only to satisfy the SDK router's ``session/set_mode``
|
|
254
|
+
route; any mode id is accepted as a no-op.
|
|
255
|
+
"""
|
|
256
|
+
return SetSessionModeResponse()
|
|
257
|
+
|
|
258
|
+
def _rebind_session_model(self, session_id: str) -> None:
|
|
259
|
+
"""Rebuild a live session's agent on the current model, keeping state.
|
|
260
|
+
|
|
261
|
+
Message history and any client-injected MCP servers are preserved, so
|
|
262
|
+
the switch is invisible to the conversation. Best-effort: a rebind
|
|
263
|
+
failure leaves the existing agent in place.
|
|
264
|
+
"""
|
|
265
|
+
session = self._sessions.get(session_id)
|
|
266
|
+
if session is None:
|
|
267
|
+
return
|
|
268
|
+
try:
|
|
269
|
+
history = list(session.agent.get_message_history())
|
|
270
|
+
session.agent = self._new_agent()
|
|
271
|
+
session.agent.set_message_history(history)
|
|
272
|
+
if session.mcp_specs:
|
|
273
|
+
mcp_config.attach(session.agent, session.mcp_specs)
|
|
274
|
+
except Exception: # noqa: BLE001
|
|
275
|
+
logger.debug("ACP: model rebind failed", exc_info=True)
|
|
276
|
+
|
|
277
|
+
async def set_config_option(
|
|
278
|
+
self, config_id: str, session_id: str, value: Any, **kwargs: Any
|
|
279
|
+
) -> Any:
|
|
280
|
+
"""Apply a config-option change and return the refreshed options.
|
|
281
|
+
|
|
282
|
+
A change to the ``model`` option rebinds the live session's agent to the
|
|
283
|
+
newly-selected model (history + client MCP servers preserved), so the
|
|
284
|
+
client's model picker switches the model mid-thread.
|
|
285
|
+
"""
|
|
286
|
+
from acp.schema import SetSessionConfigOptionResponse
|
|
287
|
+
|
|
288
|
+
options = session_config.apply_config_option(config_id, value)
|
|
289
|
+
if config_id == session_config.MODEL_OPTION_ID:
|
|
290
|
+
self._rebind_session_model(session_id)
|
|
291
|
+
return SetSessionConfigOptionResponse(config_options=options or None)
|
|
292
|
+
|
|
293
|
+
async def list_sessions(
|
|
294
|
+
self, cursor: Optional[str] = None, cwd: Optional[str] = None, **kwargs: Any
|
|
295
|
+
) -> ListSessionsResponse:
|
|
296
|
+
"""List every revivable session: live in-memory + persisted on disk.
|
|
297
|
+
|
|
298
|
+
Live sessions (this process) merge with sessions persisted to disk in
|
|
299
|
+
prior runs, so a client's picker still shows -- and can
|
|
300
|
+
``session/load`` / ``session/resume`` -- threads that outlived the
|
|
301
|
+
process that made them. Live wins on id collision (it carries the
|
|
302
|
+
freshest state). Uncursored single page; optional ``cwd`` filter.
|
|
303
|
+
"""
|
|
304
|
+
infos: List[SessionInfo] = []
|
|
305
|
+
seen: set[str] = set()
|
|
306
|
+
for s in self._sessions.values():
|
|
307
|
+
if cwd is not None and s.cwd != cwd:
|
|
308
|
+
continue
|
|
309
|
+
seen.add(s.session_id)
|
|
310
|
+
infos.append(
|
|
311
|
+
SessionInfo(
|
|
312
|
+
session_id=s.session_id,
|
|
313
|
+
cwd=s.cwd or cwd or "",
|
|
314
|
+
additional_directories=s.additional_directories or None,
|
|
315
|
+
)
|
|
316
|
+
)
|
|
317
|
+
for record in persistence.list_persisted():
|
|
318
|
+
if record.session_id in seen:
|
|
319
|
+
continue
|
|
320
|
+
if cwd is not None and record.cwd != cwd:
|
|
321
|
+
continue
|
|
322
|
+
seen.add(record.session_id)
|
|
323
|
+
infos.append(
|
|
324
|
+
SessionInfo(
|
|
325
|
+
session_id=record.session_id,
|
|
326
|
+
cwd=record.cwd or cwd or "",
|
|
327
|
+
additional_directories=record.additional_directories or None,
|
|
328
|
+
)
|
|
329
|
+
)
|
|
330
|
+
return ListSessionsResponse(sessions=infos, next_cursor=None)
|
|
331
|
+
|
|
332
|
+
async def close_session(
|
|
333
|
+
self, session_id: str, **kwargs: Any
|
|
334
|
+
) -> CloseSessionResponse:
|
|
335
|
+
"""Drop a session, cancelling any in-flight run first.
|
|
336
|
+
|
|
337
|
+
A close is a deliberate act, so its persisted history is deleted too --
|
|
338
|
+
otherwise the session would resurrect in the next ``list_sessions``,
|
|
339
|
+
which is exactly the "disappeared session that won't stay gone" bug in
|
|
340
|
+
reverse.
|
|
341
|
+
|
|
342
|
+
Also purges this session's own bucket of
|
|
343
|
+
``load_model_with_fallback``'s once-per-conversation dead-model-warning
|
|
344
|
+
dedup (keyed by the session's conversation root id -- see
|
|
345
|
+
``subagent_invocation.py`` / ``subagent_context.py``). Sub-agent
|
|
346
|
+
warnings are scoped per session precisely so they never leak into any
|
|
347
|
+
OTHER session; without this, a long-running server churning through
|
|
348
|
+
many short-lived sessions would accumulate one dangling bucket per
|
|
349
|
+
closed session forever.
|
|
350
|
+
|
|
351
|
+
Waits for any in-flight run to actually finish unwinding
|
|
352
|
+
(``cancel_and_wait``, not the bare ``cancel``) before purging --
|
|
353
|
+
otherwise a run still mid-flight inside ``_invoke_agent_impl`` could
|
|
354
|
+
write a fresh warning into this session's bucket a moment *after*
|
|
355
|
+
the purge, leaking that one entry forever (nothing will call
|
|
356
|
+
``close_session`` for this id a second time).
|
|
357
|
+
"""
|
|
358
|
+
from code_puppy.agents._builder import reset_model_fallback_warnings
|
|
359
|
+
|
|
360
|
+
session = self._sessions.pop(session_id, None)
|
|
361
|
+
if session is not None:
|
|
362
|
+
await session.cancel_and_wait()
|
|
363
|
+
persistence.delete(session_id)
|
|
364
|
+
reset_model_fallback_warnings(scope=session_id)
|
|
365
|
+
return CloseSessionResponse()
|
|
366
|
+
|
|
367
|
+
# ---- Prompt turn ------------------------------------------------------
|
|
368
|
+
async def prompt(
|
|
369
|
+
self, prompt: List[Any], session_id: str, **kwargs: Any
|
|
370
|
+
) -> PromptResponse:
|
|
371
|
+
"""Run the agent on a user prompt, streaming updates via the bridge."""
|
|
372
|
+
session = self._sessions.get(session_id)
|
|
373
|
+
if session is None:
|
|
374
|
+
raise ValueError(f"unknown session: {session_id}")
|
|
375
|
+
outcome = await session.prompt(prompt)
|
|
376
|
+
return PromptResponse(stop_reason=outcome.stop_reason, usage=outcome.usage)
|
|
377
|
+
|
|
378
|
+
async def cancel(self, session_id: str, **kwargs: Any) -> None:
|
|
379
|
+
"""Cancel the in-flight run for a session (notification)."""
|
|
380
|
+
session = self._sessions.get(session_id)
|
|
381
|
+
if session is not None:
|
|
382
|
+
session.cancel()
|
|
383
|
+
|
|
384
|
+
# ---- Helpers ----------------------------------------------------------
|
|
385
|
+
def _agent_capabilities(self) -> AgentCapabilities:
|
|
386
|
+
return capabilities.agent_capabilities()
|
|
387
|
+
|
|
388
|
+
def _make_session(
|
|
389
|
+
self,
|
|
390
|
+
session_id: str,
|
|
391
|
+
cwd: str,
|
|
392
|
+
additional_directories: Optional[List[str]],
|
|
393
|
+
mcp_servers: Optional[List[Any]],
|
|
394
|
+
*,
|
|
395
|
+
rehydrate: bool = False,
|
|
396
|
+
) -> ACPSession:
|
|
397
|
+
"""Build + register an ``ACPSession`` with a fresh agent.
|
|
398
|
+
|
|
399
|
+
When ``rehydrate`` is set, any persisted history for ``session_id`` is
|
|
400
|
+
replayed into the agent so the model continues the real conversation.
|
|
401
|
+
Client-injected ``mcp_servers`` are attached best-effort.
|
|
402
|
+
|
|
403
|
+
Also resets the SHARED/unscoped bucket of
|
|
404
|
+
``load_model_with_fallback``'s once-per-conversation
|
|
405
|
+
pinned-model-unavailable dedup (see ``_builder.reset_model_fallback_warnings``).
|
|
406
|
+
Sub-agent invocations scope their own warning state to the
|
|
407
|
+
conversation's ROOT id (a ContextVar -- see
|
|
408
|
+
``subagent_context.get_conversation_root_id``/``session.py``'s prompt
|
|
409
|
+
handler), so they already can't leak across sessions and don't need
|
|
410
|
+
this reset at all. The *main* agent build path
|
|
411
|
+
(``build_pydantic_agent``, used to build each session's own
|
|
412
|
+
top-level agent) still shares one unscoped bucket across every
|
|
413
|
+
session in this process, matching the pre-existing
|
|
414
|
+
single-main-agent-per-process model -- so without this reset, session
|
|
415
|
+
A hitting a dead pin during its own build could silently suppress the
|
|
416
|
+
identical warning for session B's build too. Resetting only the
|
|
417
|
+
``scope=None`` bucket on every new/loaded/forked session avoids that
|
|
418
|
+
without touching any other session's already-scoped sub-agent
|
|
419
|
+
warnings.
|
|
420
|
+
"""
|
|
421
|
+
from code_puppy.agents._builder import reset_model_fallback_warnings
|
|
422
|
+
|
|
423
|
+
reset_model_fallback_warnings(scope=None)
|
|
424
|
+
agent = self._new_agent()
|
|
425
|
+
if rehydrate:
|
|
426
|
+
history = persistence.load_history(session_id)
|
|
427
|
+
if history:
|
|
428
|
+
try:
|
|
429
|
+
agent.set_message_history(history)
|
|
430
|
+
except Exception: # noqa: BLE001
|
|
431
|
+
logger.debug("ACP: history rehydrate failed", exc_info=True)
|
|
432
|
+
if mcp_servers:
|
|
433
|
+
mcp_config.attach(agent, mcp_servers)
|
|
434
|
+
session = ACPSession(
|
|
435
|
+
session_id,
|
|
436
|
+
agent,
|
|
437
|
+
cwd=cwd,
|
|
438
|
+
additional_directories=additional_directories,
|
|
439
|
+
mcp_specs=mcp_servers,
|
|
440
|
+
)
|
|
441
|
+
self._sessions[session_id] = session
|
|
442
|
+
return session
|
|
443
|
+
|
|
444
|
+
@staticmethod
|
|
445
|
+
def _new_agent() -> Any:
|
|
446
|
+
from code_puppy.agents.agent_manager import get_current_agent_name, load_agent
|
|
447
|
+
|
|
448
|
+
return load_agent(get_current_agent_name())
|
|
449
|
+
|
|
450
|
+
def _announce_commands_soon(self, session_id: str) -> None:
|
|
451
|
+
"""Schedule an ``available_commands_update`` after the response ships.
|
|
452
|
+
|
|
453
|
+
We must not send the update before the ``new_session``/``load_session``
|
|
454
|
+
reply reaches the client (it wouldn't know the session yet).
|
|
455
|
+
Scheduling a
|
|
456
|
+
task defers it until this coroutine yields, i.e. after the reply.
|
|
457
|
+
"""
|
|
458
|
+
loop = state.get_loop()
|
|
459
|
+
if loop is not None:
|
|
460
|
+
loop.create_task(self._announce_commands(session_id))
|
|
461
|
+
|
|
462
|
+
async def _announce_commands(self, session_id: str) -> None:
|
|
463
|
+
"""Tell the client which slash commands Code Puppy exposes.
|
|
464
|
+
|
|
465
|
+
Deferred behind a short delay: ``create_task`` only yields one tick,
|
|
466
|
+
which isn't enough to guarantee the ``new_session`` reply has been
|
|
467
|
+
serialized onto the wire. Without this the client can receive the
|
|
468
|
+
notification before it knows the session id and drop it as "unknown
|
|
469
|
+
session". 100ms is imperceptible and comfortably after the reply.
|
|
470
|
+
"""
|
|
471
|
+
await asyncio.sleep(0.1)
|
|
472
|
+
connection = state.get_connection()
|
|
473
|
+
if connection is None:
|
|
474
|
+
return
|
|
475
|
+
try:
|
|
476
|
+
from code_puppy.command_line.command_registry import get_unique_commands
|
|
477
|
+
|
|
478
|
+
available = [
|
|
479
|
+
AvailableCommand(name=c.name, description=c.description)
|
|
480
|
+
for c in get_unique_commands()
|
|
481
|
+
]
|
|
482
|
+
except Exception: # noqa: BLE001
|
|
483
|
+
logger.debug("ACP: could not enumerate slash commands", exc_info=True)
|
|
484
|
+
return
|
|
485
|
+
if not available:
|
|
486
|
+
return
|
|
487
|
+
update = AvailableCommandsUpdate(
|
|
488
|
+
session_update="available_commands_update",
|
|
489
|
+
available_commands=available,
|
|
490
|
+
)
|
|
491
|
+
try:
|
|
492
|
+
await connection.session_update(session_id, update)
|
|
493
|
+
except Exception: # noqa: BLE001
|
|
494
|
+
logger.debug("ACP: available_commands_update failed", exc_info=True)
|