code-puppy 0.0.344__tar.gz → 0.0.346__tar.gz
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-0.0.344 → code_puppy-0.0.346}/PKG-INFO +23 -1
- {code_puppy-0.0.344 → code_puppy-0.0.346}/README.md +21 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/base_agent.py +129 -37
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/cli_runner.py +35 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/add_model_menu.py +8 -9
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/config_commands.py +10 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/catalog_server_installer.py +5 -6
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/custom_server_installer.py +6 -7
- code_puppy-0.0.346/code_puppy/command_line/utils.py +93 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/config.py +23 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/managed_server.py +3 -2
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/message_queue.py +11 -23
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/summarization_agent.py +11 -1
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/agent_tools.py +55 -11
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/browser/vqa_agent.py +7 -1
- {code_puppy-0.0.344 → code_puppy-0.0.346}/pyproject.toml +2 -1
- code_puppy-0.0.344/code_puppy/command_line/utils.py +0 -39
- {code_puppy-0.0.344 → code_puppy-0.0.346}/.gitignore +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/LICENSE +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/__main__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_c_reviewer.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_code_puppy.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_code_reviewer.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_cpp_reviewer.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_creator_agent.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_golang_reviewer.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_javascript_reviewer.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_manager.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_planning.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_python_programmer.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_python_reviewer.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_qa_expert.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_qa_kitten.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_security_auditor.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/agent_typescript_reviewer.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/json_agent.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/agents/prompt_reviewer.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/callbacks.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/chatgpt_codex_client.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/claude_cache_client.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/attachments.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/autosave_menu.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/clipboard.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/colors_menu.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/command_handler.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/command_registry.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/core_commands.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/diff_menu.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/file_path_completion.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/load_context_completion.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/base.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/custom_server_form.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/edit_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/handler.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/help_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/install_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/install_menu.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/list_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/logs_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/remove_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/restart_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/search_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/start_all_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/start_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/status_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/stop_all_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/stop_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/test_command.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/utils.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/wizard_utils.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp_completion.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/model_picker_completion.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/model_settings_menu.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/motd.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/onboarding_slides.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/onboarding_wizard.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/pin_command_completion.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/session_commands.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/error_logging.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/gemini_code_assist.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/http_utils.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/keymap.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/main.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/async_lifecycle.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/blocking_startup.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/captured_stdio_server.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/circuit_breaker.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/config_wizard.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/dashboard.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/error_isolation.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/examples/retry_example.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/health_monitor.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/manager.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/mcp_logs.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/registry.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/retry_manager.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/server_registry_catalog.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/status_tracker.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/mcp_/system_tools.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/bus.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/commands.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/markdown_patches.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/messages.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/queue_console.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/renderers.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/rich_renderer.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/spinner/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/spinner/console_spinner.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/messaging/spinner/spinner_base.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/model_factory.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/model_utils.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/models.json +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/models_dev_parser.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/accounts.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/antigravity_model.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/config.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/constants.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/oauth.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/storage.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/token.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/transport.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/antigravity_oauth/utils.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/chatgpt_oauth/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/chatgpt_oauth/config.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/chatgpt_oauth/oauth_flow.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/chatgpt_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/chatgpt_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/chatgpt_oauth/utils.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/claude_code_oauth/README.md +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/claude_code_oauth/SETUP.md +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/claude_code_oauth/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/claude_code_oauth/config.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/claude_code_oauth/register_callbacks.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/claude_code_oauth/test_plugin.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/claude_code_oauth/utils.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/customizable_commands/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/customizable_commands/register_callbacks.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/example_custom_command/README.md +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/example_custom_command/register_callbacks.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/file_permission_handler/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/file_permission_handler/register_callbacks.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/oauth_puppy_html.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/shell_safety/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/shell_safety/agent_shell_safety.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/shell_safety/command_cache.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/plugins/shell_safety/register_callbacks.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/prompts/codex_system_prompt.md +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/pydantic_patches.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/reopenable_async_client.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/round_robin_model.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/session_storage.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/status_display.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/terminal_utils.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/browser/__init__.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/browser/browser_control.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/browser/browser_interactions.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/browser/browser_locators.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/browser/browser_navigation.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/browser/browser_screenshot.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/browser/browser_scripts.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/browser/browser_workflows.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/browser/camoufox_manager.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/command_runner.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/common.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/file_modifications.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/file_operations.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/tools/tools_content.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/uvx_detection.py +0 -0
- {code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/version_checker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-puppy
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.346
|
|
4
4
|
Summary: Code generation agent
|
|
5
5
|
Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
|
|
6
6
|
Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
|
|
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
16
16
|
Classifier: Topic :: Software Development :: Code Generators
|
|
17
17
|
Requires-Python: <3.14,>=3.11
|
|
18
18
|
Requires-Dist: camoufox>=0.4.11
|
|
19
|
+
Requires-Dist: dbos>=2.5.0
|
|
19
20
|
Requires-Dist: fastapi>=0.111.0
|
|
20
21
|
Requires-Dist: httpx[http2]>=0.24.1
|
|
21
22
|
Requires-Dist: json-repair>=0.46.2
|
|
@@ -173,6 +174,27 @@ These providers are automatically configured with correct OpenAI-compatible endp
|
|
|
173
174
|
- **⚠️ Unsupported Providers** - Providers like Amazon Bedrock and Google Vertex that require special authentication are clearly marked
|
|
174
175
|
- **⚠️ No Tool Calling** - Models without tool calling support show a big warning since they can't use Code Puppy's file/shell tools
|
|
175
176
|
|
|
177
|
+
### Durable Execution
|
|
178
|
+
|
|
179
|
+
Code Puppy now supports **[DBOS](https://github.com/dbos-inc/dbos-transact-py)** durable execution.
|
|
180
|
+
|
|
181
|
+
When enabled, every agent is automatically wrapped as a `DBOSAgent`, checkpointing key interactions (including agent inputs, LLM responses, MCP calls, and tool calls) in a database for durability and recovery.
|
|
182
|
+
|
|
183
|
+
You can toggle DBOS via either of these options:
|
|
184
|
+
|
|
185
|
+
- CLI config (persists): `/set enable_dbos true` (or `false` to disable)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
Config takes precedence if set; otherwise the environment variable is used.
|
|
189
|
+
|
|
190
|
+
### Configuration
|
|
191
|
+
|
|
192
|
+
The following environment variables control DBOS behavior:
|
|
193
|
+
- `DBOS_CONDUCTOR_KEY`: If set, Code Puppy connects to the [DBOS Management Console](https://console.dbos.dev/). Make sure you first register an app named `dbos-code-puppy` on the console to generate a Conductor key. Default: `None`.
|
|
194
|
+
- `DBOS_LOG_LEVEL`: Logging verbosity: `CRITICAL`, `ERROR`, `WARNING`, `INFO`, or `DEBUG`. Default: `ERROR`.
|
|
195
|
+
- `DBOS_SYSTEM_DATABASE_URL`: Database URL used by DBOS. Can point to a local SQLite file or a Postgres instance. Example: `postgresql://postgres:dbos@localhost:5432/postgres`. Default: `dbos_store.sqlite` file in the config directory.
|
|
196
|
+
- `DBOS_APP_VERSION`: If set, Code Puppy uses it as the [DBOS application version](https://docs.dbos.dev/architecture#application-and-workflow-versions) and automatically tries to recover pending workflows for this version. Default: Code Puppy version + Unix timestamp in millisecond (disable automatic recovery).
|
|
197
|
+
|
|
176
198
|
### Custom Commands
|
|
177
199
|
Create markdown files in `.claude/commands/`, `.github/prompts/`, or `.agents/commands/` to define custom slash commands. The filename becomes the command name and the content runs as a prompt.
|
|
178
200
|
|
|
@@ -133,6 +133,27 @@ These providers are automatically configured with correct OpenAI-compatible endp
|
|
|
133
133
|
- **⚠️ Unsupported Providers** - Providers like Amazon Bedrock and Google Vertex that require special authentication are clearly marked
|
|
134
134
|
- **⚠️ No Tool Calling** - Models without tool calling support show a big warning since they can't use Code Puppy's file/shell tools
|
|
135
135
|
|
|
136
|
+
### Durable Execution
|
|
137
|
+
|
|
138
|
+
Code Puppy now supports **[DBOS](https://github.com/dbos-inc/dbos-transact-py)** durable execution.
|
|
139
|
+
|
|
140
|
+
When enabled, every agent is automatically wrapped as a `DBOSAgent`, checkpointing key interactions (including agent inputs, LLM responses, MCP calls, and tool calls) in a database for durability and recovery.
|
|
141
|
+
|
|
142
|
+
You can toggle DBOS via either of these options:
|
|
143
|
+
|
|
144
|
+
- CLI config (persists): `/set enable_dbos true` (or `false` to disable)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
Config takes precedence if set; otherwise the environment variable is used.
|
|
148
|
+
|
|
149
|
+
### Configuration
|
|
150
|
+
|
|
151
|
+
The following environment variables control DBOS behavior:
|
|
152
|
+
- `DBOS_CONDUCTOR_KEY`: If set, Code Puppy connects to the [DBOS Management Console](https://console.dbos.dev/). Make sure you first register an app named `dbos-code-puppy` on the console to generate a Conductor key. Default: `None`.
|
|
153
|
+
- `DBOS_LOG_LEVEL`: Logging verbosity: `CRITICAL`, `ERROR`, `WARNING`, `INFO`, or `DEBUG`. Default: `ERROR`.
|
|
154
|
+
- `DBOS_SYSTEM_DATABASE_URL`: Database URL used by DBOS. Can point to a local SQLite file or a Postgres instance. Example: `postgresql://postgres:dbos@localhost:5432/postgres`. Default: `dbos_store.sqlite` file in the config directory.
|
|
155
|
+
- `DBOS_APP_VERSION`: If set, Code Puppy uses it as the [DBOS application version](https://docs.dbos.dev/architecture#application-and-workflow-versions) and automatically tries to recover pending workflows for this version. Default: Code Puppy version + Unix timestamp in millisecond (disable automatic recovery).
|
|
156
|
+
|
|
136
157
|
### Custom Commands
|
|
137
158
|
Create markdown files in `.claude/commands/`, `.github/prompts/`, or `.agents/commands/` to define custom slash commands. The filename becomes the command name and the content runs as a prompt.
|
|
138
159
|
|
|
@@ -24,6 +24,7 @@ from typing import (
|
|
|
24
24
|
import mcp
|
|
25
25
|
import pydantic
|
|
26
26
|
import pydantic_ai.models
|
|
27
|
+
from dbos import DBOS, SetWorkflowID
|
|
27
28
|
from pydantic_ai import Agent as PydanticAgent
|
|
28
29
|
from pydantic_ai import (
|
|
29
30
|
BinaryContent,
|
|
@@ -34,6 +35,7 @@ from pydantic_ai import (
|
|
|
34
35
|
UsageLimitExceeded,
|
|
35
36
|
UsageLimits,
|
|
36
37
|
)
|
|
38
|
+
from pydantic_ai.durable_exec.dbos import DBOSAgent
|
|
37
39
|
from pydantic_ai.messages import (
|
|
38
40
|
ModelMessage,
|
|
39
41
|
ModelRequest,
|
|
@@ -54,6 +56,7 @@ from code_puppy.config import (
|
|
|
54
56
|
get_global_model_name,
|
|
55
57
|
get_message_limit,
|
|
56
58
|
get_protected_token_count,
|
|
59
|
+
get_use_dbos,
|
|
57
60
|
get_value,
|
|
58
61
|
)
|
|
59
62
|
from code_puppy.error_logging import log_error
|
|
@@ -1209,25 +1212,56 @@ class BaseAgent(ABC):
|
|
|
1209
1212
|
|
|
1210
1213
|
self._last_model_name = resolved_model_name
|
|
1211
1214
|
# expose for run_with_mcp
|
|
1215
|
+
# Wrap it with DBOS, but handle MCP servers separately to avoid serialization issues
|
|
1212
1216
|
global _reload_count
|
|
1213
1217
|
_reload_count += 1
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1218
|
+
if get_use_dbos():
|
|
1219
|
+
# Don't pass MCP servers to the agent constructor when using DBOS
|
|
1220
|
+
# This prevents the "cannot pickle async_generator object" error
|
|
1221
|
+
# MCP servers will be handled separately in run_with_mcp
|
|
1222
|
+
agent_without_mcp = PydanticAgent(
|
|
1223
|
+
model=model,
|
|
1224
|
+
instructions=instructions,
|
|
1225
|
+
output_type=str,
|
|
1226
|
+
retries=3,
|
|
1227
|
+
toolsets=[], # Don't include MCP servers here
|
|
1228
|
+
history_processors=[self.message_history_accumulator],
|
|
1229
|
+
model_settings=model_settings,
|
|
1230
|
+
)
|
|
1231
|
+
|
|
1232
|
+
# Register regular tools (non-MCP) on the new agent
|
|
1233
|
+
agent_tools = self.get_available_tools()
|
|
1234
|
+
register_tools_for_agent(agent_without_mcp, agent_tools)
|
|
1235
|
+
|
|
1236
|
+
# Wrap with DBOS
|
|
1237
|
+
dbos_agent = DBOSAgent(
|
|
1238
|
+
agent_without_mcp, name=f"{self.name}-{_reload_count}"
|
|
1239
|
+
)
|
|
1240
|
+
self.pydantic_agent = dbos_agent
|
|
1241
|
+
self._code_generation_agent = dbos_agent
|
|
1227
1242
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1243
|
+
# Store filtered MCP servers separately for runtime use
|
|
1244
|
+
self._mcp_servers = filtered_mcp_servers
|
|
1245
|
+
else:
|
|
1246
|
+
# Normal path without DBOS - include filtered MCP servers in the agent
|
|
1247
|
+
# Re-create agent with filtered MCP servers
|
|
1248
|
+
p_agent = PydanticAgent(
|
|
1249
|
+
model=model,
|
|
1250
|
+
instructions=instructions,
|
|
1251
|
+
output_type=str,
|
|
1252
|
+
retries=3,
|
|
1253
|
+
toolsets=filtered_mcp_servers,
|
|
1254
|
+
history_processors=[self.message_history_accumulator],
|
|
1255
|
+
model_settings=model_settings,
|
|
1256
|
+
)
|
|
1257
|
+
# Register regular tools on the agent
|
|
1258
|
+
agent_tools = self.get_available_tools()
|
|
1259
|
+
register_tools_for_agent(p_agent, agent_tools)
|
|
1260
|
+
|
|
1261
|
+
self.pydantic_agent = p_agent
|
|
1262
|
+
self._code_generation_agent = p_agent
|
|
1263
|
+
self._mcp_servers = filtered_mcp_servers
|
|
1264
|
+
self._mcp_servers = mcp_servers
|
|
1231
1265
|
return self._code_generation_agent
|
|
1232
1266
|
|
|
1233
1267
|
def _create_agent_with_output_type(self, output_type: Type[Any]) -> PydanticAgent:
|
|
@@ -1241,7 +1275,7 @@ class BaseAgent(ABC):
|
|
|
1241
1275
|
output_type: The Pydantic model or type for structured output.
|
|
1242
1276
|
|
|
1243
1277
|
Returns:
|
|
1244
|
-
A configured PydanticAgent with the custom output_type.
|
|
1278
|
+
A configured PydanticAgent (or DBOSAgent wrapper) with the custom output_type.
|
|
1245
1279
|
"""
|
|
1246
1280
|
from code_puppy.model_utils import prepare_prompt_for_model
|
|
1247
1281
|
from code_puppy.tools import register_tools_for_agent
|
|
@@ -1268,19 +1302,38 @@ class BaseAgent(ABC):
|
|
|
1268
1302
|
global _reload_count
|
|
1269
1303
|
_reload_count += 1
|
|
1270
1304
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1305
|
+
if get_use_dbos():
|
|
1306
|
+
temp_agent = PydanticAgent(
|
|
1307
|
+
model=model,
|
|
1308
|
+
instructions=instructions,
|
|
1309
|
+
output_type=output_type,
|
|
1310
|
+
retries=3,
|
|
1311
|
+
toolsets=[],
|
|
1312
|
+
history_processors=[self.message_history_accumulator],
|
|
1313
|
+
model_settings=model_settings,
|
|
1314
|
+
)
|
|
1315
|
+
agent_tools = self.get_available_tools()
|
|
1316
|
+
register_tools_for_agent(temp_agent, agent_tools)
|
|
1317
|
+
dbos_agent = DBOSAgent(
|
|
1318
|
+
temp_agent, name=f"{self.name}-structured-{_reload_count}"
|
|
1319
|
+
)
|
|
1320
|
+
return dbos_agent
|
|
1321
|
+
else:
|
|
1322
|
+
temp_agent = PydanticAgent(
|
|
1323
|
+
model=model,
|
|
1324
|
+
instructions=instructions,
|
|
1325
|
+
output_type=output_type,
|
|
1326
|
+
retries=3,
|
|
1327
|
+
toolsets=mcp_servers,
|
|
1328
|
+
history_processors=[self.message_history_accumulator],
|
|
1329
|
+
model_settings=model_settings,
|
|
1330
|
+
)
|
|
1331
|
+
agent_tools = self.get_available_tools()
|
|
1332
|
+
register_tools_for_agent(temp_agent, agent_tools)
|
|
1333
|
+
return temp_agent
|
|
1283
1334
|
|
|
1335
|
+
# It's okay to decorate it with DBOS.step even if not using DBOS; the decorator is a no-op in that case.
|
|
1336
|
+
@DBOS.step()
|
|
1284
1337
|
def message_history_accumulator(self, ctx: RunContext, messages: List[Any]):
|
|
1285
1338
|
_message_history = self.get_message_history()
|
|
1286
1339
|
message_history_hashes = set([self.hash_message(m) for m in _message_history])
|
|
@@ -1788,14 +1841,49 @@ class BaseAgent(ABC):
|
|
|
1788
1841
|
|
|
1789
1842
|
usage_limits = UsageLimits(request_limit=get_message_limit())
|
|
1790
1843
|
|
|
1791
|
-
# MCP servers
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1844
|
+
# Handle MCP servers - add them temporarily when using DBOS
|
|
1845
|
+
if (
|
|
1846
|
+
get_use_dbos()
|
|
1847
|
+
and hasattr(self, "_mcp_servers")
|
|
1848
|
+
and self._mcp_servers
|
|
1849
|
+
):
|
|
1850
|
+
# Temporarily add MCP servers to the DBOS agent using internal _toolsets
|
|
1851
|
+
original_toolsets = pydantic_agent._toolsets
|
|
1852
|
+
pydantic_agent._toolsets = original_toolsets + self._mcp_servers
|
|
1853
|
+
pydantic_agent._toolsets = original_toolsets + self._mcp_servers
|
|
1854
|
+
|
|
1855
|
+
try:
|
|
1856
|
+
# Set the workflow ID for DBOS context so DBOS and Code Puppy ID match
|
|
1857
|
+
with SetWorkflowID(group_id):
|
|
1858
|
+
result_ = await pydantic_agent.run(
|
|
1859
|
+
prompt_payload,
|
|
1860
|
+
message_history=self.get_message_history(),
|
|
1861
|
+
usage_limits=usage_limits,
|
|
1862
|
+
event_stream_handler=self._event_stream_handler,
|
|
1863
|
+
**kwargs,
|
|
1864
|
+
)
|
|
1865
|
+
finally:
|
|
1866
|
+
# Always restore original toolsets
|
|
1867
|
+
pydantic_agent._toolsets = original_toolsets
|
|
1868
|
+
elif get_use_dbos():
|
|
1869
|
+
# DBOS without MCP servers
|
|
1870
|
+
with SetWorkflowID(group_id):
|
|
1871
|
+
result_ = await pydantic_agent.run(
|
|
1872
|
+
prompt_payload,
|
|
1873
|
+
message_history=self.get_message_history(),
|
|
1874
|
+
usage_limits=usage_limits,
|
|
1875
|
+
event_stream_handler=self._event_stream_handler,
|
|
1876
|
+
**kwargs,
|
|
1877
|
+
)
|
|
1878
|
+
else:
|
|
1879
|
+
# Non-DBOS path (MCP servers are already included)
|
|
1880
|
+
result_ = await pydantic_agent.run(
|
|
1881
|
+
prompt_payload,
|
|
1882
|
+
message_history=self.get_message_history(),
|
|
1883
|
+
usage_limits=usage_limits,
|
|
1884
|
+
event_stream_handler=self._event_stream_handler,
|
|
1885
|
+
**kwargs,
|
|
1886
|
+
)
|
|
1799
1887
|
return result_
|
|
1800
1888
|
except* UsageLimitExceeded as ule:
|
|
1801
1889
|
emit_info(f"Usage limit exceeded: {str(ule)}", group_id=group_id)
|
|
@@ -1811,8 +1899,12 @@ class BaseAgent(ABC):
|
|
|
1811
1899
|
)
|
|
1812
1900
|
except* asyncio.exceptions.CancelledError:
|
|
1813
1901
|
emit_info("Cancelled")
|
|
1902
|
+
if get_use_dbos():
|
|
1903
|
+
await DBOS.cancel_workflow_async(group_id)
|
|
1814
1904
|
except* InterruptedError as ie:
|
|
1815
1905
|
emit_info(f"Interrupted: {str(ie)}")
|
|
1906
|
+
if get_use_dbos():
|
|
1907
|
+
await DBOS.cancel_workflow_async(group_id)
|
|
1816
1908
|
except* Exception as other_error:
|
|
1817
1909
|
# Filter out CancelledError and UsageLimitExceeded from the exception group - let it propagate
|
|
1818
1910
|
remaining_exceptions = []
|
|
@@ -12,9 +12,11 @@ import argparse
|
|
|
12
12
|
import asyncio
|
|
13
13
|
import os
|
|
14
14
|
import sys
|
|
15
|
+
import time
|
|
15
16
|
import traceback
|
|
16
17
|
from pathlib import Path
|
|
17
18
|
|
|
19
|
+
from dbos import DBOS, DBOSConfig
|
|
18
20
|
from rich.console import Console
|
|
19
21
|
|
|
20
22
|
from code_puppy import __version__, callbacks, plugins
|
|
@@ -24,8 +26,10 @@ from code_puppy.command_line.clipboard import get_clipboard_manager
|
|
|
24
26
|
from code_puppy.config import (
|
|
25
27
|
AUTOSAVE_DIR,
|
|
26
28
|
COMMAND_HISTORY_FILE,
|
|
29
|
+
DBOS_DATABASE_URL,
|
|
27
30
|
ensure_config_exists,
|
|
28
31
|
finalize_autosave_session,
|
|
32
|
+
get_use_dbos,
|
|
29
33
|
initialize_command_history_file,
|
|
30
34
|
save_command_to_history,
|
|
31
35
|
)
|
|
@@ -283,6 +287,33 @@ async def main():
|
|
|
283
287
|
|
|
284
288
|
await callbacks.on_startup()
|
|
285
289
|
|
|
290
|
+
# Initialize DBOS if not disabled
|
|
291
|
+
if get_use_dbos():
|
|
292
|
+
# Append a Unix timestamp in ms to the version for uniqueness
|
|
293
|
+
dbos_app_version = os.environ.get(
|
|
294
|
+
"DBOS_APP_VERSION", f"{current_version}-{int(time.time() * 1000)}"
|
|
295
|
+
)
|
|
296
|
+
dbos_config: DBOSConfig = {
|
|
297
|
+
"name": "dbos-code-puppy",
|
|
298
|
+
"system_database_url": DBOS_DATABASE_URL,
|
|
299
|
+
"run_admin_server": False,
|
|
300
|
+
"conductor_key": os.environ.get(
|
|
301
|
+
"DBOS_CONDUCTOR_KEY"
|
|
302
|
+
), # Optional, if set in env, connect to conductor
|
|
303
|
+
"log_level": os.environ.get(
|
|
304
|
+
"DBOS_LOG_LEVEL", "ERROR"
|
|
305
|
+
), # Default to ERROR level to suppress verbose logs
|
|
306
|
+
"application_version": dbos_app_version, # Match DBOS app version to Code Puppy version
|
|
307
|
+
}
|
|
308
|
+
try:
|
|
309
|
+
DBOS(config=dbos_config)
|
|
310
|
+
DBOS.launch()
|
|
311
|
+
except Exception as e:
|
|
312
|
+
emit_error(f"Error initializing DBOS: {e}")
|
|
313
|
+
sys.exit(1)
|
|
314
|
+
else:
|
|
315
|
+
pass
|
|
316
|
+
|
|
286
317
|
global shutdown_flag
|
|
287
318
|
shutdown_flag = False
|
|
288
319
|
try:
|
|
@@ -307,6 +338,8 @@ async def main():
|
|
|
307
338
|
if bus_renderer:
|
|
308
339
|
bus_renderer.stop()
|
|
309
340
|
await callbacks.on_shutdown()
|
|
341
|
+
if get_use_dbos():
|
|
342
|
+
DBOS.destroy()
|
|
310
343
|
|
|
311
344
|
|
|
312
345
|
async def interactive_mode(message_renderer, initial_command: str = None) -> None:
|
|
@@ -874,6 +907,8 @@ def main_entry():
|
|
|
874
907
|
except KeyboardInterrupt:
|
|
875
908
|
# Note: Using sys.stderr for crash output - messaging system may not be available
|
|
876
909
|
sys.stderr.write(traceback.format_exc())
|
|
910
|
+
if get_use_dbos():
|
|
911
|
+
DBOS.destroy()
|
|
877
912
|
return 0
|
|
878
913
|
finally:
|
|
879
914
|
# Reset terminal on Unix-like systems (not Windows)
|
|
@@ -17,6 +17,7 @@ from prompt_toolkit.layout import Dimension, Layout, VSplit, Window
|
|
|
17
17
|
from prompt_toolkit.layout.controls import FormattedTextControl
|
|
18
18
|
from prompt_toolkit.widgets import Frame
|
|
19
19
|
|
|
20
|
+
from code_puppy.command_line.utils import safe_input
|
|
20
21
|
from code_puppy.config import EXTRA_MODELS_FILE, set_config_value
|
|
21
22
|
from code_puppy.messaging import emit_error, emit_info, emit_warning
|
|
22
23
|
from code_puppy.models_dev_parser import ModelInfo, ModelsDevRegistry, ProviderInfo
|
|
@@ -724,8 +725,8 @@ class AddModelMenu:
|
|
|
724
725
|
emit_info(f" {hint}")
|
|
725
726
|
|
|
726
727
|
try:
|
|
727
|
-
# Use
|
|
728
|
-
value =
|
|
728
|
+
# Use safe_input for cross-platform compatibility (Windows fix)
|
|
729
|
+
value = safe_input(f" Enter {env_var} (or press Enter to skip): ")
|
|
729
730
|
|
|
730
731
|
if not value:
|
|
731
732
|
emit_warning(
|
|
@@ -785,7 +786,7 @@ class AddModelMenu:
|
|
|
785
786
|
)
|
|
786
787
|
|
|
787
788
|
try:
|
|
788
|
-
model_name =
|
|
789
|
+
model_name = safe_input(" Model ID: ")
|
|
789
790
|
|
|
790
791
|
if not model_name:
|
|
791
792
|
emit_warning("No model name provided, cancelled.")
|
|
@@ -795,7 +796,7 @@ class AddModelMenu:
|
|
|
795
796
|
emit_info("\n Enter the context window size (in tokens).")
|
|
796
797
|
emit_info(" Common sizes: 8192, 32768, 128000, 200000, 1000000\n")
|
|
797
798
|
|
|
798
|
-
context_input =
|
|
799
|
+
context_input = safe_input(" Context size [128000]: ")
|
|
799
800
|
|
|
800
801
|
if not context_input:
|
|
801
802
|
context_length = 128000 # Default
|
|
@@ -1045,11 +1046,9 @@ class AddModelMenu:
|
|
|
1045
1046
|
f" It will be very limited for coding tasks."
|
|
1046
1047
|
)
|
|
1047
1048
|
try:
|
|
1048
|
-
confirm = (
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
.lower()
|
|
1052
|
-
)
|
|
1049
|
+
confirm = safe_input(
|
|
1050
|
+
"\n Are you sure you want to add this model? (y/N): "
|
|
1051
|
+
).lower()
|
|
1053
1052
|
if confirm not in ("y", "yes"):
|
|
1054
1053
|
emit_info("Model addition cancelled.")
|
|
1055
1054
|
return False
|
|
@@ -43,6 +43,7 @@ def handle_show_command(command: str) -> bool:
|
|
|
43
43
|
get_protected_token_count,
|
|
44
44
|
get_puppy_name,
|
|
45
45
|
get_temperature,
|
|
46
|
+
get_use_dbos,
|
|
46
47
|
get_yolo_mode,
|
|
47
48
|
)
|
|
48
49
|
from code_puppy.keymap import get_cancel_agent_display_name
|
|
@@ -71,6 +72,7 @@ def handle_show_command(command: str) -> bool:
|
|
|
71
72
|
[bold]default_agent:[/bold] [cyan]{default_agent}[/cyan]
|
|
72
73
|
[bold]model:[/bold] [green]{model}[/green]
|
|
73
74
|
[bold]YOLO_MODE:[/bold] {"[red]ON[/red]" if yolo_mode else "[yellow]off[/yellow]"}
|
|
75
|
+
[bold]DBOS:[/bold] {"[green]enabled[/green]" if get_use_dbos() else "[yellow]disabled[/yellow]"} (toggle: /set enable_dbos true|false)
|
|
74
76
|
[bold]auto_save_session:[/bold] {"[green]enabled[/green]" if auto_save else "[yellow]disabled[/yellow]"}
|
|
75
77
|
[bold]protected_tokens:[/bold] [cyan]{protected_tokens:,}[/cyan] recent tokens preserved
|
|
76
78
|
[bold]compaction_threshold:[/bold] [cyan]{compaction_threshold:.1%}[/cyan] context usage triggers compaction
|
|
@@ -211,6 +213,14 @@ def handle_set_command(command: str) -> bool:
|
|
|
211
213
|
)
|
|
212
214
|
return True
|
|
213
215
|
if key:
|
|
216
|
+
# Check if we're toggling DBOS enablement
|
|
217
|
+
if key == "enable_dbos":
|
|
218
|
+
emit_info(
|
|
219
|
+
Text.from_markup(
|
|
220
|
+
"[yellow]⚠️ DBOS configuration changed. Please restart Code Puppy for this change to take effect.[/yellow]"
|
|
221
|
+
)
|
|
222
|
+
)
|
|
223
|
+
|
|
214
224
|
# Validate cancel_agent_key before setting
|
|
215
225
|
if key == "cancel_agent_key":
|
|
216
226
|
from code_puppy.keymap import VALID_CANCEL_KEYS
|
{code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/catalog_server_installer.py
RENAMED
|
@@ -7,6 +7,7 @@ MCP servers from the catalog.
|
|
|
7
7
|
import os
|
|
8
8
|
from typing import Dict, Optional
|
|
9
9
|
|
|
10
|
+
from code_puppy.command_line.utils import safe_input
|
|
10
11
|
from code_puppy.messaging import emit_info, emit_success, emit_warning
|
|
11
12
|
|
|
12
13
|
# Helpful hints for common environment variables
|
|
@@ -52,7 +53,7 @@ def prompt_for_server_config(manager, server) -> Optional[Dict]:
|
|
|
52
53
|
# Get custom name
|
|
53
54
|
default_name = server.name
|
|
54
55
|
try:
|
|
55
|
-
name_input =
|
|
56
|
+
name_input = safe_input(f" Server name [{default_name}]: ")
|
|
56
57
|
server_name = name_input if name_input else default_name
|
|
57
58
|
except (KeyboardInterrupt, EOFError):
|
|
58
59
|
emit_info("")
|
|
@@ -63,9 +64,7 @@ def prompt_for_server_config(manager, server) -> Optional[Dict]:
|
|
|
63
64
|
existing = find_server_id_by_name(manager, server_name)
|
|
64
65
|
if existing:
|
|
65
66
|
try:
|
|
66
|
-
override =
|
|
67
|
-
f" Server '{server_name}' exists. Override? [y/N]: "
|
|
68
|
-
).strip()
|
|
67
|
+
override = safe_input(f" Server '{server_name}' exists. Override? [y/N]: ")
|
|
69
68
|
if not override.lower().startswith("y"):
|
|
70
69
|
emit_warning("Installation cancelled")
|
|
71
70
|
return None
|
|
@@ -91,7 +90,7 @@ def prompt_for_server_config(manager, server) -> Optional[Dict]:
|
|
|
91
90
|
hint = get_env_var_hint(var)
|
|
92
91
|
if hint:
|
|
93
92
|
emit_info(f" {hint}")
|
|
94
|
-
value =
|
|
93
|
+
value = safe_input(f" Enter {var}: ")
|
|
95
94
|
if value:
|
|
96
95
|
env_vars[var] = value
|
|
97
96
|
# Save to config for future use
|
|
@@ -119,7 +118,7 @@ def prompt_for_server_config(manager, server) -> Optional[Dict]:
|
|
|
119
118
|
prompt_str += " (optional)"
|
|
120
119
|
|
|
121
120
|
try:
|
|
122
|
-
value =
|
|
121
|
+
value = safe_input(f"{prompt_str}: ")
|
|
123
122
|
if value:
|
|
124
123
|
cmd_args[name] = value
|
|
125
124
|
elif default:
|
{code_puppy-0.0.344 → code_puppy-0.0.346}/code_puppy/command_line/mcp/custom_server_installer.py
RENAMED
|
@@ -7,6 +7,7 @@ custom MCP servers with JSON configuration.
|
|
|
7
7
|
import json
|
|
8
8
|
import os
|
|
9
9
|
|
|
10
|
+
from code_puppy.command_line.utils import safe_input
|
|
10
11
|
from code_puppy.messaging import emit_error, emit_info, emit_success, emit_warning
|
|
11
12
|
|
|
12
13
|
# Example configurations for each server type
|
|
@@ -58,7 +59,7 @@ def prompt_and_install_custom_server(manager) -> bool:
|
|
|
58
59
|
|
|
59
60
|
# Get server name
|
|
60
61
|
try:
|
|
61
|
-
server_name =
|
|
62
|
+
server_name = safe_input(" Server name: ")
|
|
62
63
|
if not server_name:
|
|
63
64
|
emit_warning("Server name is required")
|
|
64
65
|
return False
|
|
@@ -71,9 +72,7 @@ def prompt_and_install_custom_server(manager) -> bool:
|
|
|
71
72
|
existing = find_server_id_by_name(manager, server_name)
|
|
72
73
|
if existing:
|
|
73
74
|
try:
|
|
74
|
-
override =
|
|
75
|
-
f" Server '{server_name}' exists. Override? [y/N]: "
|
|
76
|
-
).strip()
|
|
75
|
+
override = safe_input(f" Server '{server_name}' exists. Override? [y/N]: ")
|
|
77
76
|
if not override.lower().startswith("y"):
|
|
78
77
|
emit_warning("Cancelled")
|
|
79
78
|
return False
|
|
@@ -89,7 +88,7 @@ def prompt_and_install_custom_server(manager) -> bool:
|
|
|
89
88
|
emit_info(" 3. 📡 sse - Server-Sent Events\n")
|
|
90
89
|
|
|
91
90
|
try:
|
|
92
|
-
type_choice =
|
|
91
|
+
type_choice = safe_input(" Enter choice [1-3]: ")
|
|
93
92
|
except (KeyboardInterrupt, EOFError):
|
|
94
93
|
emit_info("")
|
|
95
94
|
emit_warning("Cancelled")
|
|
@@ -115,8 +114,8 @@ def prompt_and_install_custom_server(manager) -> bool:
|
|
|
115
114
|
empty_count = 0
|
|
116
115
|
try:
|
|
117
116
|
while True:
|
|
118
|
-
line =
|
|
119
|
-
if line
|
|
117
|
+
line = safe_input("")
|
|
118
|
+
if line == "":
|
|
120
119
|
empty_count += 1
|
|
121
120
|
if empty_count >= 2:
|
|
122
121
|
break
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
from rich.table import Table
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def list_directory(path: str = None) -> Tuple[List[str], List[str]]:
|
|
8
|
+
"""
|
|
9
|
+
Returns (dirs, files) for the specified path, splitting out directories and files.
|
|
10
|
+
"""
|
|
11
|
+
if path is None:
|
|
12
|
+
path = os.getcwd()
|
|
13
|
+
entries = []
|
|
14
|
+
try:
|
|
15
|
+
entries = [e for e in os.listdir(path)]
|
|
16
|
+
except Exception as e:
|
|
17
|
+
raise RuntimeError(f"Error listing directory: {e}")
|
|
18
|
+
dirs = [e for e in entries if os.path.isdir(os.path.join(path, e))]
|
|
19
|
+
files = [e for e in entries if not os.path.isdir(os.path.join(path, e))]
|
|
20
|
+
return dirs, files
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def make_directory_table(path: str = None) -> Table:
|
|
24
|
+
"""
|
|
25
|
+
Returns a rich.Table object containing the directory listing.
|
|
26
|
+
"""
|
|
27
|
+
if path is None:
|
|
28
|
+
path = os.getcwd()
|
|
29
|
+
dirs, files = list_directory(path)
|
|
30
|
+
table = Table(
|
|
31
|
+
title=f"\U0001f4c1 [bold blue]Current directory:[/bold blue] [cyan]{path}[/cyan]"
|
|
32
|
+
)
|
|
33
|
+
table.add_column("Type", style="dim", width=8)
|
|
34
|
+
table.add_column("Name", style="bold")
|
|
35
|
+
for d in sorted(dirs):
|
|
36
|
+
table.add_row("[green]dir[/green]", f"[cyan]{d}[/cyan]")
|
|
37
|
+
for f in sorted(files):
|
|
38
|
+
table.add_row("[yellow]file[/yellow]", f"{f}")
|
|
39
|
+
return table
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _reset_windows_console() -> None:
|
|
43
|
+
"""Reset Windows console to normal input mode.
|
|
44
|
+
|
|
45
|
+
After a prompt_toolkit Application exits on Windows, the console can be
|
|
46
|
+
left in a weird state where Enter doesn't work properly. This resets it.
|
|
47
|
+
"""
|
|
48
|
+
import sys
|
|
49
|
+
|
|
50
|
+
if sys.platform != "win32":
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
import ctypes
|
|
55
|
+
|
|
56
|
+
kernel32 = ctypes.windll.kernel32
|
|
57
|
+
# Get handle to stdin
|
|
58
|
+
STD_INPUT_HANDLE = -10
|
|
59
|
+
handle = kernel32.GetStdHandle(STD_INPUT_HANDLE)
|
|
60
|
+
|
|
61
|
+
# Enable line input and echo (normal console mode)
|
|
62
|
+
# ENABLE_LINE_INPUT = 0x0002
|
|
63
|
+
# ENABLE_ECHO_INPUT = 0x0004
|
|
64
|
+
# ENABLE_PROCESSED_INPUT = 0x0001
|
|
65
|
+
NORMAL_MODE = 0x0007 # Line input + echo + processed
|
|
66
|
+
kernel32.SetConsoleMode(handle, NORMAL_MODE)
|
|
67
|
+
except Exception:
|
|
68
|
+
pass # Silently ignore errors - this is best-effort
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def safe_input(prompt_text: str = "") -> str:
|
|
72
|
+
"""Cross-platform safe input that works after prompt_toolkit Applications.
|
|
73
|
+
|
|
74
|
+
On Windows, raw input() can fail after a prompt_toolkit Application exits
|
|
75
|
+
because the terminal can be left in a weird state. This function resets
|
|
76
|
+
the Windows console mode before calling input().
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
prompt_text: The prompt to display to the user
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
The user's input string (stripped)
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
KeyboardInterrupt: If user presses Ctrl+C
|
|
86
|
+
EOFError: If user presses Ctrl+D/Ctrl+Z
|
|
87
|
+
"""
|
|
88
|
+
# Reset Windows console to normal mode before reading input
|
|
89
|
+
_reset_windows_console()
|
|
90
|
+
|
|
91
|
+
# Use standard input() - now that console is reset, it should work
|
|
92
|
+
result = input(prompt_text)
|
|
93
|
+
return result.strip() if result else ""
|