stackwright-puppy 0.0.472.post1__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/__init__.py +10 -0
- code_puppy/__main__.py +10 -0
- code_puppy/agents/__init__.py +31 -0
- code_puppy/agents/_builder.py +285 -0
- code_puppy/agents/_compaction.py +485 -0
- code_puppy/agents/_diagnostics.py +173 -0
- code_puppy/agents/_history.py +273 -0
- code_puppy/agents/_key_listeners.py +150 -0
- code_puppy/agents/_non_streaming_render.py +149 -0
- code_puppy/agents/_runtime.py +523 -0
- code_puppy/agents/agent_c_reviewer.py +154 -0
- code_puppy/agents/agent_code_puppy.py +96 -0
- code_puppy/agents/agent_code_reviewer.py +89 -0
- code_puppy/agents/agent_cpp_reviewer.py +131 -0
- code_puppy/agents/agent_creator_agent.py +631 -0
- code_puppy/agents/agent_golang_reviewer.py +150 -0
- code_puppy/agents/agent_helios.py +123 -0
- code_puppy/agents/agent_javascript_reviewer.py +159 -0
- code_puppy/agents/agent_manager.py +742 -0
- code_puppy/agents/agent_pack_leader.py +402 -0
- code_puppy/agents/agent_planning.py +164 -0
- code_puppy/agents/agent_python_programmer.py +168 -0
- code_puppy/agents/agent_python_reviewer.py +89 -0
- code_puppy/agents/agent_qa_expert.py +162 -0
- code_puppy/agents/agent_qa_kitten.py +207 -0
- code_puppy/agents/agent_scheduler.py +120 -0
- code_puppy/agents/agent_security_auditor.py +180 -0
- code_puppy/agents/agent_terminal_qa.py +322 -0
- code_puppy/agents/agent_typescript_reviewer.py +165 -0
- code_puppy/agents/base_agent.py +189 -0
- code_puppy/agents/event_stream_handler.py +349 -0
- code_puppy/agents/json_agent.py +202 -0
- code_puppy/agents/pack/__init__.py +32 -0
- code_puppy/agents/pack/bloodhound.py +303 -0
- code_puppy/agents/pack/retriever.py +392 -0
- code_puppy/agents/pack/shepherd.py +344 -0
- code_puppy/agents/pack/terrier.py +286 -0
- code_puppy/agents/pack/watchdog.py +366 -0
- code_puppy/agents/prompt_reviewer.py +144 -0
- code_puppy/agents/subagent_stream_handler.py +277 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +169 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +453 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +217 -0
- code_puppy/api/routers/config.py +75 -0
- code_puppy/api/routers/sessions.py +234 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +790 -0
- code_puppy/chatgpt_codex_client.py +338 -0
- code_puppy/claude_cache_client.py +773 -0
- code_puppy/cli_runner.py +1063 -0
- code_puppy/command_line/__init__.py +1 -0
- code_puppy/command_line/add_model_menu.py +1332 -0
- code_puppy/command_line/agent_menu.py +675 -0
- code_puppy/command_line/attachments.py +395 -0
- code_puppy/command_line/autosave_menu.py +716 -0
- code_puppy/command_line/clipboard.py +527 -0
- code_puppy/command_line/colors_menu.py +532 -0
- code_puppy/command_line/command_handler.py +293 -0
- code_puppy/command_line/command_registry.py +150 -0
- code_puppy/command_line/config_commands.py +719 -0
- code_puppy/command_line/core_commands.py +764 -0
- code_puppy/command_line/diff_menu.py +865 -0
- code_puppy/command_line/file_path_completion.py +73 -0
- code_puppy/command_line/load_context_completion.py +52 -0
- code_puppy/command_line/mcp/__init__.py +10 -0
- code_puppy/command_line/mcp/base.py +32 -0
- code_puppy/command_line/mcp/catalog_server_installer.py +175 -0
- code_puppy/command_line/mcp/custom_server_form.py +688 -0
- code_puppy/command_line/mcp/custom_server_installer.py +195 -0
- code_puppy/command_line/mcp/edit_command.py +148 -0
- code_puppy/command_line/mcp/handler.py +138 -0
- code_puppy/command_line/mcp/help_command.py +147 -0
- code_puppy/command_line/mcp/install_command.py +214 -0
- code_puppy/command_line/mcp/install_menu.py +705 -0
- code_puppy/command_line/mcp/list_command.py +94 -0
- code_puppy/command_line/mcp/logs_command.py +235 -0
- code_puppy/command_line/mcp/remove_command.py +82 -0
- code_puppy/command_line/mcp/restart_command.py +100 -0
- code_puppy/command_line/mcp/search_command.py +123 -0
- code_puppy/command_line/mcp/start_all_command.py +135 -0
- code_puppy/command_line/mcp/start_command.py +117 -0
- code_puppy/command_line/mcp/status_command.py +184 -0
- code_puppy/command_line/mcp/stop_all_command.py +112 -0
- code_puppy/command_line/mcp/stop_command.py +80 -0
- code_puppy/command_line/mcp/test_command.py +107 -0
- code_puppy/command_line/mcp/utils.py +129 -0
- code_puppy/command_line/mcp/wizard_utils.py +334 -0
- code_puppy/command_line/mcp_completion.py +174 -0
- code_puppy/command_line/model_picker_completion.py +512 -0
- code_puppy/command_line/model_settings_menu.py +986 -0
- code_puppy/command_line/onboarding_slides.py +179 -0
- code_puppy/command_line/onboarding_wizard.py +342 -0
- code_puppy/command_line/pagination.py +43 -0
- code_puppy/command_line/pin_command_completion.py +329 -0
- code_puppy/command_line/prompt_toolkit_completion.py +850 -0
- code_puppy/command_line/session_commands.py +304 -0
- code_puppy/command_line/shell_passthrough.py +145 -0
- code_puppy/command_line/skills_completion.py +160 -0
- code_puppy/command_line/uc_menu.py +908 -0
- code_puppy/command_line/utils.py +93 -0
- code_puppy/command_line/wiggum_state.py +78 -0
- code_puppy/config.py +1972 -0
- code_puppy/error_logging.py +134 -0
- code_puppy/gemini_code_assist.py +385 -0
- code_puppy/gemini_model.py +840 -0
- code_puppy/hook_engine/README.md +105 -0
- code_puppy/hook_engine/__init__.py +21 -0
- code_puppy/hook_engine/aliases.py +155 -0
- code_puppy/hook_engine/engine.py +221 -0
- code_puppy/hook_engine/executor.py +296 -0
- code_puppy/hook_engine/matcher.py +156 -0
- code_puppy/hook_engine/models.py +240 -0
- code_puppy/hook_engine/registry.py +106 -0
- code_puppy/hook_engine/validator.py +144 -0
- code_puppy/http_utils.py +361 -0
- code_puppy/keymap.py +128 -0
- code_puppy/list_filtering.py +26 -0
- code_puppy/main.py +10 -0
- code_puppy/mcp_/__init__.py +66 -0
- code_puppy/mcp_/async_lifecycle.py +286 -0
- code_puppy/mcp_/blocking_startup.py +469 -0
- code_puppy/mcp_/captured_stdio_server.py +275 -0
- code_puppy/mcp_/circuit_breaker.py +290 -0
- code_puppy/mcp_/config_wizard.py +507 -0
- code_puppy/mcp_/dashboard.py +308 -0
- code_puppy/mcp_/error_isolation.py +407 -0
- code_puppy/mcp_/examples/retry_example.py +226 -0
- code_puppy/mcp_/health_monitor.py +589 -0
- code_puppy/mcp_/managed_server.py +428 -0
- code_puppy/mcp_/manager.py +872 -0
- code_puppy/mcp_/mcp_logs.py +224 -0
- code_puppy/mcp_/registry.py +451 -0
- code_puppy/mcp_/retry_manager.py +337 -0
- code_puppy/mcp_/server_registry_catalog.py +1126 -0
- code_puppy/mcp_/status_tracker.py +355 -0
- code_puppy/mcp_/system_tools.py +209 -0
- code_puppy/mcp_prompts/__init__.py +1 -0
- code_puppy/mcp_prompts/hook_creator.py +103 -0
- code_puppy/messaging/__init__.py +255 -0
- code_puppy/messaging/bus.py +613 -0
- code_puppy/messaging/commands.py +167 -0
- code_puppy/messaging/markdown_patches.py +57 -0
- code_puppy/messaging/message_queue.py +361 -0
- code_puppy/messaging/messages.py +569 -0
- code_puppy/messaging/queue_console.py +271 -0
- code_puppy/messaging/renderers.py +311 -0
- code_puppy/messaging/rich_renderer.py +1158 -0
- code_puppy/messaging/spinner/__init__.py +83 -0
- code_puppy/messaging/spinner/console_spinner.py +240 -0
- code_puppy/messaging/spinner/spinner_base.py +95 -0
- code_puppy/messaging/subagent_console.py +460 -0
- code_puppy/model_factory.py +938 -0
- code_puppy/model_switching.py +63 -0
- code_puppy/model_utils.py +156 -0
- code_puppy/models.json +165 -0
- code_puppy/models_dev_api.json +1 -0
- code_puppy/models_dev_parser.py +592 -0
- code_puppy/plugins/__init__.py +186 -0
- code_puppy/plugins/agent_skills/__init__.py +22 -0
- code_puppy/plugins/agent_skills/config.py +175 -0
- code_puppy/plugins/agent_skills/discovery.py +136 -0
- code_puppy/plugins/agent_skills/downloader.py +392 -0
- code_puppy/plugins/agent_skills/installer.py +22 -0
- code_puppy/plugins/agent_skills/metadata.py +219 -0
- code_puppy/plugins/agent_skills/prompt_builder.py +60 -0
- code_puppy/plugins/agent_skills/register_callbacks.py +241 -0
- code_puppy/plugins/agent_skills/remote_catalog.py +322 -0
- code_puppy/plugins/agent_skills/skill_catalog.py +257 -0
- code_puppy/plugins/agent_skills/skills_install_menu.py +691 -0
- code_puppy/plugins/agent_skills/skills_menu.py +797 -0
- code_puppy/plugins/aws_bedrock/__init__.py +14 -0
- code_puppy/plugins/aws_bedrock/config.py +101 -0
- code_puppy/plugins/aws_bedrock/register_callbacks.py +243 -0
- code_puppy/plugins/aws_bedrock/utils.py +155 -0
- code_puppy/plugins/azure_foundry/README.md +238 -0
- code_puppy/plugins/azure_foundry/__init__.py +15 -0
- code_puppy/plugins/azure_foundry/config.py +127 -0
- code_puppy/plugins/azure_foundry/discovery.py +189 -0
- code_puppy/plugins/azure_foundry/register_callbacks.py +497 -0
- code_puppy/plugins/azure_foundry/token.py +182 -0
- code_puppy/plugins/azure_foundry/utils.py +348 -0
- code_puppy/plugins/chatgpt_oauth/__init__.py +8 -0
- code_puppy/plugins/chatgpt_oauth/config.py +52 -0
- code_puppy/plugins/chatgpt_oauth/oauth_flow.py +329 -0
- code_puppy/plugins/chatgpt_oauth/register_callbacks.py +174 -0
- code_puppy/plugins/chatgpt_oauth/test_plugin.py +301 -0
- code_puppy/plugins/chatgpt_oauth/utils.py +530 -0
- code_puppy/plugins/claude_code_hooks/__init__.py +1 -0
- code_puppy/plugins/claude_code_hooks/config.py +137 -0
- code_puppy/plugins/claude_code_hooks/register_callbacks.py +176 -0
- code_puppy/plugins/claude_code_oauth/README.md +167 -0
- code_puppy/plugins/claude_code_oauth/SETUP.md +93 -0
- code_puppy/plugins/claude_code_oauth/__init__.py +25 -0
- code_puppy/plugins/claude_code_oauth/config.py +52 -0
- code_puppy/plugins/claude_code_oauth/fast_mode.py +128 -0
- code_puppy/plugins/claude_code_oauth/prompt_handler.py +65 -0
- code_puppy/plugins/claude_code_oauth/register_callbacks.py +515 -0
- code_puppy/plugins/claude_code_oauth/test_fast_mode.py +167 -0
- code_puppy/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_puppy/plugins/claude_code_oauth/token_refresh_heartbeat.py +241 -0
- code_puppy/plugins/claude_code_oauth/utils.py +649 -0
- code_puppy/plugins/copilot_auth/__init__.py +11 -0
- code_puppy/plugins/copilot_auth/config.py +93 -0
- code_puppy/plugins/copilot_auth/reasoning_client.py +411 -0
- code_puppy/plugins/copilot_auth/register_callbacks.py +463 -0
- code_puppy/plugins/copilot_auth/utils.py +587 -0
- code_puppy/plugins/customizable_commands/__init__.py +0 -0
- code_puppy/plugins/customizable_commands/register_callbacks.py +152 -0
- code_puppy/plugins/example_custom_command/README.md +280 -0
- code_puppy/plugins/example_custom_command/register_callbacks.py +51 -0
- code_puppy/plugins/file_permission_handler/__init__.py +4 -0
- code_puppy/plugins/file_permission_handler/register_callbacks.py +470 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/plugins/hook_creator/__init__.py +1 -0
- code_puppy/plugins/hook_creator/register_callbacks.py +33 -0
- code_puppy/plugins/hook_manager/__init__.py +1 -0
- code_puppy/plugins/hook_manager/config.py +290 -0
- code_puppy/plugins/hook_manager/hooks_menu.py +564 -0
- code_puppy/plugins/hook_manager/register_callbacks.py +227 -0
- code_puppy/plugins/oauth_puppy_html.py +228 -0
- code_puppy/plugins/ollama_setup/__init__.py +5 -0
- code_puppy/plugins/ollama_setup/completer.py +38 -0
- code_puppy/plugins/ollama_setup/register_callbacks.py +412 -0
- code_puppy/plugins/pop_command/__init__.py +1 -0
- code_puppy/plugins/pop_command/register_callbacks.py +191 -0
- code_puppy/plugins/scheduler/__init__.py +1 -0
- code_puppy/plugins/scheduler/register_callbacks.py +88 -0
- code_puppy/plugins/scheduler/scheduler_menu.py +538 -0
- code_puppy/plugins/scheduler/scheduler_wizard.py +341 -0
- code_puppy/plugins/shell_safety/__init__.py +6 -0
- code_puppy/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_puppy/plugins/shell_safety/command_cache.py +156 -0
- code_puppy/plugins/shell_safety/register_callbacks.py +202 -0
- code_puppy/plugins/synthetic_status/__init__.py +1 -0
- code_puppy/plugins/synthetic_status/register_callbacks.py +132 -0
- code_puppy/plugins/synthetic_status/status_api.py +147 -0
- code_puppy/plugins/universal_constructor/__init__.py +13 -0
- code_puppy/plugins/universal_constructor/models.py +138 -0
- code_puppy/plugins/universal_constructor/register_callbacks.py +47 -0
- code_puppy/plugins/universal_constructor/registry.py +302 -0
- code_puppy/plugins/universal_constructor/sandbox.py +584 -0
- code_puppy/provider_identity.py +107 -0
- code_puppy/pydantic_patches.py +356 -0
- code_puppy/reopenable_async_client.py +232 -0
- code_puppy/round_robin_model.py +150 -0
- code_puppy/scheduler/__init__.py +41 -0
- code_puppy/scheduler/__main__.py +9 -0
- code_puppy/scheduler/cli.py +118 -0
- code_puppy/scheduler/config.py +126 -0
- code_puppy/scheduler/daemon.py +280 -0
- code_puppy/scheduler/executor.py +155 -0
- code_puppy/scheduler/platform.py +19 -0
- code_puppy/scheduler/platform_unix.py +22 -0
- code_puppy/scheduler/platform_win.py +32 -0
- code_puppy/session_storage.py +338 -0
- code_puppy/status_display.py +257 -0
- code_puppy/summarization_agent.py +174 -0
- code_puppy/terminal_utils.py +418 -0
- code_puppy/tools/__init__.py +501 -0
- code_puppy/tools/agent_tools.py +646 -0
- code_puppy/tools/ask_user_question/__init__.py +26 -0
- code_puppy/tools/ask_user_question/constants.py +73 -0
- code_puppy/tools/ask_user_question/demo_tui.py +55 -0
- code_puppy/tools/ask_user_question/handler.py +234 -0
- code_puppy/tools/ask_user_question/models.py +304 -0
- code_puppy/tools/ask_user_question/registration.py +39 -0
- code_puppy/tools/ask_user_question/renderers.py +309 -0
- code_puppy/tools/ask_user_question/terminal_ui.py +329 -0
- code_puppy/tools/ask_user_question/theme.py +155 -0
- code_puppy/tools/ask_user_question/tui_loop.py +423 -0
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +289 -0
- code_puppy/tools/browser/browser_interactions.py +545 -0
- code_puppy/tools/browser/browser_locators.py +640 -0
- code_puppy/tools/browser/browser_manager.py +378 -0
- code_puppy/tools/browser/browser_navigation.py +251 -0
- code_puppy/tools/browser/browser_screenshot.py +179 -0
- code_puppy/tools/browser/browser_scripts.py +462 -0
- code_puppy/tools/browser/browser_workflows.py +221 -0
- code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
- code_puppy/tools/browser/terminal_command_tools.py +534 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +677 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +1346 -0
- code_puppy/tools/common.py +1409 -0
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/file_modifications.py +913 -0
- code_puppy/tools/file_operations.py +802 -0
- code_puppy/tools/scheduler_tools.py +412 -0
- code_puppy/tools/skills_tools.py +244 -0
- code_puppy/tools/subagent_context.py +158 -0
- code_puppy/tools/tools_content.py +50 -0
- code_puppy/tools/universal_constructor.py +893 -0
- code_puppy/uvx_detection.py +242 -0
- code_puppy/version_checker.py +82 -0
- stackwright_puppy-0.0.472.post1.data/data/code_puppy/models.json +165 -0
- stackwright_puppy-0.0.472.post1.data/data/code_puppy/models_dev_api.json +1 -0
- stackwright_puppy-0.0.472.post1.dist-info/METADATA +809 -0
- stackwright_puppy-0.0.472.post1.dist-info/RECORD +309 -0
- stackwright_puppy-0.0.472.post1.dist-info/WHEEL +4 -0
- stackwright_puppy-0.0.472.post1.dist-info/entry_points.txt +4 -0
- stackwright_puppy-0.0.472.post1.dist-info/licenses/LICENSE +21 -0
code_puppy/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import importlib.metadata
|
|
2
|
+
|
|
3
|
+
# Biscuit was here! 🐶
|
|
4
|
+
try:
|
|
5
|
+
_detected_version = importlib.metadata.version("code-puppy")
|
|
6
|
+
# Ensure we never end up with None or empty string
|
|
7
|
+
__version__ = _detected_version if _detected_version else "0.0.0-dev"
|
|
8
|
+
except Exception:
|
|
9
|
+
# Fallback for dev environments where metadata might not be available
|
|
10
|
+
__version__ = "0.0.0-dev"
|
code_puppy/__main__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Agent management system for code-puppy.
|
|
2
|
+
|
|
3
|
+
This module provides functionality for switching between different agent
|
|
4
|
+
configurations, each with their own system prompts and tool sets.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .agent_manager import (
|
|
8
|
+
clone_agent,
|
|
9
|
+
delete_clone_agent,
|
|
10
|
+
get_agent_descriptions,
|
|
11
|
+
get_available_agents,
|
|
12
|
+
get_current_agent,
|
|
13
|
+
is_clone_agent_name,
|
|
14
|
+
load_agent,
|
|
15
|
+
refresh_agents,
|
|
16
|
+
set_current_agent,
|
|
17
|
+
)
|
|
18
|
+
from .subagent_stream_handler import subagent_stream_handler
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"clone_agent",
|
|
22
|
+
"delete_clone_agent",
|
|
23
|
+
"get_available_agents",
|
|
24
|
+
"get_current_agent",
|
|
25
|
+
"is_clone_agent_name",
|
|
26
|
+
"set_current_agent",
|
|
27
|
+
"load_agent",
|
|
28
|
+
"get_agent_descriptions",
|
|
29
|
+
"refresh_agents",
|
|
30
|
+
"subagent_stream_handler",
|
|
31
|
+
]
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""Pydantic-ai agent construction + MCP wiring, extracted from ``BaseAgent``.
|
|
2
|
+
|
|
3
|
+
Collapses the previous duplicated DBOS vs non-DBOS build paths and the parallel
|
|
4
|
+
``_create_agent_with_output_type`` method into a single ``build_pydantic_agent``
|
|
5
|
+
entry point. Everything else in here (puppy rules loading, MCP server loading,
|
|
6
|
+
model fallback, MCP tool filtering) is a pure free function.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import uuid
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
|
14
|
+
|
|
15
|
+
from pydantic_ai import Agent as PydanticAgent
|
|
16
|
+
from pydantic_ai.durable_exec.dbos import DBOSAgent
|
|
17
|
+
from rich.text import Text
|
|
18
|
+
|
|
19
|
+
from code_puppy.agents._compaction import make_history_processor
|
|
20
|
+
from code_puppy.agents.event_stream_handler import event_stream_handler
|
|
21
|
+
from code_puppy.config import (
|
|
22
|
+
CONFIG_DIR,
|
|
23
|
+
get_global_model_name,
|
|
24
|
+
get_use_dbos,
|
|
25
|
+
get_value,
|
|
26
|
+
)
|
|
27
|
+
from code_puppy.mcp_ import get_mcp_manager
|
|
28
|
+
from code_puppy.messaging import emit_error, emit_info, emit_warning
|
|
29
|
+
from code_puppy.model_factory import ModelFactory, make_model_settings
|
|
30
|
+
|
|
31
|
+
# Module-level counter used when naming DBOSAgent instances. Incremented every
|
|
32
|
+
# time ``build_pydantic_agent`` wraps with DBOS so each workflow has a unique name.
|
|
33
|
+
_reload_count = 0
|
|
34
|
+
|
|
35
|
+
_AGENT_RULE_FILES = ("AGENTS.md", "AGENT.md", "agents.md", "agent.md")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_puppy_rules() -> Optional[str]:
|
|
39
|
+
"""Load AGENT(S).md from global config dir and/or the current project dir.
|
|
40
|
+
|
|
41
|
+
Global rules (``~/.code_puppy/AGENTS.md``) come first; project-local rules
|
|
42
|
+
are appended, allowing projects to override/extend global ones. Returns
|
|
43
|
+
``None`` if neither exists.
|
|
44
|
+
"""
|
|
45
|
+
global_rules: Optional[str] = None
|
|
46
|
+
for name in _AGENT_RULE_FILES:
|
|
47
|
+
candidate = Path(CONFIG_DIR) / name
|
|
48
|
+
if candidate.exists():
|
|
49
|
+
global_rules = candidate.read_text(encoding="utf-8-sig")
|
|
50
|
+
break
|
|
51
|
+
|
|
52
|
+
project_rules: Optional[str] = None
|
|
53
|
+
for name in _AGENT_RULE_FILES:
|
|
54
|
+
candidate = Path(name)
|
|
55
|
+
if candidate.exists():
|
|
56
|
+
project_rules = candidate.read_text(encoding="utf-8-sig")
|
|
57
|
+
break
|
|
58
|
+
|
|
59
|
+
rules = [r for r in (global_rules, project_rules) if r]
|
|
60
|
+
return "\n\n".join(rules) if rules else None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def load_mcp_servers(
|
|
64
|
+
extra_headers: Optional[Dict[str, str]] = None,
|
|
65
|
+
) -> List[Any]:
|
|
66
|
+
"""Return pydantic-ai compatible MCP servers, or ``[]`` if disabled."""
|
|
67
|
+
del extra_headers # accepted for API compatibility; manager owns headers
|
|
68
|
+
mcp_disabled = get_value("disable_mcp_servers")
|
|
69
|
+
if mcp_disabled and str(mcp_disabled).lower() in ("1", "true", "yes", "on"):
|
|
70
|
+
return []
|
|
71
|
+
return get_mcp_manager().get_servers_for_agent()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def reload_mcp_servers() -> List[Any]:
|
|
75
|
+
"""Force re-sync from ``mcp_servers.json`` and return updated servers."""
|
|
76
|
+
manager = get_mcp_manager()
|
|
77
|
+
manager.sync_from_config()
|
|
78
|
+
return manager.get_servers_for_agent()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def load_model_with_fallback(
|
|
82
|
+
requested_model_name: str,
|
|
83
|
+
models_config: Dict[str, Any],
|
|
84
|
+
message_group: str,
|
|
85
|
+
) -> Tuple[Any, str]:
|
|
86
|
+
"""Load the requested model, or fall back to a sensible alternative.
|
|
87
|
+
|
|
88
|
+
Falls back in order: the globally configured model, then any other
|
|
89
|
+
configured model. Raises ``ValueError`` only if nothing loads.
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
return ModelFactory.get_model(
|
|
93
|
+
requested_model_name, models_config
|
|
94
|
+
), requested_model_name
|
|
95
|
+
except ValueError as exc:
|
|
96
|
+
available = list(models_config.keys())
|
|
97
|
+
available_str = (
|
|
98
|
+
", ".join(sorted(available)) if available else "no configured models"
|
|
99
|
+
)
|
|
100
|
+
emit_warning(
|
|
101
|
+
f"Model '{requested_model_name}' not found. Available models: {available_str}",
|
|
102
|
+
message_group=message_group,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
candidates: List[str] = []
|
|
106
|
+
global_candidate = get_global_model_name()
|
|
107
|
+
if global_candidate:
|
|
108
|
+
candidates.append(global_candidate)
|
|
109
|
+
for candidate in available:
|
|
110
|
+
if candidate not in candidates:
|
|
111
|
+
candidates.append(candidate)
|
|
112
|
+
|
|
113
|
+
for candidate in candidates:
|
|
114
|
+
if not candidate or candidate == requested_model_name:
|
|
115
|
+
continue
|
|
116
|
+
try:
|
|
117
|
+
model = ModelFactory.get_model(candidate, models_config)
|
|
118
|
+
emit_info(
|
|
119
|
+
f"Using fallback model: {candidate}", message_group=message_group
|
|
120
|
+
)
|
|
121
|
+
return model, candidate
|
|
122
|
+
except ValueError:
|
|
123
|
+
continue
|
|
124
|
+
|
|
125
|
+
friendly = (
|
|
126
|
+
"No valid model could be loaded. Update the model configuration or "
|
|
127
|
+
"set a valid model with `config set`."
|
|
128
|
+
)
|
|
129
|
+
emit_error(friendly, message_group=message_group)
|
|
130
|
+
raise ValueError(friendly) from exc
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def filter_conflicting_mcp_tools(
|
|
134
|
+
mcp_servers: List[Any],
|
|
135
|
+
existing_tool_names: Set[str],
|
|
136
|
+
) -> List[Any]:
|
|
137
|
+
"""Strip any MCP tools whose names collide with already-registered tools.
|
|
138
|
+
|
|
139
|
+
Returns a new list of MCP toolsets (possibly containing filtered ``ToolSet``
|
|
140
|
+
replacements). If a server doesn't expose a ``.tools`` attribute we pass it
|
|
141
|
+
through unchanged — better to risk a duplicate than to drop the whole server.
|
|
142
|
+
"""
|
|
143
|
+
if not mcp_servers or not existing_tool_names:
|
|
144
|
+
return list(mcp_servers) if mcp_servers else []
|
|
145
|
+
|
|
146
|
+
from pydantic_ai.tools import ToolSet
|
|
147
|
+
|
|
148
|
+
filtered: List[Any] = []
|
|
149
|
+
for server in mcp_servers:
|
|
150
|
+
server_tools = getattr(server, "tools", None)
|
|
151
|
+
if server_tools is None:
|
|
152
|
+
filtered.append(server)
|
|
153
|
+
continue
|
|
154
|
+
|
|
155
|
+
kept = {
|
|
156
|
+
name: func
|
|
157
|
+
for name, func in server_tools.items()
|
|
158
|
+
if name not in existing_tool_names
|
|
159
|
+
}
|
|
160
|
+
if not kept:
|
|
161
|
+
continue # whole server was conflicts — drop it
|
|
162
|
+
|
|
163
|
+
replacement = ToolSet()
|
|
164
|
+
for name, func in kept.items():
|
|
165
|
+
replacement._tools[name] = func
|
|
166
|
+
filtered.append(replacement)
|
|
167
|
+
|
|
168
|
+
return filtered
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _assemble_instructions(agent: Any, resolved_model_name: str) -> str:
|
|
172
|
+
"""Compose full system prompt + puppy rules + extended-thinking note."""
|
|
173
|
+
from code_puppy.model_utils import prepare_prompt_for_model
|
|
174
|
+
from code_puppy.tools import (
|
|
175
|
+
EXTENDED_THINKING_PROMPT_NOTE,
|
|
176
|
+
has_extended_thinking_active,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
instructions = agent.get_full_system_prompt()
|
|
180
|
+
puppy_rules = load_puppy_rules()
|
|
181
|
+
if puppy_rules:
|
|
182
|
+
instructions += f"\n{puppy_rules}"
|
|
183
|
+
|
|
184
|
+
if has_extended_thinking_active(resolved_model_name):
|
|
185
|
+
instructions += EXTENDED_THINKING_PROMPT_NOTE
|
|
186
|
+
|
|
187
|
+
prepared = prepare_prompt_for_model(
|
|
188
|
+
agent.get_model_name(), instructions, "", prepend_system_to_user=False
|
|
189
|
+
)
|
|
190
|
+
return prepared.instructions
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def build_pydantic_agent(
|
|
194
|
+
agent: Any,
|
|
195
|
+
output_type: Any = str,
|
|
196
|
+
message_group: Optional[str] = None,
|
|
197
|
+
) -> Any:
|
|
198
|
+
"""Build (and wire up) the pydantic-ai agent for ``agent``.
|
|
199
|
+
|
|
200
|
+
Replaces the old ``reload_code_generation_agent`` + ``_create_agent_with_output_type``
|
|
201
|
+
pair. Side effects on ``agent``:
|
|
202
|
+
|
|
203
|
+
- ``agent._puppy_rules = None`` (invalidates any cached rules)
|
|
204
|
+
- ``agent.cur_model`` ← resolved pydantic-ai model
|
|
205
|
+
- ``agent._last_model_name`` ← resolved model name
|
|
206
|
+
- ``agent.pydantic_agent`` ← the final (possibly DBOS-wrapped) agent
|
|
207
|
+
- ``agent._code_generation_agent`` ← same as ``pydantic_agent``
|
|
208
|
+
- ``agent._mcp_servers`` ← MCP toolsets (post-filter)
|
|
209
|
+
|
|
210
|
+
The build happens in two passes: we construct once with ``toolsets=[]`` so
|
|
211
|
+
we can introspect registered tool names, then rebuild with MCP servers
|
|
212
|
+
filtered against those names to prevent collisions. DBOS keeps MCP out of
|
|
213
|
+
the constructor entirely — the runtime injects it via ``_toolsets`` swap.
|
|
214
|
+
"""
|
|
215
|
+
global _reload_count
|
|
216
|
+
|
|
217
|
+
from code_puppy.tools import register_tools_for_agent
|
|
218
|
+
|
|
219
|
+
agent._puppy_rules = None
|
|
220
|
+
message_group = message_group or str(uuid.uuid4())
|
|
221
|
+
|
|
222
|
+
models_config = ModelFactory.load_config()
|
|
223
|
+
model, resolved_model_name = load_model_with_fallback(
|
|
224
|
+
agent.get_model_name(), models_config, message_group
|
|
225
|
+
)
|
|
226
|
+
instructions = _assemble_instructions(agent, resolved_model_name)
|
|
227
|
+
mcp_servers = load_mcp_servers()
|
|
228
|
+
model_settings = make_model_settings(resolved_model_name)
|
|
229
|
+
history_processor = make_history_processor(agent)
|
|
230
|
+
|
|
231
|
+
def _new_pydantic_agent(toolsets: List[Any]) -> PydanticAgent:
|
|
232
|
+
return PydanticAgent(
|
|
233
|
+
model=model,
|
|
234
|
+
instructions=instructions,
|
|
235
|
+
output_type=output_type,
|
|
236
|
+
retries=3,
|
|
237
|
+
toolsets=toolsets,
|
|
238
|
+
history_processors=[history_processor],
|
|
239
|
+
model_settings=model_settings,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Pass 1: build with empty toolsets so we can see what pydantic-ai + our
|
|
243
|
+
# tool registry actually produced, and filter MCP to avoid name clashes.
|
|
244
|
+
probe_agent = _new_pydantic_agent(toolsets=[])
|
|
245
|
+
agent_tools = agent.get_available_tools()
|
|
246
|
+
register_tools_for_agent(probe_agent, agent_tools, model_name=resolved_model_name)
|
|
247
|
+
|
|
248
|
+
existing_tool_names: Set[str] = set(getattr(probe_agent, "_tools", {}) or {})
|
|
249
|
+
filtered_mcp_servers = filter_conflicting_mcp_tools(
|
|
250
|
+
mcp_servers, existing_tool_names
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
dropped = len(mcp_servers) - len(filtered_mcp_servers)
|
|
254
|
+
if dropped:
|
|
255
|
+
emit_info(
|
|
256
|
+
Text.from_markup(f"[dim]Filtered {dropped} conflicting MCP tools[/dim]")
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Pass 2: real build. DBOS path keeps MCP out of the constructor because
|
|
260
|
+
# pydantic-ai's DBOS integration can't pickle async_generator toolsets.
|
|
261
|
+
use_dbos = get_use_dbos()
|
|
262
|
+
final_toolsets = [] if use_dbos else filtered_mcp_servers
|
|
263
|
+
final_pydantic = _new_pydantic_agent(toolsets=final_toolsets)
|
|
264
|
+
register_tools_for_agent(
|
|
265
|
+
final_pydantic, agent_tools, model_name=resolved_model_name
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
agent.cur_model = model
|
|
269
|
+
agent._last_model_name = resolved_model_name
|
|
270
|
+
agent._mcp_servers = filtered_mcp_servers
|
|
271
|
+
|
|
272
|
+
if use_dbos:
|
|
273
|
+
_reload_count += 1
|
|
274
|
+
wrapped = DBOSAgent(
|
|
275
|
+
final_pydantic,
|
|
276
|
+
name=f"{agent.name}-{_reload_count}",
|
|
277
|
+
event_stream_handler=event_stream_handler,
|
|
278
|
+
)
|
|
279
|
+
agent.pydantic_agent = wrapped
|
|
280
|
+
agent._code_generation_agent = wrapped
|
|
281
|
+
return wrapped
|
|
282
|
+
|
|
283
|
+
agent.pydantic_agent = final_pydantic
|
|
284
|
+
agent._code_generation_agent = final_pydantic
|
|
285
|
+
return final_pydantic
|