deepagents-cli 0.0.33__tar.gz → 0.0.34__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.33 → deepagents_cli-0.0.34}/CHANGELOG.md +11 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/PKG-INFO +1 -1
- deepagents_cli-0.0.34/deepagents_cli/_version.py +3 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/agent.py +101 -10
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/app.py +181 -8
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/app.tcss +10 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/built_in_skills/skill-creator/SKILL.md +0 -16
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/configurable_model.py +23 -9
- deepagents_cli-0.0.34/deepagents_cli/editor.py +142 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/main.py +4 -1
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/server_graph.py +4 -1
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/autocomplete.py +1 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/chat_input.py +29 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/welcome.py +1 -0
- deepagents_cli-0.0.34/examples/skills/arxiv-search/SKILL.md +33 -0
- deepagents_cli-0.0.34/examples/skills/langgraph-docs/SKILL.md +28 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/examples/skills/skill-creator/SKILL.md +0 -16
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/examples/skills/web-research/SKILL.md +1 -26
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/pyproject.toml +1 -1
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_agent.py +131 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_app.py +458 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_chat_input.py +87 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_configurable_model.py +63 -31
- deepagents_cli-0.0.34/tests/unit_tests/test_editor.py +274 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_end_to_end.py +11 -1
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_server_graph.py +2 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/uv.lock +1 -1
- deepagents_cli-0.0.33/deepagents_cli/_version.py +0 -3
- deepagents_cli-0.0.33/examples/skills/arxiv-search/SKILL.md +0 -102
- deepagents_cli-0.0.33/examples/skills/langgraph-docs/SKILL.md +0 -35
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/.gitignore +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/Makefile +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/README.md +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/__init__.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/__main__.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/_debug.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/_server_config.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/_server_constants.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/_testing_models.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/ask_user.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/built_in_skills/__init__.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/clipboard.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/config.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/default_agent_prompt.md +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/file_ops.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/hooks.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/input.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/integrations/__init__.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/integrations/daytona.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/integrations/langsmith.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/integrations/modal.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/integrations/runloop.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/integrations/sandbox_factory.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/integrations/sandbox_provider.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/local_context.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/mcp_tools.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/mcp_trust.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/media_utils.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/model_config.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/non_interactive.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/offload.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/output.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/project_utils.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/py.typed +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/remote_client.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/server.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/server_manager.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/sessions.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/skills/__init__.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/skills/commands.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/skills/load.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/subagents.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/system_prompt.md +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/textual_adapter.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/tool_display.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/tools.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/ui.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/unicode_security.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/update_check.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/__init__.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/_links.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/approval.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/ask_user.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/diff.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/history.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/loading.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/mcp_viewer.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/message_store.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/messages.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/model_selector.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/status.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/thread_selector.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/tool_renderers.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/deepagents_cli/widgets/tool_widgets.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/examples/skills/arxiv-search/arxiv_search.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/images/cli.png +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/scripts/check_imports.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/scripts/install.sh +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/README.md +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/integration_tests/__init__.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/integration_tests/benchmarks/__init__.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/integration_tests/conftest.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/integration_tests/test_acp_mode.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/integration_tests/test_compact_resume.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/integration_tests/test_sandbox_factory.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/integration_tests/test_sandbox_operations.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/__init__.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/conftest.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/skills/__init__.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/skills/test_commands.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/skills/test_load.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/skills/test_skills_json.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_approval.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_args.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_ask_user.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_ask_user_middleware.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_autocomplete.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_charset.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_compact_tool.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_config.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_debug.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_exception_handling.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_file_ops.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_history.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_hooks.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_imports.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_input_parsing.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_local_context.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_main.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_main_acp_mode.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_main_args.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_mcp_tools.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_mcp_trust.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_mcp_viewer.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_media_utils.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_message_store.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_messages.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_model_config.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_model_selector.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_model_switch.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_non_interactive.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_offload.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_output.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_reload.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_remote_client.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_server.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_server_config.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_server_helpers.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_server_manager.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_sessions.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_shell_allow_list.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_status.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_subagents.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_textual_adapter.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_thread_selector.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_token_tracker.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_ui.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_unicode_security.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_update_check.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_version.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/test_welcome.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/tools/__init__.py +0 -0
- {deepagents_cli-0.0.33 → deepagents_cli-0.0.34}/tests/unit_tests/tools/test_fetch_url.py +0 -0
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.34](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.33...deepagents-cli==0.0.34) (2026-03-17)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* External editor support via `ctrl+x` and `/editor` ([#1861](https://github.com/langchain-ai/deepagents/issues/1861)) ([bf5d088](https://github.com/langchain-ai/deepagents/commit/bf5d088d4b3cee6c7e44c3abe3736f9972897896))
|
|
8
|
+
* Defer HITL approval menu while user is typing ([#1833](https://github.com/langchain-ai/deepagents/issues/1833)) ([1d1572e](https://github.com/langchain-ai/deepagents/commit/1d1572e40cc9f87b97832cbe2b9152c281f8ec92))
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* Resolve config-defined providers during runtime model swaps ([#1941](https://github.com/langchain-ai/deepagents/issues/1941)) ([aebc660](https://github.com/langchain-ai/deepagents/commit/aebc660321895909f6b6eb71e72a99ca7754bcf1))
|
|
13
|
+
|
|
3
14
|
## [0.0.33](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.32...deepagents-cli==0.0.33) (2026-03-16)
|
|
4
15
|
|
|
5
16
|
### Highlights
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagents-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.34
|
|
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/
|
|
@@ -7,6 +7,7 @@ import os
|
|
|
7
7
|
import re
|
|
8
8
|
import shutil
|
|
9
9
|
import tempfile
|
|
10
|
+
import tomllib
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import TYPE_CHECKING, Any
|
|
12
13
|
|
|
@@ -19,6 +20,7 @@ if TYPE_CHECKING:
|
|
|
19
20
|
from collections.abc import Callable, Sequence
|
|
20
21
|
|
|
21
22
|
from deepagents.backends.sandbox import SandboxBackendProtocol
|
|
23
|
+
from deepagents.middleware.async_subagents import AsyncSubAgent
|
|
22
24
|
from deepagents.middleware.subagents import CompiledSubAgent, SubAgent
|
|
23
25
|
from langchain.agents.middleware import InterruptOnConfig
|
|
24
26
|
from langchain.agents.middleware.types import AgentState
|
|
@@ -63,6 +65,74 @@ REQUIRE_COMPACT_TOOL_APPROVAL: bool = True
|
|
|
63
65
|
"""When `True`, `compact_conversation` requires HITL approval like other gated tools."""
|
|
64
66
|
|
|
65
67
|
|
|
68
|
+
def load_async_subagents(config_path: Path | None = None) -> list[AsyncSubAgent]:
|
|
69
|
+
"""Load async subagent definitions from `config.toml`.
|
|
70
|
+
|
|
71
|
+
Reads the `[async_subagents]` section where each sub-table defines a remote
|
|
72
|
+
LangGraph deployment:
|
|
73
|
+
|
|
74
|
+
```toml
|
|
75
|
+
[async_subagents.researcher]
|
|
76
|
+
description = "Research agent"
|
|
77
|
+
url = "https://my-deployment.langsmith.dev"
|
|
78
|
+
graph_id = "agent"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
config_path: Path to config file.
|
|
83
|
+
|
|
84
|
+
Defaults to `~/.deepagents/config.toml`.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
List of `AsyncSubAgent` specs (empty if section is absent or invalid).
|
|
88
|
+
"""
|
|
89
|
+
if config_path is None:
|
|
90
|
+
config_path = Path.home() / ".deepagents" / "config.toml"
|
|
91
|
+
|
|
92
|
+
if not config_path.exists():
|
|
93
|
+
return []
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
with config_path.open("rb") as f:
|
|
97
|
+
data = tomllib.load(f)
|
|
98
|
+
except (tomllib.TOMLDecodeError, PermissionError, OSError) as e:
|
|
99
|
+
logger.warning("Could not read async subagents from %s: %s", config_path, e)
|
|
100
|
+
console.print(
|
|
101
|
+
f"[bold yellow]Warning:[/bold yellow] Could not read async subagents "
|
|
102
|
+
f"from {config_path}: {e}",
|
|
103
|
+
)
|
|
104
|
+
return []
|
|
105
|
+
|
|
106
|
+
section = data.get("async_subagents")
|
|
107
|
+
if not isinstance(section, dict):
|
|
108
|
+
return []
|
|
109
|
+
|
|
110
|
+
required = {"description", "graph_id"}
|
|
111
|
+
agents: list[AsyncSubAgent] = []
|
|
112
|
+
for name, spec in section.items():
|
|
113
|
+
if not isinstance(spec, dict):
|
|
114
|
+
logger.warning("Skipping async subagent '%s': expected a table", name)
|
|
115
|
+
continue
|
|
116
|
+
missing = required - spec.keys()
|
|
117
|
+
if missing:
|
|
118
|
+
logger.warning(
|
|
119
|
+
"Skipping async subagent '%s': missing fields %s", name, missing
|
|
120
|
+
)
|
|
121
|
+
continue
|
|
122
|
+
agent: AsyncSubAgent = {
|
|
123
|
+
"name": name,
|
|
124
|
+
"description": spec["description"],
|
|
125
|
+
"graph_id": spec["graph_id"],
|
|
126
|
+
}
|
|
127
|
+
if "url" in spec and isinstance(spec["url"], str):
|
|
128
|
+
agent["url"] = spec["url"]
|
|
129
|
+
if "headers" in spec and isinstance(spec["headers"], dict):
|
|
130
|
+
agent["headers"] = spec["headers"]
|
|
131
|
+
agents.append(agent)
|
|
132
|
+
|
|
133
|
+
return agents
|
|
134
|
+
|
|
135
|
+
|
|
66
136
|
def list_agents(*, output_format: OutputFormat = "text") -> None:
|
|
67
137
|
"""List all available agents.
|
|
68
138
|
|
|
@@ -537,6 +607,11 @@ def _add_interrupt_on() -> dict[str, InterruptOnConfig]:
|
|
|
537
607
|
"description": _format_task_description, # type: ignore[typeddict-item] # Callable description narrower than TypedDict expects
|
|
538
608
|
}
|
|
539
609
|
|
|
610
|
+
async_subagent_interrupt_config: InterruptOnConfig = {
|
|
611
|
+
"allowed_decisions": ["approve", "reject"],
|
|
612
|
+
"description": "Launch, update, or cancel a remote async subagent.",
|
|
613
|
+
}
|
|
614
|
+
|
|
540
615
|
interrupt_map: dict[str, InterruptOnConfig] = {
|
|
541
616
|
"execute": execute_interrupt_config,
|
|
542
617
|
"write_file": write_file_interrupt_config,
|
|
@@ -544,6 +619,9 @@ def _add_interrupt_on() -> dict[str, InterruptOnConfig]:
|
|
|
544
619
|
"web_search": web_search_interrupt_config,
|
|
545
620
|
"fetch_url": fetch_url_interrupt_config,
|
|
546
621
|
"task": task_interrupt_config,
|
|
622
|
+
"launch_async_subagent": async_subagent_interrupt_config,
|
|
623
|
+
"update_async_subagent": async_subagent_interrupt_config,
|
|
624
|
+
"cancel_async_subagent": async_subagent_interrupt_config,
|
|
547
625
|
}
|
|
548
626
|
|
|
549
627
|
if REQUIRE_COMPACT_TOOL_APPROVAL:
|
|
@@ -577,6 +655,7 @@ def create_cli_agent(
|
|
|
577
655
|
mcp_server_info: list[MCPServerInfo] | None = None,
|
|
578
656
|
cwd: str | Path | None = None,
|
|
579
657
|
project_context: ProjectContext | None = None,
|
|
658
|
+
async_subagents: list[AsyncSubAgent] | None = None,
|
|
580
659
|
) -> tuple[Pregel, CompositeBackend]:
|
|
581
660
|
"""Create a CLI-configured agent with flexible options.
|
|
582
661
|
|
|
@@ -619,6 +698,9 @@ def create_cli_agent(
|
|
|
619
698
|
project_context: Explicit project path context for project-sensitive
|
|
620
699
|
behavior such as project `AGENTS.md` files, skills, subagents, and
|
|
621
700
|
MCP trust.
|
|
701
|
+
async_subagents: Remote LangGraph deployments to expose as async subagent tools.
|
|
702
|
+
|
|
703
|
+
Loaded from `[async_subagents]` in `config.toml` or passed directly.
|
|
622
704
|
|
|
623
705
|
Returns:
|
|
624
706
|
2-tuple of `(agent_graph, backend)`
|
|
@@ -817,14 +899,23 @@ def create_cli_agent(
|
|
|
817
899
|
)
|
|
818
900
|
|
|
819
901
|
# Create the agent
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
902
|
+
#
|
|
903
|
+
# TODO: revert to direct keyword arguments once the CLI pins SDK >=0.5.0.
|
|
904
|
+
# We use **kwargs here because `async_subagents` was added in SDK 0.5.0 but
|
|
905
|
+
# the CLI still pins 0.4.x. Passing an unknown kwarg — even as None — raises
|
|
906
|
+
# TypeError, so we must omit it from the dict entirely when unused.
|
|
907
|
+
agent_kwargs: dict[str, Any] = {
|
|
908
|
+
"model": model,
|
|
909
|
+
"system_prompt": system_prompt,
|
|
910
|
+
"tools": tools,
|
|
911
|
+
"backend": composite_backend,
|
|
912
|
+
"middleware": agent_middleware,
|
|
913
|
+
"interrupt_on": interrupt_on,
|
|
914
|
+
"checkpointer": checkpointer,
|
|
915
|
+
"subagents": custom_subagents or None,
|
|
916
|
+
}
|
|
917
|
+
if async_subagents:
|
|
918
|
+
agent_kwargs["async_subagents"] = async_subagents
|
|
919
|
+
|
|
920
|
+
agent = create_deep_agent(**agent_kwargs).with_config(config)
|
|
830
921
|
return agent, composite_backend
|
|
@@ -26,6 +26,7 @@ from textual.css.query import NoMatches
|
|
|
26
26
|
from textual.message import Message
|
|
27
27
|
from textual.screen import ModalScreen
|
|
28
28
|
from textual.style import Style as TStyle
|
|
29
|
+
from textual.widgets import Static
|
|
29
30
|
|
|
30
31
|
from deepagents_cli.clipboard import copy_selection_to_clipboard
|
|
31
32
|
from deepagents_cli.config import (
|
|
@@ -90,7 +91,6 @@ if TYPE_CHECKING:
|
|
|
90
91
|
from textual.events import Click, MouseUp, Paste
|
|
91
92
|
from textual.scrollbar import ScrollUp
|
|
92
93
|
from textual.widget import Widget
|
|
93
|
-
from textual.widgets import Static
|
|
94
94
|
from textual.worker import Worker
|
|
95
95
|
|
|
96
96
|
from deepagents_cli.ask_user import AskUserWidgetResult, Question
|
|
@@ -255,6 +255,18 @@ def _extract_model_params_flag(raw_arg: str) -> tuple[str, dict[str, Any] | None
|
|
|
255
255
|
|
|
256
256
|
InputMode = Literal["normal", "shell", "command"]
|
|
257
257
|
|
|
258
|
+
_TYPING_IDLE_THRESHOLD_SECONDS: float = 2.0
|
|
259
|
+
"""Seconds since the last keystroke after which the user is considered idle and
|
|
260
|
+
a pending approval widget can be shown.
|
|
261
|
+
|
|
262
|
+
Two seconds balances responsiveness with avoiding accidental approval
|
|
263
|
+
key presses.
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
_DEFERRED_APPROVAL_TIMEOUT_SECONDS: float = 30.0
|
|
267
|
+
"""Maximum seconds the deferred-approval worker will wait for the user to stop
|
|
268
|
+
typing before showing the approval widget regardless."""
|
|
269
|
+
|
|
258
270
|
|
|
259
271
|
@dataclass(frozen=True, slots=True)
|
|
260
272
|
class QueuedMessage:
|
|
@@ -503,6 +515,13 @@ class DeepAgentsApp(App):
|
|
|
503
515
|
show=False,
|
|
504
516
|
priority=True,
|
|
505
517
|
),
|
|
518
|
+
Binding(
|
|
519
|
+
"ctrl+x",
|
|
520
|
+
"open_editor",
|
|
521
|
+
"Open Editor",
|
|
522
|
+
show=False,
|
|
523
|
+
priority=True,
|
|
524
|
+
),
|
|
506
525
|
# Approval menu keys (handled at App level for reliability)
|
|
507
526
|
Binding("up", "approval_up", "Up", show=False),
|
|
508
527
|
Binding("k", "approval_up", "Up", show=False),
|
|
@@ -621,6 +640,9 @@ class DeepAgentsApp(App):
|
|
|
621
640
|
self._shell_running = False
|
|
622
641
|
self._loading_widget: LoadingWidget | None = None
|
|
623
642
|
self._token_tracker: TextualTokenTracker | None = None
|
|
643
|
+
# Typing-aware approval deferral state
|
|
644
|
+
self._last_typed_at: float | None = None
|
|
645
|
+
self._approval_placeholder: Static | None = None
|
|
624
646
|
# Cumulative usage stats across all turns in this session
|
|
625
647
|
self._session_stats: SessionStats = SessionStats()
|
|
626
648
|
# User message queue for sequential processing
|
|
@@ -1220,27 +1242,114 @@ class DeepAgentsApp(App):
|
|
|
1220
1242
|
menu = ApprovalMenu(action_requests, assistant_id, id=unique_id)
|
|
1221
1243
|
menu.set_future(result_future)
|
|
1222
1244
|
|
|
1223
|
-
# Store reference
|
|
1224
1245
|
self._pending_approval_widget = menu
|
|
1225
1246
|
|
|
1226
|
-
|
|
1247
|
+
if self._is_user_typing():
|
|
1248
|
+
# Show a placeholder until the user stops typing, then swap in the
|
|
1249
|
+
# real ApprovalMenu. This prevents accidental key presses (e.g.
|
|
1250
|
+
# 'y', 'n') from triggering approval decisions mid-sentence.
|
|
1251
|
+
placeholder = Static(
|
|
1252
|
+
"Waiting for typing to finish...",
|
|
1253
|
+
classes="approval-placeholder",
|
|
1254
|
+
)
|
|
1255
|
+
self._approval_placeholder = placeholder
|
|
1256
|
+
try:
|
|
1257
|
+
messages = self.query_one("#messages", Container)
|
|
1258
|
+
await self._mount_before_queued(messages, placeholder)
|
|
1259
|
+
self.call_after_refresh(placeholder.scroll_visible)
|
|
1260
|
+
except Exception:
|
|
1261
|
+
logger.exception("Failed to mount approval placeholder")
|
|
1262
|
+
# Placeholder failed — fall back to showing the menu directly
|
|
1263
|
+
# so the future is always resolvable.
|
|
1264
|
+
self._approval_placeholder = None
|
|
1265
|
+
await self._mount_approval_widget(menu, result_future)
|
|
1266
|
+
return result_future
|
|
1267
|
+
|
|
1268
|
+
self.run_worker(
|
|
1269
|
+
self._deferred_show_approval(placeholder, menu, result_future),
|
|
1270
|
+
exclusive=False,
|
|
1271
|
+
)
|
|
1272
|
+
else:
|
|
1273
|
+
await self._mount_approval_widget(menu, result_future)
|
|
1274
|
+
|
|
1275
|
+
return result_future
|
|
1276
|
+
|
|
1277
|
+
async def _mount_approval_widget(
|
|
1278
|
+
self,
|
|
1279
|
+
menu: ApprovalMenu,
|
|
1280
|
+
result_future: asyncio.Future[dict[str, str]],
|
|
1281
|
+
) -> None:
|
|
1282
|
+
"""Mount the approval menu widget inline in the messages area.
|
|
1283
|
+
|
|
1284
|
+
If mounting fails, clears `_pending_approval_widget` and propagates
|
|
1285
|
+
the exception via `result_future`.
|
|
1286
|
+
|
|
1287
|
+
Args:
|
|
1288
|
+
menu: The `ApprovalMenu` instance to mount.
|
|
1289
|
+
result_future: The future to resolve/reject for the caller.
|
|
1290
|
+
"""
|
|
1227
1291
|
try:
|
|
1228
1292
|
messages = self.query_one("#messages", Container)
|
|
1229
1293
|
await self._mount_before_queued(messages, menu)
|
|
1230
|
-
# Scroll to make approval visible (but don't re-anchor)
|
|
1231
1294
|
self.call_after_refresh(menu.scroll_visible)
|
|
1232
|
-
# Focus approval menu
|
|
1233
1295
|
self.call_after_refresh(menu.focus)
|
|
1234
1296
|
except Exception as e:
|
|
1235
1297
|
logger.exception(
|
|
1236
1298
|
"Failed to mount approval menu (id=%s) in messages container",
|
|
1237
|
-
|
|
1299
|
+
menu.id,
|
|
1238
1300
|
)
|
|
1239
1301
|
self._pending_approval_widget = None
|
|
1240
1302
|
if not result_future.done():
|
|
1241
1303
|
result_future.set_exception(e)
|
|
1242
1304
|
|
|
1243
|
-
|
|
1305
|
+
async def _deferred_show_approval(
|
|
1306
|
+
self,
|
|
1307
|
+
placeholder: Static,
|
|
1308
|
+
menu: ApprovalMenu,
|
|
1309
|
+
result_future: asyncio.Future[dict[str, str]],
|
|
1310
|
+
) -> None:
|
|
1311
|
+
"""Wait until the user is idle, then swap the placeholder for the real menu.
|
|
1312
|
+
|
|
1313
|
+
Exits early if the placeholder has already been detached (e.g. the
|
|
1314
|
+
approval was cancelled while waiting). In that case the future is
|
|
1315
|
+
cancelled so the caller is not left hanging.
|
|
1316
|
+
|
|
1317
|
+
Args:
|
|
1318
|
+
placeholder: The temporary placeholder widget currently mounted.
|
|
1319
|
+
menu: The `ApprovalMenu` to show once the user stops typing.
|
|
1320
|
+
result_future: The future backing this approval flow.
|
|
1321
|
+
"""
|
|
1322
|
+
deadline = _monotonic() + _DEFERRED_APPROVAL_TIMEOUT_SECONDS
|
|
1323
|
+
while self._is_user_typing(): # Simple polling
|
|
1324
|
+
if _monotonic() > deadline:
|
|
1325
|
+
logger.warning(
|
|
1326
|
+
"Timed out waiting for user to stop typing; showing approval now"
|
|
1327
|
+
)
|
|
1328
|
+
break
|
|
1329
|
+
await asyncio.sleep(0.2)
|
|
1330
|
+
|
|
1331
|
+
# Guard: if the placeholder was already removed (e.g. agent cancelled
|
|
1332
|
+
# the approval while we were waiting), clean up and cancel the future.
|
|
1333
|
+
if not placeholder.is_attached:
|
|
1334
|
+
logger.warning(
|
|
1335
|
+
"Approval placeholder detached before menu shown (id=%s)",
|
|
1336
|
+
menu.id,
|
|
1337
|
+
)
|
|
1338
|
+
self._approval_placeholder = None
|
|
1339
|
+
self._pending_approval_widget = None
|
|
1340
|
+
if not result_future.done():
|
|
1341
|
+
result_future.cancel()
|
|
1342
|
+
return
|
|
1343
|
+
|
|
1344
|
+
self._approval_placeholder = None
|
|
1345
|
+
try:
|
|
1346
|
+
await placeholder.remove()
|
|
1347
|
+
except Exception:
|
|
1348
|
+
logger.warning(
|
|
1349
|
+
"Failed to remove approval placeholder during swap",
|
|
1350
|
+
exc_info=True,
|
|
1351
|
+
)
|
|
1352
|
+
await self._mount_approval_widget(menu, result_future)
|
|
1244
1353
|
|
|
1245
1354
|
def _on_auto_approve_enabled(self) -> None:
|
|
1246
1355
|
"""Handle auto-approve being enabled via the HITL approval menu.
|
|
@@ -1414,11 +1523,42 @@ class DeepAgentsApp(App):
|
|
|
1414
1523
|
if self._status_bar:
|
|
1415
1524
|
self._status_bar.set_mode(event.mode)
|
|
1416
1525
|
|
|
1526
|
+
def on_chat_input_typing(
|
|
1527
|
+
self,
|
|
1528
|
+
event: ChatInput.Typing, # noqa: ARG002 # Textual event handler signature
|
|
1529
|
+
) -> None:
|
|
1530
|
+
"""Record the most recent keystroke time for typing-aware approval deferral."""
|
|
1531
|
+
self._last_typed_at = _monotonic()
|
|
1532
|
+
|
|
1533
|
+
def _is_user_typing(self) -> bool:
|
|
1534
|
+
"""Return whether the user typed recently (within the idle threshold).
|
|
1535
|
+
|
|
1536
|
+
Returns:
|
|
1537
|
+
`True` if the last recorded typing event occurred within the last
|
|
1538
|
+
`_TYPING_IDLE_THRESHOLD_SECONDS` seconds, `False` otherwise.
|
|
1539
|
+
"""
|
|
1540
|
+
if self._last_typed_at is None:
|
|
1541
|
+
return False
|
|
1542
|
+
return (_monotonic() - self._last_typed_at) < _TYPING_IDLE_THRESHOLD_SECONDS
|
|
1543
|
+
|
|
1417
1544
|
async def on_approval_menu_decided(
|
|
1418
1545
|
self,
|
|
1419
1546
|
event: Any, # noqa: ARG002, ANN401 # Textual event handler signature
|
|
1420
1547
|
) -> None:
|
|
1421
1548
|
"""Handle approval menu decision - remove from messages and refocus input."""
|
|
1549
|
+
# Defensively remove any lingering placeholder (should already be gone
|
|
1550
|
+
# once the deferred worker swaps it, but guard against edge cases).
|
|
1551
|
+
if self._approval_placeholder is not None:
|
|
1552
|
+
if self._approval_placeholder.is_attached:
|
|
1553
|
+
try:
|
|
1554
|
+
await self._approval_placeholder.remove()
|
|
1555
|
+
except Exception:
|
|
1556
|
+
logger.warning(
|
|
1557
|
+
"Failed to remove approval placeholder during cleanup",
|
|
1558
|
+
exc_info=True,
|
|
1559
|
+
)
|
|
1560
|
+
self._approval_placeholder = None
|
|
1561
|
+
|
|
1422
1562
|
# Remove ApprovalMenu using stored reference
|
|
1423
1563
|
if self._pending_approval_widget:
|
|
1424
1564
|
await self._pending_approval_widget.remove()
|
|
@@ -1659,12 +1799,13 @@ class DeepAgentsApp(App):
|
|
|
1659
1799
|
elif cmd == "/help":
|
|
1660
1800
|
await self._mount_message(UserMessage(command))
|
|
1661
1801
|
help_body = (
|
|
1662
|
-
"Commands: /quit, /clear, /offload, /mcp, "
|
|
1802
|
+
"Commands: /quit, /clear, /offload, /editor, /mcp, "
|
|
1663
1803
|
"/model [--model-params JSON] [--default], /reload, /remember, "
|
|
1664
1804
|
"/tokens, /threads, /trace, /changelog, /docs, /feedback, /help\n\n"
|
|
1665
1805
|
"Interactive Features:\n"
|
|
1666
1806
|
" Enter Submit your message\n"
|
|
1667
1807
|
f" {newline_shortcut():<15} Insert newline\n"
|
|
1808
|
+
" Ctrl+X Open prompt in external editor\n"
|
|
1668
1809
|
" Shift+Tab Toggle auto-approve mode\n"
|
|
1669
1810
|
" @filename Auto-complete files and inject content\n"
|
|
1670
1811
|
" /command Slash commands (/help, /clear, /quit)\n"
|
|
@@ -1728,6 +1869,8 @@ class DeepAgentsApp(App):
|
|
|
1728
1869
|
await self._mount_message(
|
|
1729
1870
|
AppMessage(f"Started new thread: {new_thread_id}")
|
|
1730
1871
|
)
|
|
1872
|
+
elif cmd == "/editor":
|
|
1873
|
+
await self.action_open_editor()
|
|
1731
1874
|
elif cmd in {"/offload", "/compact"}:
|
|
1732
1875
|
await self._mount_message(UserMessage(command))
|
|
1733
1876
|
await self._handle_offload()
|
|
@@ -2950,6 +3093,36 @@ class DeepAgentsApp(App):
|
|
|
2950
3093
|
if self._pending_approval_widget:
|
|
2951
3094
|
self._pending_approval_widget.action_select_reject()
|
|
2952
3095
|
|
|
3096
|
+
async def action_open_editor(self) -> None:
|
|
3097
|
+
"""Open the current prompt text in an external editor ($VISUAL/$EDITOR)."""
|
|
3098
|
+
from deepagents_cli.editor import open_in_editor
|
|
3099
|
+
|
|
3100
|
+
chat_input = self._chat_input
|
|
3101
|
+
if not chat_input or not chat_input._text_area:
|
|
3102
|
+
return
|
|
3103
|
+
|
|
3104
|
+
current_text = chat_input._text_area.text or ""
|
|
3105
|
+
|
|
3106
|
+
edited: str | None = None
|
|
3107
|
+
try:
|
|
3108
|
+
with self.suspend():
|
|
3109
|
+
edited = open_in_editor(current_text)
|
|
3110
|
+
except Exception:
|
|
3111
|
+
logger.warning("External editor failed", exc_info=True)
|
|
3112
|
+
self.notify(
|
|
3113
|
+
"External editor failed. Check $VISUAL/$EDITOR.",
|
|
3114
|
+
severity="error",
|
|
3115
|
+
timeout=5,
|
|
3116
|
+
)
|
|
3117
|
+
chat_input.focus_input()
|
|
3118
|
+
return
|
|
3119
|
+
|
|
3120
|
+
if edited is not None:
|
|
3121
|
+
chat_input._text_area.text = edited
|
|
3122
|
+
lines = edited.split("\n")
|
|
3123
|
+
chat_input._text_area.move_cursor((len(lines) - 1, len(lines[-1])))
|
|
3124
|
+
chat_input.focus_input()
|
|
3125
|
+
|
|
2953
3126
|
def on_paste(self, event: Paste) -> None:
|
|
2954
3127
|
"""Route unfocused paste events to chat input for drag/drop reliability."""
|
|
2955
3128
|
if not self._chat_input:
|
|
@@ -51,6 +51,16 @@ Screen {
|
|
|
51
51
|
border: solid $warning;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/* Placeholder shown while the user is actively typing (approval deferred) */
|
|
55
|
+
.approval-placeholder {
|
|
56
|
+
height: auto;
|
|
57
|
+
margin: 1 0;
|
|
58
|
+
padding: 0 1;
|
|
59
|
+
border: solid $panel;
|
|
60
|
+
color: $text-muted;
|
|
61
|
+
text-style: italic;
|
|
62
|
+
}
|
|
63
|
+
|
|
54
64
|
.approval-menu .approval-title {
|
|
55
65
|
text-style: bold;
|
|
56
66
|
color: $warning;
|
|
@@ -7,15 +7,6 @@ compatibility: designed for deepagents-cli
|
|
|
7
7
|
|
|
8
8
|
# Skill Creator
|
|
9
9
|
|
|
10
|
-
This skill provides guidance for creating effective skills.
|
|
11
|
-
|
|
12
|
-
## About Skills
|
|
13
|
-
|
|
14
|
-
Skills are modular, self-contained packages that extend agent capabilities by providing
|
|
15
|
-
specialized knowledge, workflows, and tools. Think of them as "onboarding guides" for specific
|
|
16
|
-
domains or tasks—they transform a general-purpose agent into a specialized agent
|
|
17
|
-
equipped with procedural knowledge and domain expertise.
|
|
18
|
-
|
|
19
10
|
### Skill Location for Deepagents
|
|
20
11
|
|
|
21
12
|
The deepagents CLI loads skills from five sources, listed here from lowest to highest precedence:
|
|
@@ -44,13 +35,6 @@ Example directory layout:
|
|
|
44
35
|
└── ...
|
|
45
36
|
```
|
|
46
37
|
|
|
47
|
-
### What Skills Provide
|
|
48
|
-
|
|
49
|
-
1. Specialized workflows - Multi-step procedures for specific domains
|
|
50
|
-
2. Tool integrations - Instructions for working with specific file formats or APIs
|
|
51
|
-
3. Domain expertise - Company-specific knowledge, schemas, business logic
|
|
52
|
-
4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks
|
|
53
|
-
|
|
54
38
|
## Core Principles
|
|
55
39
|
|
|
56
40
|
### Concise is Key
|
|
@@ -10,7 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
import logging
|
|
11
11
|
from typing import TYPE_CHECKING, Any
|
|
12
12
|
|
|
13
|
-
from deepagents._models import model_matches_spec
|
|
13
|
+
from deepagents._models import model_matches_spec # noqa: PLC2701
|
|
14
14
|
from langchain.agents.middleware.types import (
|
|
15
15
|
AgentMiddleware,
|
|
16
16
|
ModelRequest,
|
|
@@ -41,19 +41,22 @@ class CLIContext(TypedDict, total=False):
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def _is_anthropic_model(model: object) -> bool:
|
|
44
|
-
"""Check whether a resolved model
|
|
44
|
+
"""Check whether a resolved model reports `'anthropic'` as its provider.
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
Uses `_get_ls_params` from `BaseChatModel` to read the provider name.
|
|
47
47
|
|
|
48
48
|
Returns:
|
|
49
|
-
`True` if the model is
|
|
49
|
+
`True` if the model's `ls_provider` is `'anthropic'`.
|
|
50
50
|
"""
|
|
51
51
|
try:
|
|
52
|
-
|
|
53
|
-
except
|
|
54
|
-
logger.debug(
|
|
52
|
+
ls_params = model._get_ls_params() # type: ignore[attr-defined]
|
|
53
|
+
except (AttributeError, TypeError, RuntimeError):
|
|
54
|
+
logger.debug(
|
|
55
|
+
"_get_ls_params raised for %s; assuming non-Anthropic",
|
|
56
|
+
type(model).__name__,
|
|
57
|
+
)
|
|
55
58
|
return False
|
|
56
|
-
return isinstance(
|
|
59
|
+
return isinstance(ls_params, dict) and ls_params.get("ls_provider") == "anthropic"
|
|
57
60
|
|
|
58
61
|
|
|
59
62
|
_ANTHROPIC_ONLY_SETTINGS: set[str] = {"cache_control"}
|
|
@@ -83,8 +86,19 @@ def _apply_overrides(request: ModelRequest) -> ModelRequest:
|
|
|
83
86
|
new_model = None
|
|
84
87
|
model = ctx.get("model")
|
|
85
88
|
if model and not model_matches_spec(request.model, model):
|
|
89
|
+
from deepagents_cli.config import create_model
|
|
90
|
+
from deepagents_cli.model_config import ModelConfigError
|
|
91
|
+
|
|
86
92
|
logger.debug("Overriding model to %s", model)
|
|
87
|
-
|
|
93
|
+
try:
|
|
94
|
+
new_model = create_model(model).model
|
|
95
|
+
except ModelConfigError:
|
|
96
|
+
logger.exception(
|
|
97
|
+
"Failed to resolve runtime model override '%s'; "
|
|
98
|
+
"continuing with current model",
|
|
99
|
+
model,
|
|
100
|
+
)
|
|
101
|
+
return request
|
|
88
102
|
overrides["model"] = new_model
|
|
89
103
|
|
|
90
104
|
# Param merge
|