deepagents-cli 0.0.46__tar.gz → 0.0.47__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_cli-0.0.46 → deepagents_cli-0.0.47}/CHANGELOG.md +10 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/PKG-INFO +4 -4
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/README.md +2 -2
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/_version.py +1 -1
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/app.py +251 -19
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/config.py +10 -6
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/model_config.py +28 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/theme.py +69 -58
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/theme_selector.py +3 -3
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/welcome.py +15 -32
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/pyproject.toml +2 -2
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/scripts/install.sh +1 -1
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_app.py +9 -6
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_chat_input.py +13 -2
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_model_switch.py +137 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_theme.py +22 -20
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_welcome.py +0 -15
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/uv.lock +5 -5
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/.gitignore +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/DEV.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/Makefile +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/THREAT_MODEL.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/__main__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/_ask_user_types.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/_cli_context.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/_debug.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/_env_vars.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/_git.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/_server_config.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/_session_stats.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/_testing_models.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/_textual_patches.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/agent.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/app.tcss +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/ask_user.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/built_in_skills/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/built_in_skills/remember/SKILL.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/built_in_skills/skill-creator/SKILL.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/clipboard.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/command_registry.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/configurable_model.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/default_agent_prompt.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/bundler.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/commands.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/config.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/frontend_dist/assets/anonymous-B9UzAXQd.js +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/frontend_dist/assets/clerk-5xHgyQyG.js +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/frontend_dist/assets/highlighted-body-OFNGDK62-rX-7qT8o.js +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/frontend_dist/assets/index-DM3gptpu.js +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/frontend_dist/assets/index-Ddy7F6KI.css +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/frontend_dist/assets/supabase-S6NACDgm.js +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/frontend_dist/index.html +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/frontend_dist/logo-dark.svg +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/frontend_dist/logo-light.svg +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/deploy/templates.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/editor.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/extras_info.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/file_ops.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/formatting.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/hooks.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/input.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/integrations/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/integrations/sandbox_factory.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/integrations/sandbox_provider.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/local_context.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/main.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/mcp_auth.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/mcp_commands.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/mcp_providers/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/mcp_providers/_registry.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/mcp_providers/base.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/mcp_providers/github.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/mcp_providers/slack.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/mcp_tools.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/mcp_trust.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/media_utils.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/non_interactive.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/notifications.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/offload.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/output.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/project_utils.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/py.typed +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/remote_client.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/server.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/server_graph.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/server_manager.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/sessions.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/skills/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/skills/commands.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/skills/invocation.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/skills/load.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/subagents.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/system_prompt.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/terminal_capabilities.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/textual_adapter.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/token_state.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/tool_display.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/tools.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/ui.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/unicode_security.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/update_check.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/_links.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/agent_selector.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/approval.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/ask_user.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/autocomplete.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/chat_input.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/diff.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/history.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/loading.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/mcp_viewer.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/message_store.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/messages.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/model_selector.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/notification_center.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/notification_detail.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/notification_settings.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/status.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/thread_selector.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/tool_renderers.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/tool_widgets.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/deepagents_cli/widgets/update_available.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/deploy-content-writer/.env.example +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/deploy-content-writer/skills/blog-post/SKILL.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/deploy-content-writer/skills/social-media/SKILL.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/deploy-content-writer/user/context.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/deploy-content-writer/user/preferences.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/skills/arxiv-search/SKILL.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/skills/arxiv-search/arxiv_search.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/skills/langgraph-docs/SKILL.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/skills/skill-creator/SKILL.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/examples/skills/web-research/SKILL.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/.nvmrc +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/index.html +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/package-lock.json +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/package.json +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/postcss.config.js +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/public/logo-dark.svg +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/public/logo-light.svg +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/App.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/ThemeProvider.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/auth/anonymous.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/auth/clerk.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/auth/loader.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/auth/supabase.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/auth/types.ts +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/AppHeader.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/FilePanels.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/MessageList.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/SubagentActivity.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/ThreadPicker.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/TodosPanel.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/ToolCallCard.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/toolcalls/FileToolCard.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/toolcalls/SearchCard.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/toolcalls/ThinkCard.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/toolcalls/TodosCard.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/components/toolcalls/index.ts +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/constants.ts +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/index.css +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/main.tsx +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/runtimeConfig.ts +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/types.ts +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/src/vite-env.d.ts +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/tsconfig.json +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/frontend/vite.config.ts +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/images/cli.png +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/scripts/check_imports.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/scripts/debug_server.sh +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/README.md +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/integration_tests/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/integration_tests/benchmarks/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/integration_tests/conftest.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/integration_tests/test_acp_mode.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/integration_tests/test_compact_resume.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/integration_tests/test_sandbox_factory.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/integration_tests/test_sandbox_operations.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/conftest.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/deploy/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/deploy/test_bundler.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/deploy/test_commands.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/deploy/test_config.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/deploy/test_frontend_bundle.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/deploy/test_frontend_config.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/skills/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/skills/test_commands.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/skills/test_load.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/skills/test_skills_json.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_agent.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_agent_friendly.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_agent_selector.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_approval.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_args.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_ask_user.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_ask_user_middleware.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_autocomplete.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_charset.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_command_registry.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_compact_tool.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_config.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_configurable_model.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_debug.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_editor.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_end_to_end.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_env_vars.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_exception_handling.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_extras_info.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_file_ops.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_formatting.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_git.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_history.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_hooks.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_imports.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_input_parsing.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_links.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_loading.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_local_context.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_main.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_main_acp_mode.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_main_args.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_mcp_auth.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_mcp_commands.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_mcp_tools.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_mcp_trust.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_mcp_viewer.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_media_utils.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_message_store.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_messages.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_model_config.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_model_selector.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_non_interactive.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_notification_center.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_notification_detail.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_notifications.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_offload.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_offload_dict_messages.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_output.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_reload.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_remote_client.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_sandbox_factory.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_server.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_server_config.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_server_graph.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_server_helpers.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_server_manager.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_session_stats.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_sessions.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_shell_allow_list.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_skill_invocation.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_status.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_subagents.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_terminal_capabilities.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_textual_adapter.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_textual_patches.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_thread_selector.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_token_tracker.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_tool_display.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_ui.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_unicode_security.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_update_available.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_update_check.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/test_version.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/tools/__init__.py +0 -0
- {deepagents_cli-0.0.46 → deepagents_cli-0.0.47}/tests/unit_tests/tools/test_fetch_url.py +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.47](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.46...deepagents-cli==0.0.47) (2026-05-01)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* Auto-discover Textual built-in themes ([#3068](https://github.com/langchain-ai/deepagents/issues/3068)) ([27894e3](https://github.com/langchain-ai/deepagents/commit/27894e336f20f270fb4bb685bf979f24b9bea0bc))
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Recover from failed server startup via `/model` ([#3056](https://github.com/langchain-ai/deepagents/issues/3056)) ([6ee375e](https://github.com/langchain-ai/deepagents/commit/6ee375e979b672cd1d172669f0e5a7a665431d7c))
|
|
12
|
+
|
|
3
13
|
## [0.0.46](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.45...deepagents-cli==0.0.46) (2026-04-30)
|
|
4
14
|
|
|
5
15
|
### Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagents-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.47
|
|
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/
|
|
@@ -51,7 +51,7 @@ Requires-Dist: rich<15.0.0,>=14.0.0
|
|
|
51
51
|
Requires-Dist: tavily-python<1.0.0,>=0.7.21
|
|
52
52
|
Requires-Dist: textual-autocomplete<5.0.0,>=3.0.0
|
|
53
53
|
Requires-Dist: textual-speedups<1.0.0,>=0.2.1
|
|
54
|
-
Requires-Dist: textual<9.0.0,>=8.
|
|
54
|
+
Requires-Dist: textual<9.0.0,>=8.2.5
|
|
55
55
|
Requires-Dist: tomli-w<2.0.0,>=1.0.0
|
|
56
56
|
Requires-Dist: uuid-utils<1.0.0,>=0.10.0
|
|
57
57
|
Provides-Extra: agentcore
|
|
@@ -141,13 +141,13 @@ Description-Content-Type: text/markdown
|
|
|
141
141
|
## Quick Install
|
|
142
142
|
|
|
143
143
|
```bash
|
|
144
|
-
curl -LsSf https://
|
|
144
|
+
curl -LsSf https://langch.in/gh-da-cli | bash
|
|
145
145
|
```
|
|
146
146
|
|
|
147
147
|
```bash
|
|
148
148
|
# With model provider extras
|
|
149
149
|
# OpenAI, Anthropic, and Gemini are included by default
|
|
150
|
-
DEEPAGENTS_EXTRAS="nvidia,ollama" curl -LsSf https://
|
|
150
|
+
DEEPAGENTS_EXTRAS="nvidia,ollama" curl -LsSf https://langch.in/gh-da-cli | bash
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
Or install directly with `uv`:
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
## Quick Install
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
curl -LsSf https://
|
|
15
|
+
curl -LsSf https://langch.in/gh-da-cli | bash
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
# With model provider extras
|
|
20
20
|
# OpenAI, Anthropic, and Gemini are included by default
|
|
21
|
-
DEEPAGENTS_EXTRAS="nvidia,ollama" curl -LsSf https://
|
|
21
|
+
DEEPAGENTS_EXTRAS="nvidia,ollama" curl -LsSf https://langch.in/gh-da-cli | bash
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
Or install directly with `uv`:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Version information and lightweight constants for `deepagents-cli`."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.0.
|
|
3
|
+
__version__ = "0.0.47" # x-release-please-version
|
|
4
4
|
|
|
5
5
|
DOCS_URL = "https://docs.langchain.com/oss/python/deepagents/cli"
|
|
6
6
|
"""URL for `deepagents-cli` documentation."""
|
|
@@ -192,7 +192,10 @@ def _load_theme_preference() -> str:
|
|
|
192
192
|
return theme.DEFAULT_THEME
|
|
193
193
|
|
|
194
194
|
name = data.get("ui", {}).get("theme")
|
|
195
|
-
|
|
195
|
+
# Migrate legacy `textual-ansi` preference (pre-Textual 8.2.5) to `ansi-light`.
|
|
196
|
+
if name == "textual-ansi":
|
|
197
|
+
name = "ansi-light"
|
|
198
|
+
if isinstance(name, str) and name in theme.get_registry():
|
|
196
199
|
return name
|
|
197
200
|
if isinstance(name, str):
|
|
198
201
|
logger.warning(
|
|
@@ -211,7 +214,7 @@ def save_theme_preference(name: str) -> bool:
|
|
|
211
214
|
Returns:
|
|
212
215
|
`True` if the preference was saved, `False` if any error occurred.
|
|
213
216
|
"""
|
|
214
|
-
if name not in theme.
|
|
217
|
+
if name not in theme.get_registry():
|
|
215
218
|
logger.warning("Refusing to save unknown theme '%s'", name)
|
|
216
219
|
return False
|
|
217
220
|
|
|
@@ -428,6 +431,24 @@ def _truncate(text: str, *, limit: int) -> str:
|
|
|
428
431
|
return text[: limit - 1].rstrip() + "…"
|
|
429
432
|
|
|
430
433
|
|
|
434
|
+
def _log_task_exception(task: asyncio.Task[Any]) -> None:
|
|
435
|
+
"""Done-callback that surfaces unhandled exceptions from fire-and-forget tasks.
|
|
436
|
+
|
|
437
|
+
Default `asyncio` behavior is to log "Task exception was never retrieved"
|
|
438
|
+
only when the task is GC'd — easy to miss. This callback runs at task
|
|
439
|
+
completion and routes failures through `logger.warning` with `exc_info`,
|
|
440
|
+
matching the codebase pattern at `_finalize_git_branch_refresh`. Use
|
|
441
|
+
when scheduling a coroutine via `asyncio.create_task` whose result is
|
|
442
|
+
not awaited (e.g. event-handler cleanup, single-fire mounts).
|
|
443
|
+
"""
|
|
444
|
+
try:
|
|
445
|
+
task.result()
|
|
446
|
+
except asyncio.CancelledError:
|
|
447
|
+
pass
|
|
448
|
+
except Exception:
|
|
449
|
+
logger.warning("Background task failed unexpectedly", exc_info=True)
|
|
450
|
+
|
|
451
|
+
|
|
431
452
|
def _format_startup_error(error: BaseException) -> str:
|
|
432
453
|
"""Format a server-startup exception for the welcome banner.
|
|
433
454
|
|
|
@@ -905,6 +926,25 @@ class DeepAgentsApp(App):
|
|
|
905
926
|
Shown in place of the generic 'Agent not configured' message.
|
|
906
927
|
"""
|
|
907
928
|
|
|
929
|
+
self._server_startup_missing_credentials_provider: str | None = None
|
|
930
|
+
"""Set to the offending provider name when startup failed with
|
|
931
|
+
`MissingCredentialsError`; `None` otherwise. Gates the `/model`
|
|
932
|
+
recovery hint without string-matching on the formatted error.
|
|
933
|
+
"""
|
|
934
|
+
|
|
935
|
+
self._retry_status_widget: AppMessage | None = None
|
|
936
|
+
"""Transient "Retrying startup with X…" breadcrumb. Mounted via
|
|
937
|
+
`_mount_before_queued` (not `_mount_message`) because it is ephemeral
|
|
938
|
+
state and must not appear in scrollback or serialized history.
|
|
939
|
+
"""
|
|
940
|
+
|
|
941
|
+
self._startup_failure_widget: ErrorMessage | None = None
|
|
942
|
+
"""Transient chat surface for the most recent server-startup failure.
|
|
943
|
+
Mounted by `on_deep_agents_app_server_start_failed`; removed on
|
|
944
|
+
`ServerReady` so a successful `/model` retry doesn't leave the stale
|
|
945
|
+
error dangling in scrollback.
|
|
946
|
+
"""
|
|
947
|
+
|
|
908
948
|
self._quit_pending = False
|
|
909
949
|
"""True after a first `Ctrl+C` so a second press within the window quits."""
|
|
910
950
|
|
|
@@ -1181,6 +1221,14 @@ class DeepAgentsApp(App):
|
|
|
1181
1221
|
# Focus the input immediately so the cursor is visible on first paint
|
|
1182
1222
|
self._chat_input.focus_input()
|
|
1183
1223
|
|
|
1224
|
+
# Pre-import `html.entities` on the main thread before the worker
|
|
1225
|
+
# starts. Python 3.14 replaced the global import lock with per-module
|
|
1226
|
+
# locks; a worker importing `markdown_it` (which transitively pulls
|
|
1227
|
+
# `html.entities`) can race main-thread code looking up `html` *while
|
|
1228
|
+
# `html` itself is still being initialized*, raising `KeyError: 'html'`
|
|
1229
|
+
# from `_find_and_load_unlocked`.
|
|
1230
|
+
import html.entities # noqa: F401
|
|
1231
|
+
|
|
1184
1232
|
# Prewarm heavy imports in a thread while the first frame renders.
|
|
1185
1233
|
# The user can't type yet, so GIL contention is harmless. By the
|
|
1186
1234
|
# time _post_paint_init fires its inline imports are dict lookups.
|
|
@@ -1742,6 +1790,28 @@ class DeepAgentsApp(App):
|
|
|
1742
1790
|
self._agent = event.agent
|
|
1743
1791
|
self._server_proc = event.server_proc
|
|
1744
1792
|
self._mcp_server_info = event.mcp_server_info
|
|
1793
|
+
|
|
1794
|
+
# Drop transient failure-state widgets — banner state and the agent
|
|
1795
|
+
# response now convey "connected", so the prior error and breadcrumb
|
|
1796
|
+
# would just dangle in scrollback.
|
|
1797
|
+
for attr in ("_retry_status_widget", "_startup_failure_widget"):
|
|
1798
|
+
widget = getattr(self, attr)
|
|
1799
|
+
if widget is None:
|
|
1800
|
+
continue
|
|
1801
|
+
setattr(self, attr, None)
|
|
1802
|
+
|
|
1803
|
+
async def _drop(w: Widget = widget) -> None:
|
|
1804
|
+
# Mount may still be in flight when `ServerReady` arrives;
|
|
1805
|
+
# short-circuit on un-attached widgets instead of raising.
|
|
1806
|
+
# `NoMatches`/`ScreenStackError` cover later-stage detach
|
|
1807
|
+
# races (screen torn down mid-removal).
|
|
1808
|
+
if not w.is_attached:
|
|
1809
|
+
return
|
|
1810
|
+
with suppress(NoMatches, ScreenStackError):
|
|
1811
|
+
await w.remove()
|
|
1812
|
+
|
|
1813
|
+
task = asyncio.create_task(_drop())
|
|
1814
|
+
task.add_done_callback(_log_task_exception)
|
|
1745
1815
|
self._mcp_tool_count = sum(len(s.tools) for s in (event.mcp_server_info or []))
|
|
1746
1816
|
self._mcp_unauthenticated = sum(
|
|
1747
1817
|
1 for s in (event.mcp_server_info or []) if s.status == "unauthenticated"
|
|
@@ -1797,6 +1867,7 @@ class DeepAgentsApp(App):
|
|
|
1797
1867
|
def on_deep_agents_app_server_start_failed(self, event: ServerStartFailed) -> None:
|
|
1798
1868
|
"""Handle background server startup failure."""
|
|
1799
1869
|
from deepagents_cli.mcp_tools import MCPConfigError
|
|
1870
|
+
from deepagents_cli.model_config import MissingCredentialsError
|
|
1800
1871
|
|
|
1801
1872
|
self._connecting = False
|
|
1802
1873
|
if isinstance(event.error, MCPConfigError):
|
|
@@ -1804,22 +1875,68 @@ class DeepAgentsApp(App):
|
|
|
1804
1875
|
self._server_startup_error = str(event.error)
|
|
1805
1876
|
else:
|
|
1806
1877
|
self._server_startup_error = _format_startup_error(event.error)
|
|
1878
|
+
|
|
1879
|
+
# Stash the provider for the `/model` recovery hint. Reset on every
|
|
1880
|
+
# failure so a non-credentials retry-failure clears the prior flag.
|
|
1881
|
+
self._server_startup_missing_credentials_provider = (
|
|
1882
|
+
event.error.provider
|
|
1883
|
+
if isinstance(event.error, MissingCredentialsError)
|
|
1884
|
+
else None
|
|
1885
|
+
)
|
|
1807
1886
|
logger.error("Server startup failed: %s", event.error, exc_info=event.error)
|
|
1808
|
-
|
|
1887
|
+
|
|
1888
|
+
# Drop the banner's connecting spinner — chat surface owns the error.
|
|
1809
1889
|
try:
|
|
1810
1890
|
banner = self.query_one("#welcome-banner", WelcomeBanner)
|
|
1811
|
-
banner.
|
|
1891
|
+
banner.set_idle()
|
|
1812
1892
|
except NoMatches:
|
|
1813
1893
|
logger.warning("Welcome banner not found during server failure transition")
|
|
1814
1894
|
|
|
1815
|
-
#
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
self._queued_widgets.clear()
|
|
1895
|
+
# Keep any queued messages and widgets in place — `/model` retry can
|
|
1896
|
+
# bring the server up, at which point `_run_session_start_sequence`
|
|
1897
|
+
# drains them. Deferred actions (model/thread switches queued during
|
|
1898
|
+
# the initial connect) are dropped because the failure invalidates
|
|
1899
|
+
# their assumptions; the user can re-issue them after recovery.
|
|
1821
1900
|
self._deferred_actions.clear()
|
|
1822
1901
|
|
|
1902
|
+
# Failure surfaces only in chat — keeps recovery hint adjacent to the
|
|
1903
|
+
# input. Banner is set to idle above to drop the connecting spinner.
|
|
1904
|
+
text = f"Server failed to start: {self._server_startup_error}"
|
|
1905
|
+
if (
|
|
1906
|
+
self._server_startup_missing_credentials_provider is not None
|
|
1907
|
+
and self._server_kwargs is not None
|
|
1908
|
+
):
|
|
1909
|
+
text += (
|
|
1910
|
+
"\n\nHint: run `/model <provider>:<model>` to retry "
|
|
1911
|
+
"startup with a provider you have credentials for."
|
|
1912
|
+
)
|
|
1913
|
+
|
|
1914
|
+
async def _mount_failure() -> None:
|
|
1915
|
+
# Drop any prior failure widget (re-entrant on retry-then-fail).
|
|
1916
|
+
prior = self._startup_failure_widget
|
|
1917
|
+
self._startup_failure_widget = None
|
|
1918
|
+
if prior is not None and prior.is_attached:
|
|
1919
|
+
with suppress(NoMatches, ScreenStackError):
|
|
1920
|
+
await prior.remove()
|
|
1921
|
+
|
|
1922
|
+
try:
|
|
1923
|
+
messages = self.query_one("#messages", Container)
|
|
1924
|
+
except (NoMatches, ScreenStackError):
|
|
1925
|
+
return
|
|
1926
|
+
if not messages.is_attached:
|
|
1927
|
+
return
|
|
1928
|
+
|
|
1929
|
+
new_widget = ErrorMessage(text)
|
|
1930
|
+
# Mount before storing the reference so `ServerReady` racing this
|
|
1931
|
+
# await cannot observe a half-mounted widget.
|
|
1932
|
+
await self._mount_before_queued(messages, new_widget)
|
|
1933
|
+
self._startup_failure_widget = new_widget
|
|
1934
|
+
|
|
1935
|
+
# Fire-and-forget mount: this is the *only* failure surface, so log
|
|
1936
|
+
# any exception loudly via `_log_task_exception`.
|
|
1937
|
+
task = asyncio.create_task(_mount_failure())
|
|
1938
|
+
task.add_done_callback(_log_task_exception)
|
|
1939
|
+
|
|
1823
1940
|
async def _await_prewarm_imports(self) -> None:
|
|
1824
1941
|
"""Wait for prewarm imports before re-entering their module graph.
|
|
1825
1942
|
|
|
@@ -3065,14 +3182,17 @@ class DeepAgentsApp(App):
|
|
|
3065
3182
|
)
|
|
3066
3183
|
return
|
|
3067
3184
|
|
|
3068
|
-
# If the app is busy
|
|
3069
|
-
#
|
|
3070
|
-
#
|
|
3185
|
+
# If the app is busy, still sequencing startup work, or holding a
|
|
3186
|
+
# post-failure recovery state (server hasn't come up yet but `/model`
|
|
3187
|
+
# retry is still possible), enqueue instead of processing. Messages
|
|
3188
|
+
# queued in any of these states are drained once the session reaches
|
|
3189
|
+
# its first stable idle/running state.
|
|
3071
3190
|
if (
|
|
3072
3191
|
self._agent_running
|
|
3073
3192
|
or self._shell_running
|
|
3074
3193
|
or self._connecting
|
|
3075
3194
|
or self._startup_sequence_running
|
|
3195
|
+
or self._server_startup_error is not None
|
|
3076
3196
|
):
|
|
3077
3197
|
if mode == "command" and self._can_bypass_queue(value.lower().strip()):
|
|
3078
3198
|
await self._process_message(value, mode)
|
|
@@ -4120,11 +4240,10 @@ class DeepAgentsApp(App):
|
|
|
4120
4240
|
self._run_agent_task(message, message_kwargs=message_kwargs),
|
|
4121
4241
|
exclusive=False,
|
|
4122
4242
|
)
|
|
4123
|
-
elif self._server_startup_error:
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
else:
|
|
4243
|
+
elif not self._server_startup_error:
|
|
4244
|
+
# When a server-startup failure is in flight, the chat
|
|
4245
|
+
# `ErrorMessage` mounted by `on_deep_agents_app_server_start_failed`
|
|
4246
|
+
# is the single source of truth — don't duplicate it here.
|
|
4128
4247
|
await self._mount_message(
|
|
4129
4248
|
AppMessage("Agent not configured for this session.")
|
|
4130
4249
|
)
|
|
@@ -5367,7 +5486,7 @@ class DeepAgentsApp(App):
|
|
|
5367
5486
|
|
|
5368
5487
|
def _register_custom_themes(self) -> None:
|
|
5369
5488
|
"""Register all custom themes (built-in LC + user-defined) with Textual."""
|
|
5370
|
-
for name, entry in theme.
|
|
5489
|
+
for name, entry in theme.get_registry().items():
|
|
5371
5490
|
if entry.custom:
|
|
5372
5491
|
c = entry.colors
|
|
5373
5492
|
try:
|
|
@@ -6530,6 +6649,18 @@ class DeepAgentsApp(App):
|
|
|
6530
6649
|
timeout=3,
|
|
6531
6650
|
)
|
|
6532
6651
|
return
|
|
6652
|
+
# Recover from a failed startup (e.g., `MissingCredentialsError`).
|
|
6653
|
+
# The server never came up, so the only way out without
|
|
6654
|
+
# restarting the CLI is to retry startup with the new model.
|
|
6655
|
+
# Only valid for CLI-owned servers.
|
|
6656
|
+
if (
|
|
6657
|
+
self._server_startup_error is not None
|
|
6658
|
+
and self._server_kwargs is not None
|
|
6659
|
+
):
|
|
6660
|
+
await self._retry_startup_with_model(
|
|
6661
|
+
model_spec, extra_kwargs=extra_kwargs
|
|
6662
|
+
)
|
|
6663
|
+
return
|
|
6533
6664
|
await self._mount_message(
|
|
6534
6665
|
ErrorMessage("Model switching requires a server-backed session.")
|
|
6535
6666
|
)
|
|
@@ -6622,6 +6753,107 @@ class DeepAgentsApp(App):
|
|
|
6622
6753
|
finally:
|
|
6623
6754
|
self._model_switching = False
|
|
6624
6755
|
|
|
6756
|
+
async def _retry_startup_with_model(
|
|
6757
|
+
self,
|
|
6758
|
+
model_spec: str,
|
|
6759
|
+
*,
|
|
6760
|
+
extra_kwargs: dict[str, Any] | None = None,
|
|
6761
|
+
) -> None:
|
|
6762
|
+
"""Retry deferred server startup after a failed initial startup.
|
|
6763
|
+
|
|
6764
|
+
Exists because the server never came up (typically a
|
|
6765
|
+
`MissingCredentialsError`), so the only escape without restarting
|
|
6766
|
+
the CLI is re-running the deferred startup worker with a new spec.
|
|
6767
|
+
|
|
6768
|
+
Args:
|
|
6769
|
+
model_spec: The new model specification (`provider:model` or bare
|
|
6770
|
+
model name for auto-detection).
|
|
6771
|
+
extra_kwargs: Extra constructor kwargs from `--model-params`.
|
|
6772
|
+
"""
|
|
6773
|
+
from deepagents_cli.config import detect_provider
|
|
6774
|
+
from deepagents_cli.model_config import (
|
|
6775
|
+
ModelSpec,
|
|
6776
|
+
get_credential_env_var,
|
|
6777
|
+
has_provider_credentials,
|
|
6778
|
+
)
|
|
6779
|
+
|
|
6780
|
+
if self._server_kwargs is None:
|
|
6781
|
+
await self._mount_message(
|
|
6782
|
+
ErrorMessage("Cannot retry startup: server is not CLI-owned.")
|
|
6783
|
+
)
|
|
6784
|
+
return
|
|
6785
|
+
|
|
6786
|
+
parsed = ModelSpec.try_parse(model_spec)
|
|
6787
|
+
if parsed:
|
|
6788
|
+
provider: str | None = parsed.provider
|
|
6789
|
+
model_name = parsed.model
|
|
6790
|
+
else:
|
|
6791
|
+
model_name = model_spec
|
|
6792
|
+
provider = detect_provider(model_spec)
|
|
6793
|
+
|
|
6794
|
+
# Tri-state credentials check (`None` = unknown provider, treated as
|
|
6795
|
+
# proceed); bail early so retrying with still-missing creds doesn't
|
|
6796
|
+
# loop right back into the same `MissingCredentialsError`.
|
|
6797
|
+
has_creds = has_provider_credentials(provider) if provider else None
|
|
6798
|
+
if has_creds is False and provider is not None:
|
|
6799
|
+
env_var = get_credential_env_var(provider)
|
|
6800
|
+
detail = (
|
|
6801
|
+
f"{env_var} is not set or is empty"
|
|
6802
|
+
if env_var
|
|
6803
|
+
else (
|
|
6804
|
+
f"provider '{provider}' is not recognized. "
|
|
6805
|
+
"Add it to ~/.deepagents/config.toml with an "
|
|
6806
|
+
"api_key_env field"
|
|
6807
|
+
)
|
|
6808
|
+
)
|
|
6809
|
+
await self._mount_message(ErrorMessage(f"Missing credentials: {detail}"))
|
|
6810
|
+
return
|
|
6811
|
+
|
|
6812
|
+
display = model_spec
|
|
6813
|
+
if provider and not parsed:
|
|
6814
|
+
display = f"{provider}:{model_name}"
|
|
6815
|
+
|
|
6816
|
+
new_model_kwargs: dict[str, Any] = {
|
|
6817
|
+
"model_spec": display,
|
|
6818
|
+
"extra_kwargs": extra_kwargs,
|
|
6819
|
+
"profile_overrides": self._profile_override,
|
|
6820
|
+
}
|
|
6821
|
+
self._model_kwargs = new_model_kwargs
|
|
6822
|
+
self._server_kwargs["model_name"] = display
|
|
6823
|
+
if extra_kwargs is not None:
|
|
6824
|
+
self._server_kwargs["model_params"] = extra_kwargs
|
|
6825
|
+
|
|
6826
|
+
self._server_startup_error = None
|
|
6827
|
+
self._server_startup_missing_credentials_provider = None
|
|
6828
|
+
self._connecting = True
|
|
6829
|
+
try:
|
|
6830
|
+
banner = self.query_one("#welcome-banner", WelcomeBanner)
|
|
6831
|
+
banner.set_connecting()
|
|
6832
|
+
except (NoMatches, ScreenStackError):
|
|
6833
|
+
logger.debug("Welcome banner not found during startup retry", exc_info=True)
|
|
6834
|
+
|
|
6835
|
+
if self._retry_status_widget is not None:
|
|
6836
|
+
with suppress(NoMatches, ScreenStackError):
|
|
6837
|
+
await self._retry_status_widget.remove()
|
|
6838
|
+
self._retry_status_widget = None
|
|
6839
|
+
try:
|
|
6840
|
+
messages = self.query_one("#messages", Container)
|
|
6841
|
+
except (NoMatches, ScreenStackError):
|
|
6842
|
+
messages = None
|
|
6843
|
+
if messages is not None and messages.is_attached:
|
|
6844
|
+
new_widget = AppMessage(f"Retrying startup with {display}…")
|
|
6845
|
+
# Mount before storing the reference so `on_deep_agents_app_server_ready`
|
|
6846
|
+
# cannot observe a half-mounted widget if it races during this await.
|
|
6847
|
+
await self._mount_before_queued(messages, new_widget)
|
|
6848
|
+
self._retry_status_widget = new_widget
|
|
6849
|
+
logger.info("Retrying server startup with model %s", display)
|
|
6850
|
+
|
|
6851
|
+
self.run_worker(
|
|
6852
|
+
self._start_server_background,
|
|
6853
|
+
exclusive=True,
|
|
6854
|
+
group="server-startup",
|
|
6855
|
+
)
|
|
6856
|
+
|
|
6625
6857
|
async def _set_default_model(self, model_spec: str) -> None:
|
|
6626
6858
|
"""Set the default model in config without switching the current session.
|
|
6627
6859
|
|
|
@@ -2194,9 +2194,10 @@ def create_model(
|
|
|
2194
2194
|
A `ModelResult` containing the model and its metadata.
|
|
2195
2195
|
|
|
2196
2196
|
Raises:
|
|
2197
|
-
ModelConfigError: If provider cannot be determined from the model name
|
|
2198
|
-
required provider package is not installed
|
|
2199
|
-
|
|
2197
|
+
ModelConfigError: If provider cannot be determined from the model name
|
|
2198
|
+
or required provider package is not installed.
|
|
2199
|
+
MissingCredentialsError: If no credentials are configured for the
|
|
2200
|
+
resolved provider.
|
|
2200
2201
|
|
|
2201
2202
|
Examples:
|
|
2202
2203
|
>>> model = create_model("anthropic:claude-sonnet-4-5")
|
|
@@ -2248,12 +2249,15 @@ def create_model(
|
|
|
2248
2249
|
if provider and provider not in IMPLICIT_AUTH_PROVIDERS:
|
|
2249
2250
|
cred_status = has_provider_credentials(provider)
|
|
2250
2251
|
if cred_status is False:
|
|
2251
|
-
|
|
2252
|
+
from deepagents_cli.model_config import MissingCredentialsError
|
|
2253
|
+
|
|
2254
|
+
env_var = get_credential_env_var(provider)
|
|
2255
|
+
display_env = env_var or f"<{provider} API key>"
|
|
2252
2256
|
msg = (
|
|
2253
2257
|
f"No credentials found for provider '{provider}'. "
|
|
2254
|
-
f"Please set the {
|
|
2258
|
+
f"Please set the {display_env} environment variable."
|
|
2255
2259
|
)
|
|
2256
|
-
raise
|
|
2260
|
+
raise MissingCredentialsError(msg, provider=provider, env_var=env_var)
|
|
2257
2261
|
|
|
2258
2262
|
# Provider-specific kwargs (with per-model overrides)
|
|
2259
2263
|
kwargs = _get_provider_kwargs(provider, model_name=model_name)
|
|
@@ -72,6 +72,34 @@ class ModelConfigError(Exception):
|
|
|
72
72
|
"""Raised when model configuration or creation fails."""
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
class MissingCredentialsError(ModelConfigError):
|
|
76
|
+
"""Raised when a provider is selected but its API key env var is unset.
|
|
77
|
+
|
|
78
|
+
Subclasses `ModelConfigError` so existing `except ModelConfigError` blocks
|
|
79
|
+
keep working. Carries the `provider` name and the canonical `env_var` so
|
|
80
|
+
callers can render targeted recovery hints (e.g., "set OPENAI_API_KEY" or
|
|
81
|
+
"run `/model <other_provider>:<model>`") without string-matching on the
|
|
82
|
+
formatted exception message and without re-deriving the env-var name.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self, message: str, *, provider: str, env_var: str | None = None
|
|
87
|
+
) -> None:
|
|
88
|
+
"""Initialize the error.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
message: Human-readable message describing the missing credential.
|
|
92
|
+
provider: The provider whose credentials are missing
|
|
93
|
+
(e.g., `'openai'`).
|
|
94
|
+
env_var: The canonical env var name expected to hold the
|
|
95
|
+
credential (e.g., `'OPENAI_API_KEY'`). `None` when the
|
|
96
|
+
provider has no registered env-var mapping.
|
|
97
|
+
"""
|
|
98
|
+
super().__init__(message)
|
|
99
|
+
self.provider = provider
|
|
100
|
+
self.env_var = env_var
|
|
101
|
+
|
|
102
|
+
|
|
75
103
|
@dataclass(frozen=True)
|
|
76
104
|
class ModelSpec:
|
|
77
105
|
"""A model specification in `provider:model` format.
|