deepagents-code 0.1.10__tar.gz → 0.1.11__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.10 → deepagents_code-0.1.11}/CHANGELOG.md +8 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/PKG-INFO +1 -1
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_env_vars.py +10 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_version.py +1 -1
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/app.py +95 -86
- deepagents_code-0.1.11/deepagents_code/auth_display.py +137 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/main.py +125 -1
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/update_check.py +43 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/auth.py +2 -16
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/loading.py +23 -4
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/model_selector.py +3 -20
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/pyproject.toml +1 -1
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/conftest.py +14 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_app.py +26 -0
- deepagents_code-0.1.11/tests/unit_tests/test_auth_display.py +187 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_loading.py +78 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_main.py +292 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_update_check.py +46 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/uv.lock +47 -47
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/.gitignore +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/AGENTS.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/COMMANDS.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/DEV.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/Makefile +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/README.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/THREAT_MODEL.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/__init__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/__main__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_ask_user_types.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_cli_context.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_constants.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_debug.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_git.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_server_config.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_session_stats.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_startup_error.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_testing_models.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/_textual_patches.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/agent.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/app.tcss +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/ask_user.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/auth_store.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/built_in_skills/__init__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/built_in_skills/remember/SKILL.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/built_in_skills/skill-creator/SKILL.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/clipboard.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/command_registry.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/config.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/configurable_model.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/default_agent_prompt.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/editor.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/event_bus.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/extras_info.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/file_ops.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/filesystem_empty_result.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/formatting.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/hooks.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/input.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/integrations/__init__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/integrations/sandbox_factory.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/integrations/sandbox_provider.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/iterm_cursor_guide.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/local_context.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_auth.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_commands.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_disabled.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_login_service.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_oauth_ui.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_providers/__init__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_providers/_registry.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_providers/base.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_providers/github.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_providers/slack.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_tools.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/mcp_trust.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/media_utils.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/model_config.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/non_interactive.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/notifications.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/offload.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/onboarding.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/output.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/project_utils.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/py.typed +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/remote_client.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/resume_state.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/server.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/server_graph.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/server_manager.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/sessions.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/skills/__init__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/skills/commands.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/skills/invocation.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/skills/load.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/state_migration.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/subagents.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/system_prompt.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/terminal_capabilities.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/terminal_escape.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/textual_adapter.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/theme.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/tool_display.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/tools.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/ui.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/unicode_security.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/__init__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/_links.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/agent_selector.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/approval.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/ask_user.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/autocomplete.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/chat_input.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/diff.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/history.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/launch_init.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/mcp_login.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/mcp_reconnect.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/mcp_viewer.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/message_store.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/messages.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/notification_center.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/notification_detail.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/notification_settings.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/status.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/theme_selector.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/thread_selector.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/tool_renderers.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/tool_widgets.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/update_available.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/update_progress.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/deepagents_code/widgets/welcome.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/arxiv-search/SKILL.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/arxiv-search/arxiv_search.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/langgraph-docs/SKILL.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/skill-creator/SKILL.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/examples/skills/web-research/SKILL.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/images/tui.png +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/scripts/check_imports.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/scripts/debug_server.sh +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/scripts/generate_commands_catalog.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/scripts/install.sh +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/README.md +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/__init__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/benchmarks/__init__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/conftest.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/test_acp_mode.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/test_compact_resume.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/test_sandbox_factory.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/integration_tests/test_sandbox_operations.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/__init__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/skills/__init__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/skills/test_commands.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/skills/test_load.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/skills/test_skills_json.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_agent.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_agent_friendly.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_agent_selector.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_approval.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_args.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_ask_user.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_ask_user_middleware.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_auth_store.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_auth_widgets.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_autocomplete.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_charset.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_chat_input.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_clipboard.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_command_registry.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_compact_tool.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_config.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_configurable_model.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_cursor_blink.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_debug.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_editor.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_end_to_end.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_env_vars.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_event_bus.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_exception_handling.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_extras_info.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_file_ops.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_filesystem_empty_result.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_formatting.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_git.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_history.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_hooks.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_imports.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_input_parsing.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_install_command.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_iterm_cursor_guide.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_launch_init.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_links.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_local_context.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_main_acp_mode.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_main_args.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_auth.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_commands.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_disabled.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_login_modal.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_login_service.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_oauth_ui.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_reconnect.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_tools.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_trust.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_mcp_viewer.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_media_utils.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_message_store.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_messages.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_model_config.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_model_selector.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_model_switch.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_non_interactive.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_notification_center.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_notification_detail.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_notifications.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_offload.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_offload_dict_messages.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_onboarding.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_output.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_reload.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_remote_client.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_resume_state.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_sandbox_factory.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_server.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_server_config.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_server_graph.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_server_helpers.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_server_manager.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_session_stats.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_sessions.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_shell_allow_list.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_skill_invocation.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_startup_fast_paths.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_state_migration.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_status.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_subagents.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_terminal_capabilities.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_terminal_escape.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_terminal_progress_preference.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_textual_adapter.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_textual_patches.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_theme.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_thread_selector.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_tool_display.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_ui.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_unicode_security.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_update_available.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_update_progress.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_version.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/test_welcome.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/tools/__init__.py +0 -0
- {deepagents_code-0.1.10 → deepagents_code-0.1.11}/tests/unit_tests/tools/test_fetch_url.py +0 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
# Deep Agents Code Changelog
|
|
4
4
|
|
|
5
|
+
## [0.1.11](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.10...deepagents-code==0.1.11) (2026-06-07)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* Pause loading timer during approvals ([#3782](https://github.com/langchain-ai/deepagents/issues/3782)) ([f98fb0c](https://github.com/langchain-ai/deepagents/commit/f98fb0c80d08e408a018ea33a8aa7144180f4e93))
|
|
10
|
+
* Run auto-update before startup ([#3784](https://github.com/langchain-ai/deepagents/issues/3784)) ([c160ea3](https://github.com/langchain-ai/deepagents/commit/c160ea3eeda1d0ba707bb524cfd0ce087a854e08))
|
|
11
|
+
* Skip update prompts for editable installs ([#3781](https://github.com/langchain-ai/deepagents/issues/3781)) ([ae2874e](https://github.com/langchain-ai/deepagents/commit/ae2874e8ece96c04233c1a88a9da1bd7b9ee2bb2))
|
|
12
|
+
|
|
5
13
|
## [0.1.10](https://github.com/langchain-ai/deepagents/compare/deepagents-code==0.1.9...deepagents-code==0.1.10) (2026-06-05)
|
|
6
14
|
|
|
7
15
|
### Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagents-code
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
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/
|
|
@@ -134,6 +134,16 @@ and `/api/show`. See `_ollama_discovery_enabled` for accepted truthy/falsy
|
|
|
134
134
|
values.
|
|
135
135
|
"""
|
|
136
136
|
|
|
137
|
+
RESTARTED_AFTER_UPDATE = "DEEPAGENTS_CODE_RESTARTED_AFTER_UPDATE"
|
|
138
|
+
"""Internal sentinel recording the target version immediately before the
|
|
139
|
+
startup auto-update re-execs the process.
|
|
140
|
+
|
|
141
|
+
Not user-facing. The re-exec'd process consumes it and, if that same version
|
|
142
|
+
still reports as available (a no-op upgrade that did not change the running
|
|
143
|
+
version), skips auto-updating to break out of an otherwise endless
|
|
144
|
+
upgrade/restart loop. Set and read internally across `os.execv`.
|
|
145
|
+
"""
|
|
146
|
+
|
|
137
147
|
SERVER_ENV_PREFIX = "DEEPAGENTS_CODE_SERVER_"
|
|
138
148
|
"""Environment variable prefix used to pass CLI config to the server subprocess."""
|
|
139
149
|
|
|
@@ -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.11" # 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."""
|
|
@@ -3201,23 +3201,31 @@ class DeepAgentsApp(App):
|
|
|
3201
3201
|
self._update_check_done.set()
|
|
3202
3202
|
|
|
3203
3203
|
async def _check_for_updates_impl(self, *, periodic: bool = False) -> None:
|
|
3204
|
-
"""Check PyPI for a newer version and
|
|
3204
|
+
"""Check PyPI for a newer version and surface it in-session.
|
|
3205
3205
|
|
|
3206
3206
|
Phase 1 contacts PyPI and records the latest version on the app.
|
|
3207
|
-
Phase 2
|
|
3208
|
-
|
|
3207
|
+
Phase 2 surfaces a detected update without installing it in-session
|
|
3208
|
+
(the actual install runs at startup via `_run_startup_auto_update`):
|
|
3209
|
+
when auto-update is enabled it toasts a prompt to restart so the
|
|
3210
|
+
startup path can upgrade; otherwise it raises an actionable notice
|
|
3211
|
+
(periodic recheck) or registers the notice and schedules the update
|
|
3212
|
+
modal (initial check).
|
|
3209
3213
|
Phase 2 sets `_update_modal_pending` *only* when the modal is
|
|
3210
3214
|
actually being scheduled; a detected-but-throttled update
|
|
3211
3215
|
leaves the event clear so missing-dep toasts still fire.
|
|
3212
3216
|
"""
|
|
3213
3217
|
# Phase 1: version check (benign failure)
|
|
3214
3218
|
try:
|
|
3219
|
+
from deepagents_code.config import _is_editable_install
|
|
3215
3220
|
from deepagents_code.update_check import (
|
|
3216
3221
|
is_auto_update_enabled,
|
|
3217
3222
|
is_update_available,
|
|
3218
3223
|
upgrade_command,
|
|
3219
3224
|
)
|
|
3220
3225
|
|
|
3226
|
+
if await asyncio.to_thread(_is_editable_install):
|
|
3227
|
+
return
|
|
3228
|
+
|
|
3221
3229
|
available, latest = await asyncio.to_thread(
|
|
3222
3230
|
is_update_available,
|
|
3223
3231
|
bypass_cache=periodic,
|
|
@@ -3233,68 +3241,16 @@ class DeepAgentsApp(App):
|
|
|
3233
3241
|
# Phase 2: auto-update or register actionable notice
|
|
3234
3242
|
try:
|
|
3235
3243
|
from deepagents_code._version import __version__ as cli_version
|
|
3244
|
+
from deepagents_code.update_check import (
|
|
3245
|
+
format_installed_age_suffix,
|
|
3246
|
+
format_release_age_parenthetical,
|
|
3247
|
+
mark_update_notified,
|
|
3248
|
+
should_notify_update,
|
|
3249
|
+
)
|
|
3236
3250
|
|
|
3237
3251
|
if is_auto_update_enabled():
|
|
3238
|
-
from deepagents_code._env_vars import DEBUG_UPDATE
|
|
3239
|
-
from deepagents_code.update_check import (
|
|
3240
|
-
create_update_log_path,
|
|
3241
|
-
perform_upgrade,
|
|
3242
|
-
)
|
|
3243
|
-
|
|
3244
|
-
if os.environ.get(DEBUG_UPDATE):
|
|
3245
|
-
self.notify(
|
|
3246
|
-
"Skipped update install (debug mode).",
|
|
3247
|
-
severity="information",
|
|
3248
|
-
timeout=4,
|
|
3249
|
-
markup=False,
|
|
3250
|
-
)
|
|
3251
|
-
return
|
|
3252
|
-
|
|
3253
|
-
log_path = create_update_log_path()
|
|
3254
|
-
self.notify(
|
|
3255
|
-
f"Updating to v{latest}... Logs: {log_path}",
|
|
3256
|
-
severity="information",
|
|
3257
|
-
timeout=5,
|
|
3258
|
-
markup=False,
|
|
3259
|
-
)
|
|
3260
|
-
success, output = await perform_upgrade(log_path=log_path)
|
|
3261
|
-
if success:
|
|
3262
|
-
self.notify(
|
|
3263
|
-
f"Updated to v{latest}. Restart to use the new version.",
|
|
3264
|
-
severity="information",
|
|
3265
|
-
timeout=10,
|
|
3266
|
-
)
|
|
3267
|
-
else:
|
|
3268
|
-
logger.warning(
|
|
3269
|
-
"Background auto-upgrade to v%s failed. Output:\n%s",
|
|
3270
|
-
latest,
|
|
3271
|
-
output,
|
|
3272
|
-
)
|
|
3273
|
-
cmd = upgrade_command()
|
|
3274
|
-
snippet = _truncate(output, limit=160) if output else ""
|
|
3275
|
-
message = (
|
|
3276
|
-
f"Auto-update failed. Run manually: {cmd}\nLog: {log_path}"
|
|
3277
|
-
)
|
|
3278
|
-
if snippet:
|
|
3279
|
-
message = f"{message}\n{snippet}"
|
|
3280
|
-
self.notify(
|
|
3281
|
-
message,
|
|
3282
|
-
severity="warning",
|
|
3283
|
-
timeout=15,
|
|
3284
|
-
markup=False,
|
|
3285
|
-
)
|
|
3286
|
-
else:
|
|
3287
|
-
from deepagents_code.update_check import (
|
|
3288
|
-
format_installed_age_suffix,
|
|
3289
|
-
format_release_age_parenthetical,
|
|
3290
|
-
mark_update_notified,
|
|
3291
|
-
should_notify_update,
|
|
3292
|
-
)
|
|
3293
|
-
|
|
3294
3252
|
if not await asyncio.to_thread(should_notify_update, latest):
|
|
3295
3253
|
return
|
|
3296
|
-
|
|
3297
|
-
cmd = upgrade_command()
|
|
3298
3254
|
release_age = await asyncio.to_thread(
|
|
3299
3255
|
format_release_age_parenthetical,
|
|
3300
3256
|
latest,
|
|
@@ -3303,33 +3259,56 @@ class DeepAgentsApp(App):
|
|
|
3303
3259
|
format_installed_age_suffix,
|
|
3304
3260
|
cli_version,
|
|
3305
3261
|
)
|
|
3306
|
-
|
|
3307
|
-
latest
|
|
3308
|
-
cli_version
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3262
|
+
self.notify(
|
|
3263
|
+
f"Update available: v{latest}{release_age}. "
|
|
3264
|
+
f"Currently installed: {cli_version}{installed_age}. "
|
|
3265
|
+
"Restart dcode to auto-update before startup.",
|
|
3266
|
+
severity="information",
|
|
3267
|
+
timeout=12,
|
|
3268
|
+
markup=False,
|
|
3312
3269
|
)
|
|
3313
|
-
if periodic:
|
|
3314
|
-
self._notify_actionable(
|
|
3315
|
-
notification,
|
|
3316
|
-
severity="information",
|
|
3317
|
-
timeout=12,
|
|
3318
|
-
action_hint="Press ctrl+n to install.",
|
|
3319
|
-
)
|
|
3320
|
-
await asyncio.to_thread(mark_update_notified, latest)
|
|
3321
|
-
return
|
|
3322
|
-
# Register without a toast: the dedicated modal is
|
|
3323
|
-
# the update's UI, so a parallel toast would be
|
|
3324
|
-
# redundant. Registration still makes the entry
|
|
3325
|
-
# reachable via ctrl+n if the modal is dismissed.
|
|
3326
|
-
self._notice_registry.add(notification)
|
|
3327
3270
|
await asyncio.to_thread(mark_update_notified, latest)
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3271
|
+
return
|
|
3272
|
+
|
|
3273
|
+
if not await asyncio.to_thread(should_notify_update, latest):
|
|
3274
|
+
return
|
|
3275
|
+
|
|
3276
|
+
cmd = upgrade_command()
|
|
3277
|
+
release_age = await asyncio.to_thread(
|
|
3278
|
+
format_release_age_parenthetical,
|
|
3279
|
+
latest,
|
|
3280
|
+
)
|
|
3281
|
+
installed_age = await asyncio.to_thread(
|
|
3282
|
+
format_installed_age_suffix,
|
|
3283
|
+
cli_version,
|
|
3284
|
+
)
|
|
3285
|
+
notification = self._build_update_notification(
|
|
3286
|
+
latest=latest,
|
|
3287
|
+
cli_version=cli_version,
|
|
3288
|
+
release_age=release_age,
|
|
3289
|
+
installed_age=installed_age,
|
|
3290
|
+
upgrade_cmd=cmd,
|
|
3291
|
+
)
|
|
3292
|
+
if periodic:
|
|
3293
|
+
self._notify_actionable(
|
|
3294
|
+
notification,
|
|
3295
|
+
severity="information",
|
|
3296
|
+
timeout=12,
|
|
3297
|
+
action_hint="Press ctrl+n to install.",
|
|
3298
|
+
)
|
|
3299
|
+
await asyncio.to_thread(mark_update_notified, latest)
|
|
3300
|
+
return
|
|
3301
|
+
# Register without a toast: the dedicated modal is
|
|
3302
|
+
# the update's UI, so a parallel toast would be
|
|
3303
|
+
# redundant. Registration still makes the entry
|
|
3304
|
+
# reachable via ctrl+n if the modal is dismissed.
|
|
3305
|
+
self._notice_registry.add(notification)
|
|
3306
|
+
await asyncio.to_thread(mark_update_notified, latest)
|
|
3307
|
+
# Set *before* scheduling the modal: the optional-tools
|
|
3308
|
+
# worker may race with this path, and it gates toast
|
|
3309
|
+
# suppression on this event.
|
|
3310
|
+
self._update_modal_pending.set()
|
|
3311
|
+
self.call_after_refresh(self._open_update_available_modal, notification)
|
|
3333
3312
|
except Exception:
|
|
3334
3313
|
logger.warning("Update check/notify failed unexpectedly", exc_info=True)
|
|
3335
3314
|
if is_auto_update_enabled():
|
|
@@ -4101,6 +4080,24 @@ class DeepAgentsApp(App):
|
|
|
4101
4080
|
# Cosmetic only: must never break app startup or theme changes.
|
|
4102
4081
|
logger.warning("set_terminal_background raised unexpectedly", exc_info=True)
|
|
4103
4082
|
|
|
4083
|
+
def _pause_loading_spinner_for_approval(self) -> None:
|
|
4084
|
+
"""Pause the global spinner timer while an approval widget is visible."""
|
|
4085
|
+
if self._loading_widget is not None:
|
|
4086
|
+
self._loading_widget.pause()
|
|
4087
|
+
|
|
4088
|
+
def _resume_loading_spinner_after_approval(
|
|
4089
|
+
self,
|
|
4090
|
+
_future: asyncio.Future[Any] | None = None,
|
|
4091
|
+
) -> None:
|
|
4092
|
+
"""Resume the global spinner timer after an approval decision.
|
|
4093
|
+
|
|
4094
|
+
Accepts an unused `_future` argument so it can be registered directly as
|
|
4095
|
+
a `Future.add_done_callback`, which always passes the completed future
|
|
4096
|
+
positionally.
|
|
4097
|
+
"""
|
|
4098
|
+
if self._loading_widget is not None:
|
|
4099
|
+
self._loading_widget.resume()
|
|
4100
|
+
|
|
4104
4101
|
async def _set_spinner(self, status: SpinnerStatus) -> None:
|
|
4105
4102
|
"""Show, update, or hide the loading spinner.
|
|
4106
4103
|
|
|
@@ -4149,6 +4146,11 @@ class DeepAgentsApp(App):
|
|
|
4149
4146
|
self._loading_widget = LoadingWidget(status)
|
|
4150
4147
|
await self._mount_before_queued(messages, self._loading_widget)
|
|
4151
4148
|
else:
|
|
4149
|
+
# A fresh status update means the agent is active again, so
|
|
4150
|
+
# un-pause as a backstop in case an approval future was ever
|
|
4151
|
+
# abandoned without completing the resume callback. `resume()` is a
|
|
4152
|
+
# no-op when the spinner is not paused.
|
|
4153
|
+
self._loading_widget.resume()
|
|
4152
4154
|
# Update existing
|
|
4153
4155
|
self._loading_widget.set_status(status)
|
|
4154
4156
|
# Reposition via move_child so elapsed-time and animation state
|
|
@@ -4261,6 +4263,13 @@ class DeepAgentsApp(App):
|
|
|
4261
4263
|
while self._pending_approval_widget is not None: # noqa: ASYNC110 # Simple polling is sufficient here
|
|
4262
4264
|
await asyncio.sleep(0.1)
|
|
4263
4265
|
|
|
4266
|
+
# Pause the elapsed-time counter while the user decides, then resume it
|
|
4267
|
+
# when the decision future completes. Resolve, reject, and cancel all
|
|
4268
|
+
# fire the done-callback; the `_set_spinner` backstop covers the
|
|
4269
|
+
# remaining case where a future is abandoned without completing.
|
|
4270
|
+
self._pause_loading_spinner_for_approval()
|
|
4271
|
+
result_future.add_done_callback(self._resume_loading_spinner_after_approval)
|
|
4272
|
+
|
|
4264
4273
|
# Create menu with unique ID to avoid conflicts
|
|
4265
4274
|
from deepagents_code.widgets.approval import ApprovalMenu
|
|
4266
4275
|
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Shared provider auth status formatting."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, assert_never
|
|
6
|
+
|
|
7
|
+
from textual.content import Content
|
|
8
|
+
|
|
9
|
+
from deepagents_code.model_config import (
|
|
10
|
+
ProviderAuthSource,
|
|
11
|
+
ProviderAuthState,
|
|
12
|
+
ProviderAuthStatus,
|
|
13
|
+
resolved_env_var_name,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from deepagents_code.config import Glyphs
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def format_auth_badge(status: ProviderAuthStatus) -> Content:
|
|
21
|
+
"""Format an auth manager badge for a provider.
|
|
22
|
+
|
|
23
|
+
Used by the `/auth` manager, where each provider renders a bracketed,
|
|
24
|
+
styled badge (e.g. `[stored]`, `[env: ANTHROPIC_API_KEY]`, `[missing]`).
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
status: Provider auth/readiness status.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
A styled badge `Content` for the auth manager surface.
|
|
31
|
+
"""
|
|
32
|
+
state = status.state
|
|
33
|
+
match state:
|
|
34
|
+
case ProviderAuthState.CONFIGURED:
|
|
35
|
+
return _format_configured_badge(status)
|
|
36
|
+
case ProviderAuthState.MISSING:
|
|
37
|
+
return Content.styled("[missing]", "bold $warning")
|
|
38
|
+
case ProviderAuthState.NOT_REQUIRED:
|
|
39
|
+
return _auth_badge(status.detail or "no API key required")
|
|
40
|
+
case ProviderAuthState.IMPLICIT:
|
|
41
|
+
return _auth_badge(status.detail or "implicit auth")
|
|
42
|
+
case ProviderAuthState.MANAGED:
|
|
43
|
+
return _auth_badge(status.detail or "custom auth")
|
|
44
|
+
case ProviderAuthState.UNKNOWN:
|
|
45
|
+
return _auth_badge(status.detail or "credentials unknown", prefix="? ")
|
|
46
|
+
case _:
|
|
47
|
+
assert_never(state)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def format_auth_indicator(status: ProviderAuthStatus, glyphs: Glyphs) -> str:
|
|
51
|
+
"""Format a model selector provider-header indicator.
|
|
52
|
+
|
|
53
|
+
Used by `/model`, where the indicator is plain text shown next to the
|
|
54
|
+
provider name. Returns an empty string for `CONFIGURED` providers, which
|
|
55
|
+
need no indicator.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
status: Provider auth/readiness status.
|
|
59
|
+
glyphs: Glyph table for the active terminal mode.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Text shown next to the provider name, or an empty string when no
|
|
63
|
+
indicator should be rendered (e.g., `CONFIGURED`).
|
|
64
|
+
"""
|
|
65
|
+
state = status.state
|
|
66
|
+
match state:
|
|
67
|
+
case ProviderAuthState.CONFIGURED:
|
|
68
|
+
return ""
|
|
69
|
+
case ProviderAuthState.MISSING:
|
|
70
|
+
if status.env_var:
|
|
71
|
+
return f"{glyphs.warning} missing {status.env_var}"
|
|
72
|
+
return f"{glyphs.warning} missing credentials"
|
|
73
|
+
case ProviderAuthState.NOT_REQUIRED:
|
|
74
|
+
return status.detail or "no API key required"
|
|
75
|
+
case ProviderAuthState.IMPLICIT:
|
|
76
|
+
return status.detail or "implicit auth"
|
|
77
|
+
case ProviderAuthState.MANAGED:
|
|
78
|
+
return status.detail or "custom auth"
|
|
79
|
+
case ProviderAuthState.UNKNOWN:
|
|
80
|
+
detail = status.detail or "credentials unknown"
|
|
81
|
+
return f"{glyphs.question} {detail}"
|
|
82
|
+
case _:
|
|
83
|
+
assert_never(state)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _auth_badge(detail: str, *, prefix: str = "") -> Content:
|
|
87
|
+
"""Format a muted auth manager badge.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
detail: Badge text inside the brackets.
|
|
91
|
+
prefix: Text prepended verbatim before `detail` inside the brackets.
|
|
92
|
+
Callers include any separator themselves (e.g. `"? "`); this
|
|
93
|
+
helper does not insert one.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Formatted auth manager badge content.
|
|
97
|
+
"""
|
|
98
|
+
return Content.assemble(
|
|
99
|
+
("[", "$text-muted"),
|
|
100
|
+
(prefix, "$text-muted"),
|
|
101
|
+
Content.styled(detail, "$text-muted"),
|
|
102
|
+
("]", "$text-muted"),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _format_configured_badge(status: ProviderAuthStatus) -> Content:
|
|
107
|
+
"""Format the auth manager badge for a `CONFIGURED` provider.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
status: A `CONFIGURED` provider auth status.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
A styled badge naming the credential source (`[stored]` or `[env: …]`).
|
|
114
|
+
|
|
115
|
+
Raises:
|
|
116
|
+
ValueError: If the status carries no source. `ProviderAuthStatus`
|
|
117
|
+
guarantees `CONFIGURED` implies a source, so this guards against
|
|
118
|
+
that invariant being violated rather than a normal input.
|
|
119
|
+
"""
|
|
120
|
+
match status.source:
|
|
121
|
+
case ProviderAuthSource.STORED:
|
|
122
|
+
return Content.styled("[stored]", "bold $success")
|
|
123
|
+
case ProviderAuthSource.ENV:
|
|
124
|
+
if status.env_var:
|
|
125
|
+
return Content.assemble(
|
|
126
|
+
("[env: ", "$text-muted"),
|
|
127
|
+
Content.styled(
|
|
128
|
+
resolved_env_var_name(status.env_var), "$text-muted"
|
|
129
|
+
),
|
|
130
|
+
("]", "$text-muted"),
|
|
131
|
+
)
|
|
132
|
+
return Content.styled("[env]", "$text-muted")
|
|
133
|
+
case None:
|
|
134
|
+
msg = f"CONFIGURED auth status has no source: {status!r}"
|
|
135
|
+
raise ValueError(msg)
|
|
136
|
+
case _:
|
|
137
|
+
assert_never(status.source)
|
|
@@ -20,9 +20,11 @@ import sys
|
|
|
20
20
|
import traceback
|
|
21
21
|
from collections.abc import Callable, Sequence
|
|
22
22
|
from pathlib import Path
|
|
23
|
-
from typing import TYPE_CHECKING, Any
|
|
23
|
+
from typing import TYPE_CHECKING, Any, NoReturn
|
|
24
24
|
|
|
25
25
|
if TYPE_CHECKING:
|
|
26
|
+
from rich.console import Console
|
|
27
|
+
|
|
26
28
|
from deepagents_code.app import AppResult
|
|
27
29
|
from deepagents_code.mcp_tools import MCPServerInfo
|
|
28
30
|
from deepagents_code.notifications import PendingNotification
|
|
@@ -35,6 +37,127 @@ from deepagents_code._version import __version__
|
|
|
35
37
|
logger = logging.getLogger(__name__)
|
|
36
38
|
|
|
37
39
|
|
|
40
|
+
def _restart_current_process() -> NoReturn:
|
|
41
|
+
"""Replace the current process with a fresh `deepagents_code` invocation.
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
RuntimeError: If process replacement unexpectedly returns.
|
|
45
|
+
"""
|
|
46
|
+
argv = [sys.executable, "-m", "deepagents_code", *sys.argv[1:]]
|
|
47
|
+
# Re-exec the trusted interpreter with the user's own argv verbatim; the
|
|
48
|
+
# only "input" is the command the user already ran, so S606's concern
|
|
49
|
+
# (untrusted/unsanitized args to a spawned executable) does not apply.
|
|
50
|
+
os.execv(sys.executable, argv) # noqa: S606
|
|
51
|
+
msg = "os.execv returned unexpectedly"
|
|
52
|
+
raise RuntimeError(msg)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _run_startup_auto_update(console: "Console") -> None:
|
|
56
|
+
"""Apply enabled auto-updates before the TUI and server start.
|
|
57
|
+
|
|
58
|
+
On a successful upgrade the process is re-exec'd so the new version is
|
|
59
|
+
loaded. Any failure is fail-soft: the installed version is launched and
|
|
60
|
+
the error is surfaced, never blocking startup.
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
SystemExit: Re-raised rather than suppressed by the fail-soft handler,
|
|
64
|
+
so a process-exit request is never swallowed (the `os.execv`
|
|
65
|
+
re-exec is simulated this way under test).
|
|
66
|
+
"""
|
|
67
|
+
from rich.markup import escape
|
|
68
|
+
|
|
69
|
+
from deepagents_code._env_vars import DEBUG_UPDATE, RESTARTED_AFTER_UPDATE
|
|
70
|
+
from deepagents_code._version import __version__ as cli_version
|
|
71
|
+
from deepagents_code.config import _is_editable_install
|
|
72
|
+
from deepagents_code.update_check import (
|
|
73
|
+
create_update_log_path,
|
|
74
|
+
format_release_age_parenthetical,
|
|
75
|
+
get_cached_update_available,
|
|
76
|
+
is_auto_update_enabled,
|
|
77
|
+
perform_upgrade,
|
|
78
|
+
upgrade_command,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
if _is_editable_install() or not is_auto_update_enabled():
|
|
83
|
+
return
|
|
84
|
+
# Consume the re-exec sentinel recorded before the previous restart.
|
|
85
|
+
restarted_for = os.environ.pop(RESTARTED_AFTER_UPDATE, None)
|
|
86
|
+
available, latest = get_cached_update_available()
|
|
87
|
+
if not available or latest is None:
|
|
88
|
+
return
|
|
89
|
+
if restarted_for == latest:
|
|
90
|
+
# Already restarted after upgrading to this version, yet it still
|
|
91
|
+
# reports as available: the install did not change the running
|
|
92
|
+
# version. Bail out instead of upgrading and restarting forever
|
|
93
|
+
# (this runs before the TUI, so there is no in-app way to stop it).
|
|
94
|
+
cmd = upgrade_command()
|
|
95
|
+
console.print(
|
|
96
|
+
f"[bold yellow]Warning:[/bold yellow] v{latest} still reports as "
|
|
97
|
+
"available after an automatic update; skipping auto-update to "
|
|
98
|
+
f"avoid a restart loop. Update manually: [cyan]{cmd}[/cyan]\n"
|
|
99
|
+
f"Continuing with v{cli_version}.",
|
|
100
|
+
highlight=False,
|
|
101
|
+
)
|
|
102
|
+
return
|
|
103
|
+
release_age = format_release_age_parenthetical(latest)
|
|
104
|
+
console.print(
|
|
105
|
+
f"Auto-updating deepagents-code from v{cli_version} to "
|
|
106
|
+
f"v{latest}{release_age} before startup..."
|
|
107
|
+
)
|
|
108
|
+
if os.environ.get(DEBUG_UPDATE):
|
|
109
|
+
console.print("Skipped update install (debug mode).", style="dim")
|
|
110
|
+
return
|
|
111
|
+
log_path = create_update_log_path()
|
|
112
|
+
console.print(
|
|
113
|
+
f"Update log: {log_path}\nTail progress: tail -f {log_path}",
|
|
114
|
+
style="dim",
|
|
115
|
+
highlight=False,
|
|
116
|
+
markup=False,
|
|
117
|
+
)
|
|
118
|
+
success, output = asyncio.run(perform_upgrade(log_path=log_path))
|
|
119
|
+
if success:
|
|
120
|
+
console.print(f"[green]Updated to v{latest}. Restarting...[/green]")
|
|
121
|
+
# Record the target version so the re-exec'd process can detect a
|
|
122
|
+
# no-op upgrade and break the loop (see the `restarted_for` guard).
|
|
123
|
+
os.environ[RESTARTED_AFTER_UPDATE] = latest
|
|
124
|
+
try:
|
|
125
|
+
_restart_current_process()
|
|
126
|
+
except (OSError, RuntimeError):
|
|
127
|
+
# Upgrade succeeded but the re-exec did not happen (`os.execv`
|
|
128
|
+
# raised, or returned unexpectedly). Drop the sentinel and
|
|
129
|
+
# continue on the old in-memory code; the user must restart
|
|
130
|
+
# manually to load the new version.
|
|
131
|
+
os.environ.pop(RESTARTED_AFTER_UPDATE, None)
|
|
132
|
+
logger.warning("Restart after update failed", exc_info=True)
|
|
133
|
+
console.print(
|
|
134
|
+
f"[bold yellow]Warning:[/bold yellow] Updated to v{latest} but "
|
|
135
|
+
"the automatic restart failed. Restart dcode manually to use "
|
|
136
|
+
"the new version.",
|
|
137
|
+
highlight=False,
|
|
138
|
+
)
|
|
139
|
+
return
|
|
140
|
+
cmd = upgrade_command()
|
|
141
|
+
detail = f": {escape(output[:200])}" if output else ""
|
|
142
|
+
console.print(
|
|
143
|
+
f"[bold red]Auto-update failed{detail}[/bold red]\n"
|
|
144
|
+
f"Run manually: [cyan]{cmd}[/cyan]\n"
|
|
145
|
+
f"Continuing with v{cli_version}.",
|
|
146
|
+
markup=True,
|
|
147
|
+
highlight=False,
|
|
148
|
+
)
|
|
149
|
+
except SystemExit:
|
|
150
|
+
# Process replacement (and test doubles that simulate it) must not be
|
|
151
|
+
# swallowed by the fail-soft handler below.
|
|
152
|
+
raise
|
|
153
|
+
except Exception:
|
|
154
|
+
logger.warning("Startup auto-update failed", exc_info=True)
|
|
155
|
+
console.print(
|
|
156
|
+
"[bold yellow]Warning:[/bold yellow] Auto-update failed before startup; "
|
|
157
|
+
"continuing with the installed version."
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
38
161
|
def _resolve_agent_arg(args: argparse.Namespace) -> str:
|
|
39
162
|
"""Resolve the final agent identifier from parsed CLI args.
|
|
40
163
|
|
|
@@ -2491,6 +2614,7 @@ def cli_main() -> None:
|
|
|
2491
2614
|
sys.exit(130)
|
|
2492
2615
|
sys.exit(exit_code)
|
|
2493
2616
|
else:
|
|
2617
|
+
_run_startup_auto_update(console)
|
|
2494
2618
|
# Resolve recent-agent fallback only for actual session launches.
|
|
2495
2619
|
assistant_id = _resolve_agent_arg(args)
|
|
2496
2620
|
# Interactive mode - handle thread resume
|
|
@@ -158,6 +158,49 @@ def _latest_from_releases(
|
|
|
158
158
|
return best_str
|
|
159
159
|
|
|
160
160
|
|
|
161
|
+
def get_cached_update_available() -> tuple[bool, str | None]:
|
|
162
|
+
"""Check for updates using only a fresh local cache entry.
|
|
163
|
+
|
|
164
|
+
This is the startup fast path: it never contacts PyPI. Stale, missing,
|
|
165
|
+
corrupt, or unparsable cache data is treated as "no cached update answer" so
|
|
166
|
+
callers can launch immediately and let a background update check refresh the
|
|
167
|
+
cache later.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
A `(available, latest)` tuple. `latest` is `None` when the cache cannot
|
|
171
|
+
provide a fresh answer.
|
|
172
|
+
"""
|
|
173
|
+
try:
|
|
174
|
+
installed = _parse_version(__version__)
|
|
175
|
+
except InvalidVersion:
|
|
176
|
+
logger.warning(
|
|
177
|
+
"Installed version %r is not PEP 440 compliant; "
|
|
178
|
+
"cache-only update checks disabled for this install",
|
|
179
|
+
__version__,
|
|
180
|
+
)
|
|
181
|
+
return False, None
|
|
182
|
+
|
|
183
|
+
cache_key = "version_prerelease" if installed.is_prerelease else "version"
|
|
184
|
+
try:
|
|
185
|
+
if not CACHE_FILE.exists():
|
|
186
|
+
return False, None
|
|
187
|
+
data = json.loads(CACHE_FILE.read_text(encoding="utf-8"))
|
|
188
|
+
if not isinstance(data, dict):
|
|
189
|
+
return False, None
|
|
190
|
+
checked_at = data.get("checked_at")
|
|
191
|
+
if not isinstance(checked_at, (int, float)):
|
|
192
|
+
return False, None
|
|
193
|
+
if time.time() - checked_at >= CACHE_TTL:
|
|
194
|
+
return False, None
|
|
195
|
+
value = data.get(cache_key)
|
|
196
|
+
if not isinstance(value, str):
|
|
197
|
+
return False, None
|
|
198
|
+
return _parse_version(value) > installed, value
|
|
199
|
+
except (OSError, json.JSONDecodeError, TypeError, InvalidVersion):
|
|
200
|
+
logger.debug("Failed to read cache-only update answer", exc_info=True)
|
|
201
|
+
return False, None
|
|
202
|
+
|
|
203
|
+
|
|
161
204
|
def get_latest_version(
|
|
162
205
|
*,
|
|
163
206
|
bypass_cache: bool = False,
|
|
@@ -35,19 +35,18 @@ if TYPE_CHECKING:
|
|
|
35
35
|
from textual.events import Click
|
|
36
36
|
|
|
37
37
|
from deepagents_code import auth_store, theme
|
|
38
|
+
from deepagents_code.auth_display import format_auth_badge
|
|
38
39
|
from deepagents_code.config import get_glyphs, is_ascii_mode
|
|
39
40
|
from deepagents_code.model_config import (
|
|
40
41
|
PROVIDER_API_KEY_ENV,
|
|
41
42
|
PROVIDERS_DOCS_URL as _PROVIDERS_DOCS_URL,
|
|
42
43
|
ModelConfig,
|
|
43
|
-
ProviderAuthSource,
|
|
44
44
|
clear_caches,
|
|
45
45
|
get_available_models,
|
|
46
46
|
get_base_url_env_var,
|
|
47
47
|
get_credential_env_var,
|
|
48
48
|
get_default_base_url_env,
|
|
49
49
|
get_provider_auth_status,
|
|
50
|
-
resolved_env_var_name,
|
|
51
50
|
)
|
|
52
51
|
from deepagents_code.widgets._links import open_style_link
|
|
53
52
|
|
|
@@ -671,20 +670,7 @@ class AuthManagerScreen(ModalScreen[None]):
|
|
|
671
670
|
A composed `Content` with the provider name and a status badge.
|
|
672
671
|
"""
|
|
673
672
|
status = get_provider_auth_status(provider)
|
|
674
|
-
|
|
675
|
-
if status.source is ProviderAuthSource.STORED:
|
|
676
|
-
badge = Content.styled("[stored]", "bold $success")
|
|
677
|
-
elif status.source is ProviderAuthSource.ENV:
|
|
678
|
-
if env_var:
|
|
679
|
-
badge = Content.assemble(
|
|
680
|
-
("[env: ", "$text-muted"),
|
|
681
|
-
Content.styled(resolved_env_var_name(env_var), "$text-muted"),
|
|
682
|
-
("]", "$text-muted"),
|
|
683
|
-
)
|
|
684
|
-
else:
|
|
685
|
-
badge = Content.styled("[env]", "$text-muted")
|
|
686
|
-
else:
|
|
687
|
-
badge = Content.styled("[missing]", "bold $warning")
|
|
673
|
+
badge = format_auth_badge(status)
|
|
688
674
|
return Content.assemble(
|
|
689
675
|
Content.from_markup("$provider", provider=provider),
|
|
690
676
|
" ",
|