deepagents-code 0.1.12__tar.gz → 0.1.13__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.
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/AGENTS.md +9 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/CHANGELOG.md +23 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/PKG-INFO +1 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/THREAT_MODEL.md +7 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_debug.py +29 -9
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_env_vars.py +37 -7
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_server_config.py +3 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_textual_patches.py +134 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_version.py +1 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/agent.py +92 -22
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/app.py +477 -51
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/config.py +298 -111
- deepagents_code-0.1.13/deepagents_code/config_commands.py +487 -0
- deepagents_code-0.1.13/deepagents_code/config_manifest.py +1055 -0
- deepagents_code-0.1.13/deepagents_code/integrations/sandbox_config.py +198 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/integrations/sandbox_factory.py +74 -71
- deepagents_code-0.1.13/deepagents_code/integrations/sandbox_provider.py +137 -0
- deepagents_code-0.1.13/deepagents_code/integrations/sandbox_registry.py +341 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/local_context.py +85 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/main.py +240 -21
- deepagents_code-0.1.13/deepagents_code/managed_tools.py +470 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/model_config.py +34 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/server.py +25 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/ui.py +60 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/ask_user.py +5 -2
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/chat_input.py +141 -5
- deepagents_code-0.1.13/deepagents_code/widgets/install_confirm.py +120 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/status.py +110 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/theme_selector.py +60 -6
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/welcome.py +24 -175
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/pyproject.toml +1 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_agent.py +223 -9
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_app.py +1082 -59
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_args.py +73 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_charset.py +17 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_chat_input.py +210 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_config.py +428 -0
- deepagents_code-0.1.13/tests/unit_tests/test_config_manifest.py +999 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_debug.py +72 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_install_command.py +127 -4
- deepagents_code-0.1.13/tests/unit_tests/test_install_confirm.py +88 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_local_context.py +179 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_main.py +117 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_main_args.py +347 -0
- deepagents_code-0.1.13/tests/unit_tests/test_managed_tools.py +690 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_model_config.py +23 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_reload.py +11 -1
- deepagents_code-0.1.13/tests/unit_tests/test_sandbox_config.py +140 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_sandbox_factory.py +86 -1
- deepagents_code-0.1.13/tests/unit_tests/test_sandbox_provider.py +50 -0
- deepagents_code-0.1.13/tests/unit_tests/test_sandbox_registry.py +305 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_server_helpers.py +67 -9
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_startup_fast_paths.py +2 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_status.py +207 -5
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_textual_patches.py +115 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_theme.py +455 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_ui.py +34 -1
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_welcome.py +47 -143
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/uv.lock +1 -1
- deepagents_code-0.1.12/deepagents_code/integrations/sandbox_provider.py +0 -71
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/.gitignore +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/COMMANDS.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/DEV.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/Makefile +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/README.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/__init__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/__main__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_ask_user_types.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_cli_context.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_constants.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_git.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_session_stats.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_startup_error.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/_testing_models.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/app.tcss +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/ask_user.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/auth_display.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/auth_store.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/built_in_skills/__init__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/built_in_skills/remember/SKILL.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/built_in_skills/skill-creator/SKILL.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/clipboard.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/command_registry.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/configurable_model.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/default_agent_prompt.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/editor.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/event_bus.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/extras_info.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/file_ops.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/filesystem_empty_result.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/formatting.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/hooks.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/input.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/integrations/__init__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/iterm_cursor_guide.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_auth.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_commands.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_disabled.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_login_service.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_oauth_ui.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_providers/__init__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_providers/_registry.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_providers/base.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_providers/github.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_providers/slack.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_tools.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/mcp_trust.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/media_utils.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/memory_guard.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/non_interactive.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/notifications.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/offload.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/onboarding.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/output.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/project_utils.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/py.typed +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/remote_client.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/resume_state.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/server_graph.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/server_manager.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/sessions.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/skills/__init__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/skills/commands.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/skills/invocation.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/skills/load.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/state_migration.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/subagents.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/system_prompt.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/terminal_capabilities.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/terminal_escape.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/textual_adapter.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/theme.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/tool_display.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/tools.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/unicode_security.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/update_check.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/__init__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/_links.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/agent_selector.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/approval.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/auth.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/autocomplete.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/cwd_switch.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/diff.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/history.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/launch_init.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/loading.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/mcp_login.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/mcp_reconnect.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/mcp_viewer.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/message_store.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/messages.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/model_selector.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/notification_center.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/notification_detail.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/notification_settings.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/restart_prompt.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/thread_selector.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/tool_renderers.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/tool_widgets.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/update_available.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/deepagents_code/widgets/update_progress.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/examples/skills/arxiv-search/SKILL.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/examples/skills/arxiv-search/arxiv_search.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/examples/skills/langgraph-docs/SKILL.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/examples/skills/skill-creator/SKILL.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/examples/skills/web-research/SKILL.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/images/tui.png +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/scripts/check_imports.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/scripts/debug_server.sh +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/scripts/generate_commands_catalog.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/scripts/install.sh +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/README.md +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/integration_tests/__init__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/integration_tests/benchmarks/__init__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/integration_tests/conftest.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/integration_tests/test_acp_mode.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/integration_tests/test_compact_resume.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/integration_tests/test_sandbox_factory.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/integration_tests/test_sandbox_operations.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/__init__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/conftest.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/skills/__init__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/skills/test_commands.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/skills/test_load.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/skills/test_skills_json.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_agent_friendly.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_agent_selector.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_approval.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_ask_user.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_ask_user_middleware.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_auth_display.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_auth_store.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_auth_widgets.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_autocomplete.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_clipboard.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_command_registry.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_compact_tool.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_configurable_model.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_cursor_blink.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_cwd_switch.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_editor.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_end_to_end.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_env_vars.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_event_bus.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_exception_handling.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_extras_info.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_file_ops.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_filesystem_empty_result.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_formatting.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_git.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_history.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_hooks.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_imports.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_input_parsing.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_iterm_cursor_guide.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_launch_init.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_links.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_loading.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_main_acp_mode.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_mcp_auth.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_mcp_commands.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_mcp_disabled.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_mcp_login_modal.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_mcp_login_service.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_mcp_oauth_ui.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_mcp_reconnect.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_mcp_tools.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_mcp_trust.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_mcp_viewer.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_media_utils.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_memory_guard.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_message_store.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_messages.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_model_selector.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_model_switch.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_non_interactive.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_notification_center.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_notification_detail.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_notifications.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_offload.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_offload_dict_messages.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_onboarding.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_output.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_remote_client.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_restart_prompt.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_resume_state.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_server.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_server_config.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_server_graph.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_server_manager.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_session_stats.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_sessions.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_shell_allow_list.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_skill_invocation.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_state_migration.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_subagents.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_terminal_capabilities.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_terminal_escape.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_terminal_progress_preference.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_textual_adapter.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_thread_selector.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_tool_display.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_unicode_security.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_update_available.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_update_check.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_update_progress.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/test_version.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/tools/__init__.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/tools/test_current_thread.py +0 -0
- {deepagents_code-0.1.12 → deepagents_code-0.1.13}/tests/unit_tests/tools/test_fetch_url.py +0 -0
|
@@ -38,6 +38,13 @@ Textual's `App.notify(message)` parses the message string as Rich markup by defa
|
|
|
38
38
|
|
|
39
39
|
`console.print()` defaults to `highlight=True`, which runs `ReprHighlighter` and auto-applies bold + cyan to any detected numbers. This visually overrides subtle styles like `dim` (bold cancels dim in most terminals). Pass `highlight=False` on any `console.print()` call where the content contains numbers and consistent dim/subtle styling matters.
|
|
40
40
|
|
|
41
|
+
### Glyphs and spinners — reuse, don't redefine
|
|
42
|
+
|
|
43
|
+
Charset-dependent characters and animations have **single sources of truth**. Reuse them instead of hand-rolling new ones — copies drift, and (more subtly) a hardcoded Unicode literal won't degrade to ASCII on terminals that need it.
|
|
44
|
+
|
|
45
|
+
- **Glyphs** (checkmarks, arrows, ellipsis, cursor, box-drawing, branch icon, etc.): pull from `get_glyphs()` (`config.Glyphs`). Each glyph has a Unicode and an ASCII variant; `get_glyphs()` returns the right set for the active terminal. Never inline `"✓"`, `"…"`, `"›"`, and friends.
|
|
46
|
+
- **Animated spinners**: reuse the `Spinner` class in `widgets/loading.py`, which wraps `get_glyphs().spinner_frames` (braille on Unicode, `(-) (\) (|) (/)` on ASCII) and exposes `next_frame()`/`current_frame()`. Do **not** define your own frame tuples or interval constants for a spinner — drive a `Spinner` on a `set_interval`. The status-bar reconnect indicator (`widgets/status.py`) is the reference example; it ticks at the same 0.1s cadence as `LoadingWidget`. All connection/queue progress lives in the status bar — the welcome banner deliberately stays out of it, so there are currently no bespoke spinners to carve an exception for.
|
|
47
|
+
|
|
41
48
|
### Textual patterns used in this codebase
|
|
42
49
|
|
|
43
50
|
- **Workers** (`@work` decorator) for async operations - see [Workers guide](https://textual.textualize.io/guide/workers/)
|
|
@@ -140,7 +147,8 @@ To add a new slash command: (1) add a `SlashCommand` entry to `COMMANDS`, (2) se
|
|
|
140
147
|
1. `deepagents_code/model_config.py` — add `"provider_name": "ENV_VAR_NAME"` to `PROVIDER_API_KEY_ENV`
|
|
141
148
|
2. `deepagents_code/model_config.py` — if the provider reads a *dedicated* endpoint env var, add `"provider_name": ("CANONICAL_BASE_URL", "ALTERNATE", ...)` to `PROVIDER_BASE_URL_ENV` (see guidelines below); omit the provider entirely when it has no provider-specific endpoint variable
|
|
142
149
|
3. `pyproject.toml` — add `provider = ["langchain-provider>=X.Y.Z,<N.0.0"]` to `[project.optional-dependencies]` and include it in the `all-providers` composite extra
|
|
143
|
-
4. `
|
|
150
|
+
4. `deepagents_code/model_config.py` — add `"provider_name"` to `RETRY_PARAM_BY_PROVIDER` if the provider's chat model accepts `max_retries`
|
|
151
|
+
5. `tests/unit_tests/test_model_config.py` — add `assert PROVIDER_API_KEY_ENV["provider_name"] == "ENV_VAR_NAME"` to `TestProviderApiKeyEnv.test_contains_major_providers`, and pin any `PROVIDER_BASE_URL_ENV` entry with a matching assertion
|
|
144
152
|
|
|
145
153
|
### `PROVIDER_BASE_URL_ENV` guidelines
|
|
146
154
|
|
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
# Deep Agents Code Changelog
|
|
4
4
|
|
|
5
|
+
## [0.1.13](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.12...deepagents-code==0.1.13) (2026-06-11)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* Pluggable third-party sandbox backends ([#3842](https://github.com/langchain-ai/deepagents/issues/3842)) ([2b635a7](https://github.com/langchain-ai/deepagents/commit/2b635a7e6e6b50ca8ce783c2ac96ed8643ae0224))
|
|
10
|
+
* Auto-install ripgrep on first run ([#3348](https://github.com/langchain-ai/deepagents/issues/3348)) ([fecf22b](https://github.com/langchain-ai/deepagents/commit/fecf22b0909e79ff7bdf180baf20abf5fdf1f390))
|
|
11
|
+
* `config` command and canonical config manifest ([#3763](https://github.com/langchain-ai/deepagents/issues/3763)) ([79899a3](https://github.com/langchain-ai/deepagents/commit/79899a306d01de6217a1dfcc013ae92c808a47a0))
|
|
12
|
+
* Confirm modal for `/install --package` ([#3840](https://github.com/langchain-ai/deepagents/issues/3840)) ([3d75026](https://github.com/langchain-ai/deepagents/commit/3d75026e2f241648fae78d9e1de2cbb4985f58ff))
|
|
13
|
+
* Copy focused input selection on `Ctrl+C` ([#3841](https://github.com/langchain-ai/deepagents/issues/3841)) ([99f782c](https://github.com/langchain-ai/deepagents/commit/99f782cf08336c200d02a24ae4edaa650af67ed2))
|
|
14
|
+
* `[retries]` config ([#3772](https://github.com/langchain-ai/deepagents/issues/3772)) ([9334d91](https://github.com/langchain-ai/deepagents/commit/9334d91ef94997e46d5373daca9c146fa9498763))
|
|
15
|
+
* Show connection state in the status bar ([#3710](https://github.com/langchain-ai/deepagents/issues/3710)) ([3e3e8fe](https://github.com/langchain-ai/deepagents/commit/3e3e8feb0e6e1b77be75a7756fbf32e5c9497c28))
|
|
16
|
+
* Surface LangSmith tracing projects in `LocalContextMiddleware` ([#3836](https://github.com/langchain-ai/deepagents/issues/3836)) ([676abec](https://github.com/langchain-ai/deepagents/commit/676abecf892ff537fcb1425ba5929cace3c5d503))
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* Add debug-log guidance for truncated startup errors ([#3849](https://github.com/langchain-ai/deepagents/issues/3849)) ([cd1ef30](https://github.com/langchain-ai/deepagents/commit/cd1ef303cf5d0e9d746c787eb09a4a89437e965a))
|
|
21
|
+
* Drop lock-key events so Caps Lock in iTerm2 doesn't type ([#3855](https://github.com/langchain-ai/deepagents/issues/3855)) ([110f1a7](https://github.com/langchain-ai/deepagents/commit/110f1a7a975743efda12e181cb3afc8404202254))
|
|
22
|
+
* Hand pointer over splash tracing project link ([#3858](https://github.com/langchain-ai/deepagents/issues/3858)) ([ea7dae5](https://github.com/langchain-ai/deepagents/commit/ea7dae58a37cfb1d2b96544eb7c941aad331b280))
|
|
23
|
+
* Keep terminal-default theme on Esc in `/theme` selector ([#3854](https://github.com/langchain-ai/deepagents/issues/3854)) ([c3bc67b](https://github.com/langchain-ai/deepagents/commit/c3bc67b0cdeda6f4dbcc6360ddd72b455aec4fe7))
|
|
24
|
+
* Preserve inherited `PYTHONPATH` for server subprocess ([#3833](https://github.com/langchain-ai/deepagents/issues/3833)) ([4689569](https://github.com/langchain-ai/deepagents/commit/4689569f94138987319cd9cbb45ce66a1f496934))
|
|
25
|
+
* Resolve interpreter PTC allowlist against the runtime tool registry ([#3845](https://github.com/langchain-ai/deepagents/issues/3845)) ([c59a27e](https://github.com/langchain-ai/deepagents/commit/c59a27ef2405b8e04c4351ce7ffa53d8d16d519c))
|
|
26
|
+
* Treat multi-line key-event pastes as one input ([#3856](https://github.com/langchain-ai/deepagents/issues/3856)) ([6bb15d4](https://github.com/langchain-ai/deepagents/commit/6bb15d4bd97bd16f47504f937c8458d1b53d9cc4))
|
|
27
|
+
|
|
5
28
|
## [0.1.12](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.11...deepagents-code==0.1.12) (2026-06-10)
|
|
6
29
|
|
|
7
30
|
### Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagents-code
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.13
|
|
4
4
|
Summary: Terminal interface for Deep Agents - interactive AI agent with file operations, shell access, and sub-agent capabilities.
|
|
5
5
|
Project-URL: Homepage, https://docs.langchain.com/oss/python/deepagents/overview
|
|
6
6
|
Project-URL: Documentation, https://reference.langchain.com/python/deepagents/
|
|
@@ -409,6 +409,13 @@
|
|
|
409
409
|
- **Description**: The `"env"` field in stdio MCP server definitions (`.mcp.json`) accepts an arbitrary key-value dict. `mcp_tools._validate_server_config` only checks that the field is a dict — it does not filter key names or values. The dict is forwarded directly to `StdioConnection(env=...)` which passes it to the subprocess. An attacker who can modify a project-level `.mcp.json` could set `PATH` to redirect command resolution, `LD_PRELOAD` to inject shared libraries, or `PYTHONPATH` to hijack Python imports in the MCP subprocess.
|
|
410
410
|
- **Preconditions**: (1) Attacker has write access to a project-level `.mcp.json`; (2) The project MCP config must be trusted by the user (fingerprint approval gate via `mcp_trust`). For user-level `~/.deepagents/.mcp.json`, the attacker already has home directory write access. Note: the `env` dict from MCP config is passed to `StdioConnection` — whether it replaces or merges with `os.environ` depends on the `langchain_mcp_adapters` library implementation.
|
|
411
411
|
|
|
412
|
+
#### T11: Auto-Installed ripgrep Binary from Upstream Release
|
|
413
|
+
|
|
414
|
+
- **Flow**: First-run download performed by `managed_tools.ensure_ripgrep` when `rg` is not on `PATH`.
|
|
415
|
+
- **Description**: On first invocation without a system `rg`, Deep Agents Code fetches the pinned ripgrep release tarball from `github.com/BurntSushi/ripgrep/releases/...`, verifies it against an in-tree SHA-256 (`RIPGREP_ASSETS`), extracts it under a `TemporaryDirectory`, and atomically moves the binary into `~/.deepagents/bin/rg`. The binary then runs unsandboxed, inheriting the same trust as a user-installed `rg` (the SDK invokes it via `subprocess.run(["rg", ...])`).
|
|
416
|
+
- **Mitigations**: (1) SHA-256 verified against the pinned hash table before move — a mismatch aborts the install and leaves `BIN_DIR` clean. (2) Network egress is limited to `github.com`. (3) Opt-out via `DEEPAGENTS_CODE_OFFLINE` for air-gapped environments. (4) Pinned version + checksums are bumped in-tree, so a compromised upstream release is detected on the next Deep Agents Code release rather than silently propagating. (5) Atomic move-into-place avoids partial installs when concurrent CLI invocations race.
|
|
417
|
+
- **Preconditions**: User has not installed `rg` via their package manager, `DEEPAGENTS_CODE_OFFLINE` is unset, and the host can reach `github.com`. The pinned SHA-256 in `RIPGREP_ASSETS` would need to be incorrect (a supply-chain compromise of the deepagents-code release) for a tampered binary to be installed.
|
|
418
|
+
|
|
412
419
|
---
|
|
413
420
|
|
|
414
421
|
## Input Source Coverage
|
|
@@ -13,7 +13,12 @@ import os
|
|
|
13
13
|
import sys
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
|
-
from deepagents_code._env_vars import
|
|
16
|
+
from deepagents_code._env_vars import (
|
|
17
|
+
DEBUG,
|
|
18
|
+
DEBUG_FILE,
|
|
19
|
+
DEFAULT_DEBUG_FILE,
|
|
20
|
+
is_env_truthy,
|
|
21
|
+
)
|
|
17
22
|
|
|
18
23
|
_DEBUG_HANDLER_ATTR = "_deepagents_code_debug_handler"
|
|
19
24
|
|
|
@@ -25,8 +30,8 @@ def configure_debug_logging(target: logging.Logger) -> None:
|
|
|
25
30
|
module loggers reach the same file via propagation, so individual modules do
|
|
26
31
|
not configure logging themselves.
|
|
27
32
|
|
|
28
|
-
The log file defaults to `
|
|
29
|
-
|
|
33
|
+
The log file defaults to `DEFAULT_DEBUG_FILE` but can be overridden with
|
|
34
|
+
`DEEPAGENTS_CODE_DEBUG_FILE`. The handler appends (`mode='a'`) so logs
|
|
30
35
|
are preserved across separate process runs. Calling this again with the same
|
|
31
36
|
resolved path is a no-op: the existing tagged handler is reused rather than
|
|
32
37
|
stacking duplicates. If the resolved path changes, the stale handler is
|
|
@@ -40,12 +45,7 @@ def configure_debug_logging(target: logging.Logger) -> None:
|
|
|
40
45
|
if not is_env_truthy(DEBUG):
|
|
41
46
|
return
|
|
42
47
|
|
|
43
|
-
debug_path = Path(
|
|
44
|
-
os.environ.get(
|
|
45
|
-
DEBUG_FILE,
|
|
46
|
-
"/tmp/deepagents_debug.log", # noqa: S108
|
|
47
|
-
)
|
|
48
|
-
)
|
|
48
|
+
debug_path = Path(os.environ.get(DEBUG_FILE, DEFAULT_DEBUG_FILE))
|
|
49
49
|
for existing in list(target.handlers):
|
|
50
50
|
if not (
|
|
51
51
|
isinstance(existing, logging.FileHandler)
|
|
@@ -74,3 +74,23 @@ def configure_debug_logging(target: logging.Logger) -> None:
|
|
|
74
74
|
handler.setFormatter(logging.Formatter("%(asctime)s %(name)s %(message)s"))
|
|
75
75
|
target.addHandler(handler)
|
|
76
76
|
target.setLevel(logging.DEBUG)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def installed_debug_log_path() -> Path | None:
|
|
80
|
+
"""Return the path of the active debug log file, or `None` if not logging.
|
|
81
|
+
|
|
82
|
+
Reflects the file handler actually attached by `configure_debug_logging`,
|
|
83
|
+
not the current `DEEPAGENTS_CODE_DEBUG` env value. The two diverge when the
|
|
84
|
+
variable is set after import — e.g. via a project/global `.env` loaded during
|
|
85
|
+
settings bootstrap — in which case the variable reads truthy but no handler
|
|
86
|
+
was installed and no log file exists. Callers that surface "full error in
|
|
87
|
+
<path>" hints must use this rather than the env var to avoid pointing users
|
|
88
|
+
at a file that was never created.
|
|
89
|
+
"""
|
|
90
|
+
package_logger = logging.getLogger(__package__ or "deepagents_code")
|
|
91
|
+
for handler in package_logger.handlers:
|
|
92
|
+
if isinstance(handler, logging.FileHandler) and getattr(
|
|
93
|
+
handler, _DEBUG_HANDLER_ATTR, False
|
|
94
|
+
):
|
|
95
|
+
return Path(handler.baseFilename)
|
|
96
|
+
return None
|
|
@@ -46,7 +46,10 @@ as enabled, and `0`, `false`, `no`, `off`, empty string, or unset as disabled.
|
|
|
46
46
|
"""
|
|
47
47
|
|
|
48
48
|
DEBUG_FILE = "DEEPAGENTS_CODE_DEBUG_FILE"
|
|
49
|
-
"""Path for the debug log file (default:
|
|
49
|
+
"""Path for the debug log file (default: `DEFAULT_DEBUG_FILE`)."""
|
|
50
|
+
|
|
51
|
+
DEFAULT_DEBUG_FILE = "/tmp/deepagents_debug.log" # noqa: S108 # opt-in debug log
|
|
52
|
+
"""Default path for the debug log when `DEBUG_FILE` is unset."""
|
|
50
53
|
|
|
51
54
|
DEBUG_MCP_PROJECT_TRUST = "DEEPAGENTS_CODE_DEBUG_MCP_PROJECT_TRUST"
|
|
52
55
|
"""Force the project MCP approval prompt for manual UI testing.
|
|
@@ -124,6 +127,14 @@ NO_TERMINAL_ESCAPE = "DEEPAGENTS_CODE_NO_TERMINAL_ESCAPE"
|
|
|
124
127
|
NO_UPDATE_CHECK = "DEEPAGENTS_CODE_NO_UPDATE_CHECK"
|
|
125
128
|
"""Disable automatic update checking when set."""
|
|
126
129
|
|
|
130
|
+
OFFLINE = "DEEPAGENTS_CODE_OFFLINE"
|
|
131
|
+
"""Disable network downloads of managed binaries (e.g. ripgrep).
|
|
132
|
+
|
|
133
|
+
Parsed by `is_env_truthy`: accepts `1`, `true`, `yes`, `on` as enabled. When
|
|
134
|
+
truthy, `managed_tools.ensure_ripgrep` will not attempt to download a binary
|
|
135
|
+
and falls back to the existing missing-tool notification + slow Python regex
|
|
136
|
+
path."""
|
|
137
|
+
|
|
127
138
|
OLLAMA_DISCOVERY = "DEEPAGENTS_CODE_OLLAMA_DISCOVERY"
|
|
128
139
|
"""Toggle Ollama model and profile discovery probes.
|
|
129
140
|
|
|
@@ -164,6 +175,29 @@ _TRUTHY_VALUES = frozenset({"1", "true", "yes", "on"})
|
|
|
164
175
|
_FALSY_VALUES = frozenset({"0", "false", "no", "off", ""})
|
|
165
176
|
|
|
166
177
|
|
|
178
|
+
def classify_env_bool(raw: str) -> bool | None:
|
|
179
|
+
"""Classify a raw env-var string as a truthy, falsy, or unrecognized token.
|
|
180
|
+
|
|
181
|
+
The single source of truth for which strings count as boolean on/off
|
|
182
|
+
values; `is_env_truthy` and the config resolver both build on it so they
|
|
183
|
+
agree on what "recognizably boolean" means.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
raw: The raw (unstripped) environment-variable value.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
`True` for `1`/`true`/`yes`/`on`, `False` for `0`/`false`/`no`/`off`/
|
|
190
|
+
empty string (case-insensitive), or `None` when the value
|
|
191
|
+
is neither.
|
|
192
|
+
"""
|
|
193
|
+
lowered = raw.strip().lower()
|
|
194
|
+
if lowered in _TRUTHY_VALUES:
|
|
195
|
+
return True
|
|
196
|
+
if lowered in _FALSY_VALUES:
|
|
197
|
+
return False
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
|
|
167
201
|
def is_env_truthy(name: str, *, default: bool = False) -> bool:
|
|
168
202
|
"""Return whether env var *name* is set to a recognizably truthy value.
|
|
169
203
|
|
|
@@ -184,9 +218,5 @@ def is_env_truthy(name: str, *, default: bool = False) -> bool:
|
|
|
184
218
|
raw = os.environ.get(name)
|
|
185
219
|
if raw is None:
|
|
186
220
|
return default
|
|
187
|
-
|
|
188
|
-
if
|
|
189
|
-
return True
|
|
190
|
-
if lowered in _FALSY_VALUES:
|
|
191
|
-
return False
|
|
192
|
-
return default
|
|
221
|
+
classified = classify_env_bool(raw)
|
|
222
|
+
return default if classified is None else classified
|
|
@@ -164,7 +164,9 @@ class ServerConfig:
|
|
|
164
164
|
|
|
165
165
|
`None` means "fall through to whatever `settings.interpreter_ptc` resolves
|
|
166
166
|
to from `~/.deepagents/config.toml`". A string is one of `"safe"`/`"all"`;
|
|
167
|
-
a list is an explicit allowlist of tool names
|
|
167
|
+
a list is an explicit allowlist of tool names that may also include the
|
|
168
|
+
`"safe"` preset (expanded at agent-build time); `"all"` is rejected inside
|
|
169
|
+
a list.
|
|
168
170
|
"""
|
|
169
171
|
|
|
170
172
|
interpreter_ptc_acknowledge_unsafe: bool = False
|
|
@@ -13,7 +13,30 @@ upstream.
|
|
|
13
13
|
`alt+enter`. Tracked in Textualize/textual#6378. Remove this patch and
|
|
14
14
|
the Textual pin comment in `pyproject.toml` when that lands.
|
|
15
15
|
|
|
16
|
-
2.
|
|
16
|
+
2. Kitty lock-key and sub-field handling. Two related problems with the
|
|
17
|
+
pinned Textual parser:
|
|
18
|
+
|
|
19
|
+
a. Lock keys (Caps Lock / Num Lock / Scroll Lock) must never produce
|
|
20
|
+
text, but terminals encode them inconsistently. kitty/Ghostty/VS Code
|
|
21
|
+
send the functional key code (`CSI 57358 ... u`) with associated text
|
|
22
|
+
set to the letter the *next* key would have produced. iTerm2 instead
|
|
23
|
+
reports the Caps Lock toggle as a bare upper-case ASCII letter (`CSI
|
|
24
|
+
65 u` → 'A') with no modifier or associated-text field — not a valid
|
|
25
|
+
encoding for a real key press per the kitty spec. Either way the chat
|
|
26
|
+
input would type a stray capital. The patch collapses both forms to a
|
|
27
|
+
single character-less `caps_lock` event, regardless of the modifier,
|
|
28
|
+
associated-text, or event-type sub-fields the terminal includes.
|
|
29
|
+
|
|
30
|
+
b. `_re_extended_key` only accepts `;`-separated numeric fields, so any
|
|
31
|
+
*non-lock* kitty sequence carrying `:`-separated sub-fields — alternate
|
|
32
|
+
keys (`unicode:shifted:base`) or an event-type (`modifiers:event`) —
|
|
33
|
+
fails to match and is re-emitted one byte at a time as literal text.
|
|
34
|
+
The patch strips the `:` sub-fields before Textual parses the sequence
|
|
35
|
+
so it resolves to a single key event.
|
|
36
|
+
|
|
37
|
+
Remove when the pinned Textual neutralizes lock keys and widens its parser.
|
|
38
|
+
|
|
39
|
+
3. Double-click word selection. Stock Textual selects the entire widget on
|
|
17
40
|
a click chain; these patches narrow a double-click (and double-click
|
|
18
41
|
drag) to word boundaries. No upstream issue tracks this yet, so it has
|
|
19
42
|
no removal criterion — it stays until Textual grows native word select.
|
|
@@ -24,6 +47,7 @@ Imported for side effect from `app.py` before any `App()` is created.
|
|
|
24
47
|
from __future__ import annotations
|
|
25
48
|
|
|
26
49
|
import logging
|
|
50
|
+
import re
|
|
27
51
|
from inspect import isawaitable
|
|
28
52
|
from typing import TYPE_CHECKING
|
|
29
53
|
|
|
@@ -60,6 +84,99 @@ try:
|
|
|
60
84
|
except (ImportError, AttributeError) as exc: # pragma: no cover - defensive
|
|
61
85
|
logger.warning("Textual keyboard parser patch skipped: %s", exc)
|
|
62
86
|
else:
|
|
87
|
+
# Kitty functional key codes for the lock keys (Caps Lock, Scroll Lock,
|
|
88
|
+
# Num Lock). The kitty protocol assigns these Private Use Area codepoints;
|
|
89
|
+
# they appear as the leading key-code field of a `CSI ... u` sequence.
|
|
90
|
+
_KITTY_LOCK_KEY_CODES = frozenset({"57358", "57359", "57360"})
|
|
91
|
+
_KITTY_LOCK_KEY_NAMES = {
|
|
92
|
+
"57358": "caps_lock",
|
|
93
|
+
"57359": "scroll_lock",
|
|
94
|
+
"57360": "num_lock",
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Any `CSI <code>[:...][;...] u` sequence. Group 1 is the leading key-code
|
|
98
|
+
# field (before any `:` alternate-key sub-field); `_lock_key_event` checks
|
|
99
|
+
# it against the lock-key set. The match is deliberately broad so the code
|
|
100
|
+
# is extracted regardless of the modifier / associated-text / event-type
|
|
101
|
+
# sub-fields that follow, which iTerm2 and other terminals encode in
|
|
102
|
+
# varying shapes.
|
|
103
|
+
_KITTY_KEY_SEQUENCE = re.compile(r"\x1b\[(\d+)[\d;:]*u")
|
|
104
|
+
|
|
105
|
+
# Kitty extended-key sequence carrying `:` sub-fields (alternate keys or an
|
|
106
|
+
# event-type sub-field). The pinned Textual's `_re_extended_key` rejects the
|
|
107
|
+
# colons, so non-lock keys with these sub-fields would otherwise leak as
|
|
108
|
+
# literal text — strip the sub-fields so they parse to a single key event.
|
|
109
|
+
_KITTY_SUBFIELD_KEY = re.compile(r"\x1b\[[\d;:]*:[\d;:]*[u~ABCDEFHPQRS]")
|
|
110
|
+
|
|
111
|
+
# iTerm2 reports the Caps Lock toggle as a `CSI u` sequence whose primary
|
|
112
|
+
# key code is the *uppercase* ASCII letter that would be produced next
|
|
113
|
+
# (e.g. `CSI 65 u` → 'A'), with no real modifier bits and no associated
|
|
114
|
+
# text. The kitty spec requires the primary code to be the unshifted
|
|
115
|
+
# (lower-case) code point, so a bare upper-case letter here is iTerm2's
|
|
116
|
+
# Caps Lock artifact rather than a real key press. Group 1 is the code
|
|
117
|
+
# point; group 2 the optional modifier field; group 3 the optional text.
|
|
118
|
+
_KITTY_CSI_U = re.compile(
|
|
119
|
+
r"\x1b\[(\d+)(?::\d+)*(?:;(\d+)[\d:]*)?(?:;(\d+)[\d:]*)?u"
|
|
120
|
+
)
|
|
121
|
+
_ASCII_UPPER_A = 65
|
|
122
|
+
_ASCII_UPPER_Z = 90
|
|
123
|
+
# Modifier mask for the "real" modifiers (shift|alt|ctrl|super|hyper|meta);
|
|
124
|
+
# excludes the caps_lock (64) and num_lock (128) lock bits.
|
|
125
|
+
_REAL_MODIFIER_MASK = 0b111111
|
|
126
|
+
|
|
127
|
+
def _spurious_caps_lock(sequence: str) -> bool:
|
|
128
|
+
"""Whether `sequence` is iTerm2's bare Caps Lock toggle report.
|
|
129
|
+
|
|
130
|
+
Matches a `CSI u` key whose primary code point is an upper-case ASCII
|
|
131
|
+
letter with no real modifiers and no associated-text field — which the
|
|
132
|
+
kitty spec never produces for a genuine key press.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
`True` if `sequence` is the spurious Caps Lock toggle report.
|
|
136
|
+
"""
|
|
137
|
+
match = _KITTY_CSI_U.fullmatch(sequence)
|
|
138
|
+
if match is None:
|
|
139
|
+
return False
|
|
140
|
+
code = int(match.group(1))
|
|
141
|
+
if not _ASCII_UPPER_A <= code <= _ASCII_UPPER_Z:
|
|
142
|
+
return False
|
|
143
|
+
modifier_bits = (int(match.group(2)) - 1) if match.group(2) else 0
|
|
144
|
+
has_text = match.group(3) is not None
|
|
145
|
+
return modifier_bits & _REAL_MODIFIER_MASK == 0 and not has_text
|
|
146
|
+
|
|
147
|
+
def _strip_kitty_subfields(sequence: str) -> str:
|
|
148
|
+
"""Drop `:` sub-fields from a kitty extended-key sequence.
|
|
149
|
+
|
|
150
|
+
Keeps the primary value of each `;`-separated field (the unicode key
|
|
151
|
+
code, modifier mask, and associated text), which is all Textual reads.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
The sequence with every `:` sub-field removed.
|
|
155
|
+
"""
|
|
156
|
+
body, terminator = sequence[2:-1], sequence[-1]
|
|
157
|
+
primary = ";".join(field.split(":", 1)[0] for field in body.split(";"))
|
|
158
|
+
return f"\x1b[{primary}{terminator}"
|
|
159
|
+
|
|
160
|
+
def _lock_key_event(sequence: str) -> events.Key | None:
|
|
161
|
+
"""Return a text-free lock-key event for a kitty lock-key sequence.
|
|
162
|
+
|
|
163
|
+
Lock keys must never produce text. Under the kitty protocol with
|
|
164
|
+
associated-text reporting, terminals (notably iTerm2) encode Caps
|
|
165
|
+
Lock as a `CSI 57358 ... u` sequence whose associated-text field is
|
|
166
|
+
the letter the *next* key would have produced — Textual then either
|
|
167
|
+
types that letter or, when `:` sub-fields are present, leaks the raw
|
|
168
|
+
sequence byte by byte. Collapsing any lock-key sequence to a single
|
|
169
|
+
character-less event stops both failure modes at the source, for
|
|
170
|
+
every widget.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
A `Key` event for the lock key, or `None` if `sequence` is not a
|
|
174
|
+
kitty lock-key sequence.
|
|
175
|
+
"""
|
|
176
|
+
match = _KITTY_KEY_SEQUENCE.fullmatch(sequence)
|
|
177
|
+
if match is None or match.group(1) not in _KITTY_LOCK_KEY_CODES:
|
|
178
|
+
return None
|
|
179
|
+
return events.Key(_KITTY_LOCK_KEY_NAMES[match.group(1)], None)
|
|
63
180
|
|
|
64
181
|
def _emit_alt(keys: tuple, character: str | None) -> Iterable[events.Key]:
|
|
65
182
|
for key in keys:
|
|
@@ -68,6 +185,22 @@ else:
|
|
|
68
185
|
def _sequence_to_key_events_with_alt(
|
|
69
186
|
self: XTermParser, sequence: str, alt: bool = False
|
|
70
187
|
) -> Iterable[events.Key]:
|
|
188
|
+
# Lock keys (Caps Lock / Num Lock / Scroll Lock) must never type. Emit
|
|
189
|
+
# a single character-less event regardless of how the terminal encoded
|
|
190
|
+
# the modifiers, associated text, or event-type sub-fields.
|
|
191
|
+
if (lock_event := _lock_key_event(sequence)) is not None:
|
|
192
|
+
yield lock_event
|
|
193
|
+
return
|
|
194
|
+
# iTerm2 reports the Caps Lock toggle as a bare upper-case letter (e.g.
|
|
195
|
+
# `CSI 65 u` → 'A') rather than the kitty `57358` functional code. Drop
|
|
196
|
+
# it so the toggle never types a stray capital into the input.
|
|
197
|
+
if _spurious_caps_lock(sequence):
|
|
198
|
+
yield events.Key("caps_lock", None)
|
|
199
|
+
return
|
|
200
|
+
# Normalize any other kitty sequence with `:` sub-fields so it resolves
|
|
201
|
+
# to a single key event instead of leaking raw bytes.
|
|
202
|
+
if _KITTY_SUBFIELD_KEY.fullmatch(sequence):
|
|
203
|
+
sequence = _strip_kitty_subfields(sequence)
|
|
71
204
|
# Fast path: \x1b<byte> on first pass. Short-circuits the ~100 ms
|
|
72
205
|
# escape-delay wait when both bytes arrive together. Semantic side
|
|
73
206
|
# effect: \x1b\x1b dispatches as `alt+escape` with no delay, matching
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Keep the `x-release-please-version` annotation — release-please uses it to
|
|
4
4
|
# bump `__version__` in sync with `pyproject.toml` on every release PR.
|
|
5
|
-
__version__ = "0.1.
|
|
5
|
+
__version__ = "0.1.13" # x-release-please-version
|
|
6
6
|
|
|
7
7
|
DOCS_URL = "https://docs.langchain.com/oss/python/deepagents/code"
|
|
8
8
|
"""URL for `deepagents-code` documentation."""
|
|
@@ -54,11 +54,13 @@ from langchain.agents.middleware.types import AgentMiddleware
|
|
|
54
54
|
from deepagents_code import theme
|
|
55
55
|
from deepagents_code._constants import DEFAULT_AGENT_NAME
|
|
56
56
|
from deepagents_code.config import (
|
|
57
|
+
_INHERITED_PYTHONPATH_ENV,
|
|
57
58
|
_ShellAllowAll,
|
|
58
59
|
config,
|
|
59
60
|
console,
|
|
60
61
|
get_default_coding_instructions,
|
|
61
62
|
get_glyphs,
|
|
63
|
+
get_langsmith_project_name,
|
|
62
64
|
settings,
|
|
63
65
|
)
|
|
64
66
|
from deepagents_code.configurable_model import ConfigurableModelMiddleware
|
|
@@ -219,12 +221,23 @@ def _resolve_ptc_option(
|
|
|
219
221
|
) -> list[str] | None:
|
|
220
222
|
"""Resolve the configured PTC allowlist to a concrete list of tool names.
|
|
221
223
|
|
|
224
|
+
Names are *not* validated against `tools`. The Deep Agents SDK injects the
|
|
225
|
+
filesystem, `task`, `write_todos`, and `execute` tools via middleware in
|
|
226
|
+
`create_deep_agent` — *after* this point — so they are absent from `tools`
|
|
227
|
+
here, and the SDK exposes no importable list of them. `CodeInterpreterMiddleware`
|
|
228
|
+
matches the resolved names against the live runtime registry and silently
|
|
229
|
+
ignores any that are absent, so resolution passes names through and lets
|
|
230
|
+
runtime decide. (Names that match nothing at runtime are dropped, so a typo
|
|
231
|
+
silently exposes no tool rather than raising.)
|
|
232
|
+
|
|
222
233
|
Args:
|
|
223
234
|
ptc: Raw `interpreter_ptc` value from settings or CLI. Accepts
|
|
224
|
-
`False`/`[]`, `"safe"`, `"all"`, or a list of names.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
235
|
+
`False`/`[]`, `"safe"`, `"all"`, or a list of names. A list may
|
|
236
|
+
include `"safe"`, which expands to `INTERPRETER_PTC_SAFE_PRESET`;
|
|
237
|
+
`"all"` is rejected inside a list.
|
|
238
|
+
tools: Tools passed to `create_cli_agent`. Used only to enumerate
|
|
239
|
+
`"all"`, which is therefore limited to these explicitly-passed
|
|
240
|
+
tools (the SDK runtime built-ins cannot be enumerated here).
|
|
228
241
|
acknowledge_unsafe: Mirrors `settings.interpreter_ptc_acknowledge_unsafe`;
|
|
229
242
|
required when `ptc="all"` and `auto_approve` is `False`.
|
|
230
243
|
auto_approve: Whether HITL approval is globally disabled. When `True`,
|
|
@@ -236,8 +249,9 @@ def _resolve_ptc_option(
|
|
|
236
249
|
suitable for `CodeInterpreterMiddleware(ptc=...)`.
|
|
237
250
|
|
|
238
251
|
Raises:
|
|
239
|
-
ValueError: For
|
|
240
|
-
|
|
252
|
+
ValueError: For `"all"` inside a list, for `"all"` without
|
|
253
|
+
`acknowledge_unsafe` outside of `auto_approve`, or for an invalid
|
|
254
|
+
`ptc` type or string.
|
|
241
255
|
"""
|
|
242
256
|
from langchain.tools import BaseTool as _BaseTool
|
|
243
257
|
|
|
@@ -265,14 +279,10 @@ def _resolve_ptc_option(
|
|
|
265
279
|
if normalized == "safe":
|
|
266
280
|
from deepagents_code.config import INTERPRETER_PTC_SAFE_PRESET
|
|
267
281
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
"interpreter_ptc='safe' preset members not present in toolset: %s",
|
|
273
|
-
dropped,
|
|
274
|
-
)
|
|
275
|
-
return selected
|
|
282
|
+
# Return the preset as-is; the middleware exposes whichever members
|
|
283
|
+
# exist in the live registry at runtime (they are SDK built-ins not
|
|
284
|
+
# present in `tools` here).
|
|
285
|
+
return sorted(INTERPRETER_PTC_SAFE_PRESET)
|
|
276
286
|
if normalized == "all":
|
|
277
287
|
if not auto_approve and not acknowledge_unsafe:
|
|
278
288
|
msg = (
|
|
@@ -282,6 +292,11 @@ def _resolve_ptc_option(
|
|
|
282
292
|
"auto_approve=True) to opt in."
|
|
283
293
|
)
|
|
284
294
|
raise ValueError(msg)
|
|
295
|
+
# `all` can only enumerate the tools passed to `create_cli_agent`;
|
|
296
|
+
# SDK runtime built-ins (filesystem, `task`, …) are injected later
|
|
297
|
+
# and are not enumerable here. Exposing them under `all` needs an
|
|
298
|
+
# "expose everything" sentinel in `CodeInterpreterMiddleware`
|
|
299
|
+
# (tracked in langchain-ai/deepagents#3847).
|
|
285
300
|
included = sorted(live_set)
|
|
286
301
|
write_included = sorted(_INTERPRETER_WRITE_TOOLS & live_set)
|
|
287
302
|
if write_included:
|
|
@@ -297,15 +312,42 @@ def _resolve_ptc_option(
|
|
|
297
312
|
raise ValueError(msg)
|
|
298
313
|
|
|
299
314
|
if isinstance(ptc, list):
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
315
|
+
from deepagents_code.config import INTERPRETER_PTC_SAFE_PRESET
|
|
316
|
+
|
|
317
|
+
if any(name.strip().lower() == "all" for name in ptc):
|
|
303
318
|
msg = (
|
|
304
|
-
"
|
|
305
|
-
|
|
319
|
+
"interpreter_ptc list entries cannot include 'all'; use 'all' "
|
|
320
|
+
"as a standalone value or list explicit tool names (optionally "
|
|
321
|
+
"with the 'safe' preset)."
|
|
306
322
|
)
|
|
307
323
|
raise ValueError(msg)
|
|
308
|
-
|
|
324
|
+
|
|
325
|
+
resolved: list[str] = []
|
|
326
|
+
seen: set[str] = set()
|
|
327
|
+
|
|
328
|
+
def _add(name: str) -> None:
|
|
329
|
+
if name not in seen:
|
|
330
|
+
seen.add(name)
|
|
331
|
+
resolved.append(name)
|
|
332
|
+
|
|
333
|
+
for name in ptc:
|
|
334
|
+
if name.strip().lower() == "safe":
|
|
335
|
+
for member in sorted(INTERPRETER_PTC_SAFE_PRESET):
|
|
336
|
+
_add(member)
|
|
337
|
+
continue
|
|
338
|
+
_add(name)
|
|
339
|
+
|
|
340
|
+
# Explicit names are passed through unvalidated: the middleware resolves
|
|
341
|
+
# them against the live runtime registry (which includes the SDK
|
|
342
|
+
# built-ins absent from `tools`) and drops any that match nothing.
|
|
343
|
+
absent = sorted(n for n in resolved if n not in live_set)
|
|
344
|
+
if absent:
|
|
345
|
+
logger.debug(
|
|
346
|
+
"interpreter_ptc names not in the build-time toolset (resolved "
|
|
347
|
+
"at runtime if present): %s",
|
|
348
|
+
absent,
|
|
349
|
+
)
|
|
350
|
+
return resolved
|
|
309
351
|
|
|
310
352
|
msg = (
|
|
311
353
|
"interpreter_ptc must be False, 'safe', 'all', or a list of tool names; "
|
|
@@ -1024,6 +1066,23 @@ def _add_interrupt_on() -> dict[str, InterruptOnConfig]:
|
|
|
1024
1066
|
return interrupt_map
|
|
1025
1067
|
|
|
1026
1068
|
|
|
1069
|
+
def _apply_inherited_pythonpath(env: dict[str, str]) -> None:
|
|
1070
|
+
"""Re-apply a relayed launch-time `PYTHONPATH` to a shell-command env.
|
|
1071
|
+
|
|
1072
|
+
`server._build_server_env` strips `PYTHONPATH` from the server interpreter
|
|
1073
|
+
and relays the launch value via `config._INHERITED_PYTHONPATH_ENV`. This
|
|
1074
|
+
restores it as `PYTHONPATH` for the approval-gated `execute` subprocesses,
|
|
1075
|
+
which run in the user's working directory and need the import path. Mutates
|
|
1076
|
+
`env` in place; a no-op when no value was relayed.
|
|
1077
|
+
|
|
1078
|
+
Args:
|
|
1079
|
+
env: Environment mapping for the shell backend, modified in place.
|
|
1080
|
+
"""
|
|
1081
|
+
inherited = env.pop(_INHERITED_PYTHONPATH_ENV, None)
|
|
1082
|
+
if inherited is not None:
|
|
1083
|
+
env["PYTHONPATH"] = inherited
|
|
1084
|
+
|
|
1085
|
+
|
|
1027
1086
|
def create_cli_agent(
|
|
1028
1087
|
model: str | BaseChatModel,
|
|
1029
1088
|
assistant_id: str,
|
|
@@ -1367,13 +1426,19 @@ def create_cli_agent(
|
|
|
1367
1426
|
shell_env = os.environ.copy()
|
|
1368
1427
|
if settings.user_langchain_project:
|
|
1369
1428
|
shell_env["LANGSMITH_PROJECT"] = settings.user_langchain_project
|
|
1429
|
+
# Re-apply a launch-time PYTHONPATH that was stripped from the server
|
|
1430
|
+
# interpreter but relayed for approval-gated `execute` commands.
|
|
1431
|
+
_apply_inherited_pythonpath(shell_env)
|
|
1370
1432
|
|
|
1371
1433
|
# Use LocalShellBackend for filesystem + shell execution.
|
|
1372
1434
|
# The SDK's FilesystemMiddleware exposes per-command timeout
|
|
1373
1435
|
# on the execute tool natively.
|
|
1436
|
+
# `inherit_env=False`: `shell_env` is already a complete, curated
|
|
1437
|
+
# copy of `os.environ`. Inheriting again would re-copy `os.environ`
|
|
1438
|
+
# and resurrect the popped carrier var, leaking it into `execute`.
|
|
1374
1439
|
backend = LocalShellBackend(
|
|
1375
1440
|
root_dir=root_dir,
|
|
1376
|
-
inherit_env=
|
|
1441
|
+
inherit_env=False,
|
|
1377
1442
|
env=shell_env,
|
|
1378
1443
|
)
|
|
1379
1444
|
else:
|
|
@@ -1419,7 +1484,12 @@ def create_cli_agent(
|
|
|
1419
1484
|
# Local context middleware (git info, directory tree, etc.).
|
|
1420
1485
|
if isinstance(backend, (_ExecutableBackend, _AsyncExecutableBackend)):
|
|
1421
1486
|
agent_middleware.append(
|
|
1422
|
-
LocalContextMiddleware(
|
|
1487
|
+
LocalContextMiddleware(
|
|
1488
|
+
backend=backend,
|
|
1489
|
+
mcp_server_info=mcp_server_info,
|
|
1490
|
+
tracing_project=get_langsmith_project_name(),
|
|
1491
|
+
user_tracing_project=settings.user_langchain_project,
|
|
1492
|
+
)
|
|
1423
1493
|
)
|
|
1424
1494
|
|
|
1425
1495
|
# Add shell allow-list middleware when interrupt_shell_only is active.
|