deepagents-cli 0.0.37__tar.gz → 0.0.39__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.37 → deepagents_cli-0.0.39}/CHANGELOG.md +30 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/PKG-INFO +2 -2
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/_version.py +1 -1
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/app.py +63 -11
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/command_registry.py +50 -11
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/deploy/bundler.py +160 -31
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/deploy/commands.py +4 -1
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/deploy/config.py +142 -1
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/deploy/templates.py +251 -41
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/main.py +60 -12
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/model_config.py +10 -1
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/ui.py +3 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/update_check.py +76 -17
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/autocomplete.py +16 -9
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/chat_input.py +207 -5
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/message_store.py +34 -10
- deepagents_cli-0.0.39/examples/deploy-content-writer/.env.example +2 -0
- deepagents_cli-0.0.39/examples/deploy-content-writer/skills/blog-post/SKILL.md +15 -0
- deepagents_cli-0.0.39/examples/deploy-content-writer/skills/social-media/SKILL.md +15 -0
- deepagents_cli-0.0.39/examples/deploy-content-writer/user/context.md +15 -0
- deepagents_cli-0.0.39/examples/deploy-content-writer/user/preferences.md +11 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/pyproject.toml +2 -2
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/integration_tests/test_compact_resume.py +6 -11
- deepagents_cli-0.0.39/tests/unit_tests/deploy/test_bundler.py +541 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/deploy/test_config.py +148 -1
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_app.py +166 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_args.py +16 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_autocomplete.py +36 -8
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_chat_input.py +230 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_command_registry.py +14 -9
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_message_store.py +71 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_offload.py +2 -2
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_reload.py +1 -1
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_skill_invocation.py +12 -11
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_update_check.py +228 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/uv.lock +100 -100
- deepagents_cli-0.0.37/tests/unit_tests/deploy/test_bundler.py +0 -245
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/.gitignore +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/DEV.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/Makefile +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/README.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/THREAT_MODEL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/__main__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/_ask_user_types.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/_cli_context.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/_debug.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/_env_vars.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/_server_config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/_session_stats.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/_testing_models.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/agent.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/app.tcss +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/ask_user.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/built_in_skills/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/built_in_skills/remember/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/built_in_skills/skill-creator/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/clipboard.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/configurable_model.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/default_agent_prompt.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/deploy/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/editor.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/file_ops.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/formatting.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/hooks.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/input.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/integrations/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/integrations/sandbox_factory.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/integrations/sandbox_provider.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/local_context.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/mcp_tools.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/mcp_trust.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/media_utils.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/non_interactive.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/offload.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/output.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/project_utils.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/py.typed +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/remote_client.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/server.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/server_graph.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/server_manager.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/sessions.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/skills/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/skills/commands.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/skills/invocation.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/skills/load.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/subagents.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/system_prompt.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/textual_adapter.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/theme.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/token_state.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/tool_display.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/tools.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/unicode_security.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/_links.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/approval.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/ask_user.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/diff.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/history.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/loading.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/mcp_viewer.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/messages.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/model_selector.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/notification_settings.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/status.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/theme_selector.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/thread_selector.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/tool_renderers.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/tool_widgets.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/deepagents_cli/widgets/welcome.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/examples/skills/arxiv-search/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/examples/skills/arxiv-search/arxiv_search.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/examples/skills/langgraph-docs/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/examples/skills/skill-creator/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/examples/skills/web-research/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/images/cli.png +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/scripts/check_imports.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/scripts/debug_server.sh +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/scripts/install.sh +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/README.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/integration_tests/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/integration_tests/benchmarks/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/integration_tests/conftest.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/integration_tests/test_acp_mode.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/integration_tests/test_sandbox_factory.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/integration_tests/test_sandbox_operations.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/conftest.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/deploy/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/deploy/test_commands.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/skills/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/skills/test_commands.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/skills/test_load.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/skills/test_skills_json.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_agent.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_agent_friendly.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_approval.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_ask_user.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_ask_user_middleware.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_charset.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_compact_tool.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_configurable_model.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_debug.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_editor.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_end_to_end.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_env_vars.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_exception_handling.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_file_ops.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_history.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_hooks.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_imports.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_input_parsing.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_links.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_loading.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_local_context.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_main.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_main_acp_mode.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_main_args.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_mcp_tools.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_mcp_trust.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_mcp_viewer.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_media_utils.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_messages.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_model_config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_model_selector.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_model_switch.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_non_interactive.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_output.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_remote_client.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_sandbox_factory.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_server.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_server_config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_server_graph.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_server_helpers.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_server_manager.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_sessions.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_shell_allow_list.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_status.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_subagents.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_textual_adapter.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_theme.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_thread_selector.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_token_tracker.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_ui.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_unicode_security.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_version.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/test_welcome.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/tools/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.39}/tests/unit_tests/tools/test_fetch_url.py +0 -0
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.39](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.38...deepagents-cli==0.0.39) (2026-04-17)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **cli:** inline argument hints for slash commands ([#2181](https://github.com/langchain-ai/deepagents/issues/2181)) ([6b58e06](https://github.com/langchain-ai/deepagents/commit/6b58e06b06f6fb360d85c54eac31953d1e47dd7a))
|
|
9
|
+
* **cli:** subagents for `deepagents deploy` ([#2786](https://github.com/langchain-ai/deepagents/issues/2786)) ([7dd5565](https://github.com/langchain-ai/deepagents/commit/7dd5565e30ab91a2cd2ca10fcd227e590451f13c))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **cli:** throttle update notification to once per day and fix teardown banner ([#2764](https://github.com/langchain-ai/deepagents/issues/2764)) ([ba31294](https://github.com/langchain-ai/deepagents/commit/ba31294b733d4ef542d18830eec6df30cfa0d23f))
|
|
15
|
+
|
|
16
|
+
## [0.0.38](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.37...deepagents-cli==0.0.38) (2026-04-15)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* **cli:** user scoped memory ([#2708](https://github.com/langchain-ai/deepagents/issues/2708)) ([23bfca6](https://github.com/langchain-ai/deepagents/commit/23bfca6e46e6f3e4fba6657d858ddd5a0b06626f))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* **deepagents:** remove old integration tests ([#2728](https://github.com/langchain-ai/deepagents/issues/2728)) ([6653197](https://github.com/langchain-ai/deepagents/commit/6653197b6cbec6dd1ca23d9f90bc1439ca26e6e5))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Performance Improvements
|
|
30
|
+
|
|
31
|
+
* **cli:** `O(1)` message lookups in `MessageStore` ([#2350](https://github.com/langchain-ai/deepagents/issues/2350)) ([d39fd5d](https://github.com/langchain-ai/deepagents/commit/d39fd5d3651fd87d1eea8c02cbef2c2f62449e67))
|
|
32
|
+
|
|
3
33
|
## [0.0.37](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.36...deepagents-cli==0.0.37) (2026-04-10)
|
|
4
34
|
|
|
5
35
|
### Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagents-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.39
|
|
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/
|
|
@@ -27,7 +27,7 @@ Classifier: Topic :: Terminals
|
|
|
27
27
|
Requires-Python: <4.0,>=3.11
|
|
28
28
|
Requires-Dist: aiosqlite<1.0.0,>=0.19.0
|
|
29
29
|
Requires-Dist: deepagents-acp>=0.0.4
|
|
30
|
-
Requires-Dist: deepagents==0.5.
|
|
30
|
+
Requires-Dist: deepagents==0.5.3
|
|
31
31
|
Requires-Dist: httpx<1.0.0,>=0.28.1
|
|
32
32
|
Requires-Dist: langchain-anthropic<2.0.0,>=1.4.0
|
|
33
33
|
Requires-Dist: langchain-google-genai<5.0.0,>=4.2.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Version information and lightweight constants for `deepagents-cli`."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.0.
|
|
3
|
+
__version__ = "0.0.39" # 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."""
|
|
@@ -1439,7 +1439,7 @@ class DeepAgentsApp(App):
|
|
|
1439
1439
|
)
|
|
1440
1440
|
|
|
1441
1441
|
available, latest = await asyncio.to_thread(is_update_available)
|
|
1442
|
-
if not available:
|
|
1442
|
+
if not available or latest is None:
|
|
1443
1443
|
return
|
|
1444
1444
|
|
|
1445
1445
|
self._update_available = (True, latest)
|
|
@@ -1447,7 +1447,7 @@ class DeepAgentsApp(App):
|
|
|
1447
1447
|
logger.debug("Background update check failed", exc_info=True)
|
|
1448
1448
|
return
|
|
1449
1449
|
|
|
1450
|
-
# Phase 2: auto-update or notify
|
|
1450
|
+
# Phase 2: auto-update or notify
|
|
1451
1451
|
try:
|
|
1452
1452
|
from deepagents_cli._version import __version__ as cli_version
|
|
1453
1453
|
|
|
@@ -1475,6 +1475,14 @@ class DeepAgentsApp(App):
|
|
|
1475
1475
|
markup=False,
|
|
1476
1476
|
)
|
|
1477
1477
|
else:
|
|
1478
|
+
from deepagents_cli.update_check import (
|
|
1479
|
+
mark_update_notified,
|
|
1480
|
+
should_notify_update,
|
|
1481
|
+
)
|
|
1482
|
+
|
|
1483
|
+
if not await asyncio.to_thread(should_notify_update, latest):
|
|
1484
|
+
return
|
|
1485
|
+
|
|
1478
1486
|
cmd = upgrade_command()
|
|
1479
1487
|
self.notify(
|
|
1480
1488
|
f"Update available: v{latest} (current: v{cli_version}). "
|
|
@@ -1484,13 +1492,15 @@ class DeepAgentsApp(App):
|
|
|
1484
1492
|
timeout=15,
|
|
1485
1493
|
markup=False,
|
|
1486
1494
|
)
|
|
1495
|
+
await asyncio.to_thread(mark_update_notified, latest)
|
|
1487
1496
|
except Exception:
|
|
1488
|
-
logger.warning("
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1497
|
+
logger.warning("Update check/notify failed unexpectedly", exc_info=True)
|
|
1498
|
+
if is_auto_update_enabled():
|
|
1499
|
+
self.notify(
|
|
1500
|
+
"Auto-update failed unexpectedly.",
|
|
1501
|
+
severity="warning",
|
|
1502
|
+
timeout=10,
|
|
1503
|
+
)
|
|
1494
1504
|
|
|
1495
1505
|
async def _show_whats_new(self) -> None:
|
|
1496
1506
|
"""Show a 'what's new' banner on the first launch after an upgrade."""
|
|
@@ -1505,11 +1515,15 @@ class DeepAgentsApp(App):
|
|
|
1505
1515
|
|
|
1506
1516
|
try:
|
|
1507
1517
|
from deepagents_cli._version import __version__ as cli_version
|
|
1518
|
+
from deepagents_cli.config import _is_editable_install
|
|
1519
|
+
|
|
1520
|
+
if await asyncio.to_thread(_is_editable_install):
|
|
1521
|
+
heading = f"Now running v{cli_version}"
|
|
1522
|
+
else:
|
|
1523
|
+
heading = f"Updated to v{cli_version}"
|
|
1508
1524
|
|
|
1509
1525
|
await self._mount_message(
|
|
1510
|
-
AppMessage(
|
|
1511
|
-
f"Updated to v{cli_version}\nSee what's new: {CHANGELOG_URL}"
|
|
1512
|
-
)
|
|
1526
|
+
AppMessage(f"{heading}\nSee what's new: {CHANGELOG_URL}")
|
|
1513
1527
|
)
|
|
1514
1528
|
except Exception:
|
|
1515
1529
|
logger.debug("What's new banner display failed", exc_info=True)
|
|
@@ -2759,6 +2773,15 @@ class DeepAgentsApp(App):
|
|
|
2759
2773
|
elif cmd == "/remember" or cmd.startswith("/remember "):
|
|
2760
2774
|
# Convenience alias for /skill:remember — shorter and discoverable
|
|
2761
2775
|
# before skill loading completes.
|
|
2776
|
+
if not await self._has_conversation_messages():
|
|
2777
|
+
await self._mount_message(UserMessage(command))
|
|
2778
|
+
await self._mount_message(
|
|
2779
|
+
AppMessage(
|
|
2780
|
+
"Nothing to remember yet. Start a conversation first,"
|
|
2781
|
+
" then use /remember to capture learnings."
|
|
2782
|
+
)
|
|
2783
|
+
)
|
|
2784
|
+
return
|
|
2762
2785
|
args = command.strip()[len("/remember") :].strip()
|
|
2763
2786
|
rewritten = f"/skill:remember {args}" if args else "/skill:remember"
|
|
2764
2787
|
await self._handle_skill_command(rewritten)
|
|
@@ -3039,6 +3062,35 @@ class DeepAgentsApp(App):
|
|
|
3039
3062
|
skill_name, args = parse_skill_command(command)
|
|
3040
3063
|
await self._invoke_skill(skill_name, args, command=command)
|
|
3041
3064
|
|
|
3065
|
+
async def _has_conversation_messages(self) -> bool:
|
|
3066
|
+
"""Check whether the current thread has at least one human message.
|
|
3067
|
+
|
|
3068
|
+
Returns:
|
|
3069
|
+
`True` if the conversation contains a `HumanMessage`, `False`
|
|
3070
|
+
otherwise. On transient errors (network, corrupt state) returns
|
|
3071
|
+
`True` so that `/remember` is not blocked with a misleading
|
|
3072
|
+
"nothing to remember" message.
|
|
3073
|
+
"""
|
|
3074
|
+
if not self._agent:
|
|
3075
|
+
return False
|
|
3076
|
+
try:
|
|
3077
|
+
from langchain_core.messages import HumanMessage
|
|
3078
|
+
|
|
3079
|
+
config: RunnableConfig = {
|
|
3080
|
+
"configurable": {"thread_id": self._lc_thread_id},
|
|
3081
|
+
}
|
|
3082
|
+
state = await self._agent.aget_state(config)
|
|
3083
|
+
if not state or not state.values:
|
|
3084
|
+
return False
|
|
3085
|
+
messages = state.values.get("messages", [])
|
|
3086
|
+
return any(isinstance(m, HumanMessage) for m in messages)
|
|
3087
|
+
except Exception:
|
|
3088
|
+
logger.warning(
|
|
3089
|
+
"Failed to check conversation messages; allowing /remember to proceed",
|
|
3090
|
+
exc_info=True,
|
|
3091
|
+
)
|
|
3092
|
+
return True
|
|
3093
|
+
|
|
3042
3094
|
async def _get_conversation_token_count(self) -> int | None:
|
|
3043
3095
|
"""Return the approximate conversation-only token count.
|
|
3044
3096
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Unified slash-command registry.
|
|
2
2
|
|
|
3
3
|
Every slash command is declared once as a `SlashCommand` entry in `COMMANDS`.
|
|
4
|
-
Bypass-tier frozensets and autocomplete
|
|
4
|
+
Bypass-tier frozensets and autocomplete entries are derived automatically — no
|
|
5
5
|
other file should hard-code command metadata.
|
|
6
6
|
"""
|
|
7
7
|
|
|
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
from dataclasses import dataclass
|
|
11
11
|
from enum import StrEnum
|
|
12
|
-
from typing import TYPE_CHECKING
|
|
12
|
+
from typing import TYPE_CHECKING, NamedTuple
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
15
|
from deepagents_cli.skills.load import ExtendedSkillMetadata
|
|
@@ -50,9 +50,26 @@ class SlashCommand:
|
|
|
50
50
|
hidden_keywords: str = ""
|
|
51
51
|
"""Space-separated terms for fuzzy matching (never displayed)."""
|
|
52
52
|
|
|
53
|
+
argument_hint: str = ""
|
|
54
|
+
"""Placeholder text for autocomplete when the command accepts args."""
|
|
55
|
+
|
|
53
56
|
aliases: tuple[str, ...] = ()
|
|
54
57
|
"""Alternative names (e.g. `("/q",)` for `/quit`)."""
|
|
55
58
|
|
|
59
|
+
def to_entry(self) -> CommandEntry:
|
|
60
|
+
"""Project this command into a `CommandEntry` for autocomplete.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
A `CommandEntry` carrying only the fields the autocomplete
|
|
64
|
+
layer needs.
|
|
65
|
+
"""
|
|
66
|
+
return CommandEntry(
|
|
67
|
+
name=self.name,
|
|
68
|
+
description=self.description,
|
|
69
|
+
hidden_keywords=self.hidden_keywords,
|
|
70
|
+
argument_hint=self.argument_hint,
|
|
71
|
+
)
|
|
72
|
+
|
|
56
73
|
|
|
57
74
|
COMMANDS: tuple[SlashCommand, ...] = (
|
|
58
75
|
SlashCommand(
|
|
@@ -94,11 +111,13 @@ COMMANDS: tuple[SlashCommand, ...] = (
|
|
|
94
111
|
name="/remember",
|
|
95
112
|
description="Update memory and skills from conversation",
|
|
96
113
|
bypass_tier=BypassTier.QUEUED,
|
|
114
|
+
argument_hint="[context]",
|
|
97
115
|
),
|
|
98
116
|
SlashCommand( # Static alias; not auto-generated from skill discovery
|
|
99
117
|
name="/skill-creator",
|
|
100
118
|
description="Guide for creating effective agent skills",
|
|
101
119
|
bypass_tier=BypassTier.QUEUED,
|
|
120
|
+
argument_hint="[task]",
|
|
102
121
|
),
|
|
103
122
|
SlashCommand(
|
|
104
123
|
name="/threads",
|
|
@@ -228,13 +247,28 @@ ALL_CLASSIFIED: frozenset[str] = (
|
|
|
228
247
|
|
|
229
248
|
|
|
230
249
|
# ---------------------------------------------------------------------------
|
|
231
|
-
# Autocomplete
|
|
250
|
+
# Autocomplete entries
|
|
232
251
|
# ---------------------------------------------------------------------------
|
|
233
252
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
253
|
+
|
|
254
|
+
class CommandEntry(NamedTuple):
|
|
255
|
+
"""A single autocomplete entry for the slash-command controller."""
|
|
256
|
+
|
|
257
|
+
name: str
|
|
258
|
+
"""Canonical command name (e.g. `/quit`)."""
|
|
259
|
+
|
|
260
|
+
description: str
|
|
261
|
+
"""Short user-facing description."""
|
|
262
|
+
|
|
263
|
+
hidden_keywords: str
|
|
264
|
+
"""Space-separated terms for fuzzy matching (never displayed)."""
|
|
265
|
+
|
|
266
|
+
argument_hint: str
|
|
267
|
+
"""Placeholder text shown when the command accepts arguments (e.g. `[context]`)."""
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
SLASH_COMMANDS: list[CommandEntry] = [cmd.to_entry() for cmd in COMMANDS]
|
|
271
|
+
"""Autocomplete entries derived from `COMMANDS` for `SlashCommandController`."""
|
|
238
272
|
|
|
239
273
|
|
|
240
274
|
def parse_skill_command(command: str) -> tuple[str, str]:
|
|
@@ -271,8 +305,8 @@ appear as `/skill:model`).
|
|
|
271
305
|
|
|
272
306
|
def build_skill_commands(
|
|
273
307
|
skills: list[ExtendedSkillMetadata],
|
|
274
|
-
) -> list[
|
|
275
|
-
"""Build autocomplete
|
|
308
|
+
) -> list[CommandEntry]:
|
|
309
|
+
"""Build autocomplete entries for discovered skills.
|
|
276
310
|
|
|
277
311
|
Each skill becomes a `/skill:<name>` entry with its description
|
|
278
312
|
and the skill name as a hidden keyword for fuzzy matching.
|
|
@@ -285,10 +319,15 @@ def build_skill_commands(
|
|
|
285
319
|
skills: List of discovered skill metadata.
|
|
286
320
|
|
|
287
321
|
Returns:
|
|
288
|
-
List of `
|
|
322
|
+
List of `CommandEntry` instances.
|
|
289
323
|
"""
|
|
290
324
|
return [
|
|
291
|
-
(
|
|
325
|
+
CommandEntry(
|
|
326
|
+
name=f"/skill:{skill['name']}",
|
|
327
|
+
description=skill["description"],
|
|
328
|
+
hidden_keywords=skill["name"],
|
|
329
|
+
argument_hint="",
|
|
330
|
+
)
|
|
292
331
|
for skill in skills
|
|
293
332
|
if skill["name"] not in _STATIC_SKILL_ALIASES
|
|
294
333
|
]
|
|
@@ -9,9 +9,16 @@ Reads the canonical project layout:
|
|
|
9
9
|
.env # optional — environment variables
|
|
10
10
|
mcp.json # optional — HTTP/SSE MCP servers
|
|
11
11
|
skills/ # optional — auto-seeded into skills namespace
|
|
12
|
+
user/ # optional — per-user writable memory
|
|
13
|
+
AGENTS.md # optional — seeded as empty if not provided
|
|
12
14
|
```
|
|
13
15
|
|
|
14
16
|
...and writes everything `langgraph deploy` needs to a build directory.
|
|
17
|
+
|
|
18
|
+
AGENTS.md and skills are read-only at runtime. When a ``user/``
|
|
19
|
+
directory is present, a per-user ``AGENTS.md`` is seeded (from
|
|
20
|
+
``user/AGENTS.md`` if provided, otherwise empty) and is writable
|
|
21
|
+
at runtime.
|
|
15
22
|
"""
|
|
16
23
|
|
|
17
24
|
from __future__ import annotations
|
|
@@ -20,18 +27,23 @@ import json
|
|
|
20
27
|
import logging
|
|
21
28
|
import shutil
|
|
22
29
|
from pathlib import Path
|
|
30
|
+
from typing import Any
|
|
23
31
|
|
|
24
32
|
from deepagents_cli.deploy.config import (
|
|
25
33
|
AGENTS_MD_FILENAME,
|
|
26
34
|
MCP_FILENAME,
|
|
27
35
|
SKILLS_DIRNAME,
|
|
36
|
+
USER_DIRNAME,
|
|
28
37
|
DeployConfig,
|
|
38
|
+
SubAgentProject,
|
|
39
|
+
load_subagents,
|
|
29
40
|
)
|
|
30
41
|
from deepagents_cli.deploy.templates import (
|
|
31
42
|
DEPLOY_GRAPH_TEMPLATE,
|
|
32
43
|
MCP_TOOLS_TEMPLATE,
|
|
33
44
|
PYPROJECT_TEMPLATE,
|
|
34
45
|
SANDBOX_BLOCKS,
|
|
46
|
+
SYNC_SUBAGENTS_TEMPLATE,
|
|
35
47
|
)
|
|
36
48
|
|
|
37
49
|
logger = logging.getLogger(__name__)
|
|
@@ -69,15 +81,16 @@ def bundle(
|
|
|
69
81
|
system_prompt = agents_md_path.read_text(encoding="utf-8")
|
|
70
82
|
|
|
71
83
|
# 2. Build and write the seed payload: memory (AGENTS.md) + skills/.
|
|
72
|
-
seed = _build_seed(
|
|
84
|
+
seed = _build_seed(project_root, system_prompt)
|
|
73
85
|
(build_dir / "_seed.json").write_text(
|
|
74
86
|
json.dumps(seed, indent=2, ensure_ascii=False),
|
|
75
87
|
encoding="utf-8",
|
|
76
88
|
)
|
|
77
89
|
logger.info(
|
|
78
|
-
"Wrote _seed.json (memories: %d, skills: %d)",
|
|
90
|
+
"Wrote _seed.json (memories: %d, skills: %d, user_memories: %d)",
|
|
79
91
|
len(seed["memories"]),
|
|
80
92
|
len(seed["skills"]),
|
|
93
|
+
len(seed.get("user_memories", {})),
|
|
81
94
|
)
|
|
82
95
|
|
|
83
96
|
# 3. Copy mcp.json if present.
|
|
@@ -95,58 +108,110 @@ def bundle(
|
|
|
95
108
|
shutil.copy2(env_src, build_dir / ".env")
|
|
96
109
|
logger.info("Copied %s → .env", env_src)
|
|
97
110
|
|
|
98
|
-
# 4.
|
|
111
|
+
# 4. Load subagents (needed for both deploy_graph.py and pyproject.toml).
|
|
112
|
+
sync_subagents = load_subagents(project_root)
|
|
113
|
+
|
|
114
|
+
# 5. Render deploy_graph.py.
|
|
115
|
+
has_user_memories = (project_root / USER_DIRNAME).is_dir()
|
|
116
|
+
has_sync_subagents = bool(sync_subagents)
|
|
99
117
|
(build_dir / "deploy_graph.py").write_text(
|
|
100
|
-
_render_deploy_graph(
|
|
118
|
+
_render_deploy_graph(
|
|
119
|
+
config,
|
|
120
|
+
mcp_present=mcp_present,
|
|
121
|
+
has_user_memories=has_user_memories,
|
|
122
|
+
has_sync_subagents=has_sync_subagents,
|
|
123
|
+
),
|
|
101
124
|
encoding="utf-8",
|
|
102
125
|
)
|
|
103
126
|
logger.info("Generated deploy_graph.py")
|
|
104
127
|
|
|
105
|
-
#
|
|
128
|
+
# 6. Render langgraph.json.
|
|
106
129
|
(build_dir / "langgraph.json").write_text(
|
|
107
130
|
_render_langgraph_json(env_present=env_present),
|
|
108
131
|
encoding="utf-8",
|
|
109
132
|
)
|
|
110
133
|
|
|
111
|
-
#
|
|
134
|
+
# 7. Render pyproject.toml.
|
|
135
|
+
subagent_model_providers: list[str] = []
|
|
136
|
+
has_subagent_mcp = False
|
|
137
|
+
for sa in sync_subagents.values():
|
|
138
|
+
model = sa.config.agent.model
|
|
139
|
+
if ":" in model:
|
|
140
|
+
subagent_model_providers.append(model.split(":", 1)[0])
|
|
141
|
+
if (sa.root / MCP_FILENAME).is_file():
|
|
142
|
+
has_subagent_mcp = True
|
|
143
|
+
|
|
112
144
|
(build_dir / "pyproject.toml").write_text(
|
|
113
|
-
_render_pyproject(
|
|
145
|
+
_render_pyproject(
|
|
146
|
+
config,
|
|
147
|
+
mcp_present=mcp_present,
|
|
148
|
+
subagent_model_providers=subagent_model_providers,
|
|
149
|
+
has_subagent_mcp=has_subagent_mcp,
|
|
150
|
+
),
|
|
114
151
|
encoding="utf-8",
|
|
115
152
|
)
|
|
116
153
|
|
|
117
154
|
return build_dir
|
|
118
155
|
|
|
119
156
|
|
|
157
|
+
def _build_subagent_seed(subagent: SubAgentProject) -> dict:
|
|
158
|
+
"""Build the seed entry for a single sync subagent."""
|
|
159
|
+
sa_root = subagent.root
|
|
160
|
+
agent = subagent.config.agent
|
|
161
|
+
|
|
162
|
+
memories: dict[str, str] = {
|
|
163
|
+
f"/{AGENTS_MD_FILENAME}": (sa_root / AGENTS_MD_FILENAME).read_text(
|
|
164
|
+
encoding="utf-8"
|
|
165
|
+
),
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
skills: dict[str, str] = {}
|
|
169
|
+
skills_dir = sa_root / SKILLS_DIRNAME
|
|
170
|
+
if skills_dir.is_dir():
|
|
171
|
+
for f in sorted(skills_dir.rglob("*")):
|
|
172
|
+
if f.is_file() and not f.name.startswith("."):
|
|
173
|
+
rel = f.relative_to(skills_dir).as_posix()
|
|
174
|
+
skills[f"/{rel}"] = f.read_text(encoding="utf-8")
|
|
175
|
+
|
|
176
|
+
mcp_path = sa_root / MCP_FILENAME
|
|
177
|
+
mcp = None
|
|
178
|
+
if mcp_path.is_file():
|
|
179
|
+
mcp = json.loads(mcp_path.read_text(encoding="utf-8"))
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
"config": {
|
|
183
|
+
"name": agent.name,
|
|
184
|
+
"description": agent.description,
|
|
185
|
+
"model": agent.model,
|
|
186
|
+
},
|
|
187
|
+
"memories": memories,
|
|
188
|
+
"skills": skills,
|
|
189
|
+
"mcp": mcp,
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
|
|
120
193
|
def _build_seed(
|
|
121
|
-
config: DeployConfig, # noqa: ARG001
|
|
122
194
|
project_root: Path,
|
|
123
195
|
system_prompt: str,
|
|
124
|
-
) -> dict
|
|
196
|
+
) -> dict:
|
|
125
197
|
"""Build the `_seed.json` payload.
|
|
126
198
|
|
|
127
|
-
Layout
|
|
199
|
+
Layout::
|
|
128
200
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
`memories` always contains `/AGENTS.md` — the middleware loads it at
|
|
137
|
-
startup via `/memories/AGENTS.md`. Agent reads of `/memories/` and
|
|
138
|
-
`/skills/` are denied by `FilesystemPermission` rules.
|
|
201
|
+
{
|
|
202
|
+
"memories": { "/AGENTS.md": "..." },
|
|
203
|
+
"skills": { "/<skill>/SKILL.md": "...", ... },
|
|
204
|
+
"user_memories": { "/AGENTS.md": "..." }
|
|
205
|
+
}
|
|
139
206
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
207
|
+
``memories`` and ``skills`` are read-only at runtime.
|
|
208
|
+
``user_memories`` contains a single writable ``AGENTS.md`` mounted at
|
|
209
|
+
``/memories/user/``, namespaced per user_id. If the project has a
|
|
210
|
+
``user/`` directory (even if empty), an ``AGENTS.md`` is always seeded.
|
|
143
211
|
"""
|
|
144
|
-
# Keys must match what CompositeBackend passes to the mounted
|
|
145
|
-
# StoreBackend after stripping the route prefix: for a read of
|
|
146
|
-
# /memories/AGENTS.md it calls store.read("/AGENTS.md").
|
|
147
|
-
# Seed with the same leading-slash convention.
|
|
148
212
|
memories: dict[str, str] = {f"/{AGENTS_MD_FILENAME}": system_prompt}
|
|
149
213
|
skills: dict[str, str] = {}
|
|
214
|
+
user_memories: dict[str, str] = {}
|
|
150
215
|
|
|
151
216
|
skills_dir = project_root / SKILLS_DIRNAME
|
|
152
217
|
if skills_dir.is_dir():
|
|
@@ -155,13 +220,38 @@ def _build_seed(
|
|
|
155
220
|
rel = f.relative_to(skills_dir).as_posix()
|
|
156
221
|
skills[f"/{rel}"] = f.read_text(encoding="utf-8")
|
|
157
222
|
|
|
158
|
-
|
|
223
|
+
user_dir = project_root / USER_DIRNAME
|
|
224
|
+
if user_dir.is_dir():
|
|
225
|
+
user_agents_md = user_dir / AGENTS_MD_FILENAME
|
|
226
|
+
content = (
|
|
227
|
+
user_agents_md.read_text(encoding="utf-8")
|
|
228
|
+
if user_agents_md.is_file()
|
|
229
|
+
else ""
|
|
230
|
+
)
|
|
231
|
+
user_memories[f"/{AGENTS_MD_FILENAME}"] = content
|
|
232
|
+
|
|
233
|
+
seed: dict = {
|
|
234
|
+
"memories": memories,
|
|
235
|
+
"skills": skills,
|
|
236
|
+
"user_memories": user_memories,
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
# Sync subagents.
|
|
240
|
+
sync_subagents = load_subagents(project_root)
|
|
241
|
+
if sync_subagents:
|
|
242
|
+
seed["subagents"] = {
|
|
243
|
+
name: _build_subagent_seed(sa) for name, sa in sync_subagents.items()
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return seed
|
|
159
247
|
|
|
160
248
|
|
|
161
249
|
def _render_deploy_graph(
|
|
162
250
|
config: DeployConfig,
|
|
163
251
|
*,
|
|
164
252
|
mcp_present: bool,
|
|
253
|
+
has_user_memories: bool = False,
|
|
254
|
+
has_sync_subagents: bool = False,
|
|
165
255
|
) -> str:
|
|
166
256
|
"""Render the generated `deploy_graph.py`."""
|
|
167
257
|
provider = config.sandbox.provider
|
|
@@ -177,6 +267,16 @@ def _render_deploy_graph(
|
|
|
177
267
|
mcp_tools_block = ""
|
|
178
268
|
mcp_tools_load_call = "pass # no MCP servers configured"
|
|
179
269
|
|
|
270
|
+
if has_sync_subagents:
|
|
271
|
+
sync_subagents_block = SYNC_SUBAGENTS_TEMPLATE
|
|
272
|
+
sync_subagents_load_call = (
|
|
273
|
+
"all_subagents.extend("
|
|
274
|
+
"await _build_sync_subagents(seed, store, assistant_id))"
|
|
275
|
+
)
|
|
276
|
+
else:
|
|
277
|
+
sync_subagents_block = ""
|
|
278
|
+
sync_subagents_load_call = "pass # no sync subagents"
|
|
279
|
+
|
|
180
280
|
return DEPLOY_GRAPH_TEMPLATE.format(
|
|
181
281
|
model=config.agent.model,
|
|
182
282
|
sandbox_template=config.sandbox.template,
|
|
@@ -186,6 +286,9 @@ def _render_deploy_graph(
|
|
|
186
286
|
mcp_tools_block=mcp_tools_block,
|
|
187
287
|
mcp_tools_load_call=mcp_tools_load_call,
|
|
188
288
|
default_assistant_id=config.agent.name,
|
|
289
|
+
has_user_memories=has_user_memories,
|
|
290
|
+
sync_subagents_block=sync_subagents_block,
|
|
291
|
+
sync_subagents_load_call=sync_subagents_load_call,
|
|
189
292
|
)
|
|
190
293
|
|
|
191
294
|
|
|
@@ -201,7 +304,13 @@ def _render_langgraph_json(*, env_present: bool) -> str:
|
|
|
201
304
|
return json.dumps(data, indent=2) + "\n"
|
|
202
305
|
|
|
203
306
|
|
|
204
|
-
def _render_pyproject(
|
|
307
|
+
def _render_pyproject(
|
|
308
|
+
config: DeployConfig,
|
|
309
|
+
*,
|
|
310
|
+
mcp_present: bool,
|
|
311
|
+
subagent_model_providers: list[str] | None = None,
|
|
312
|
+
has_subagent_mcp: bool = False,
|
|
313
|
+
) -> str:
|
|
205
314
|
"""Render the deployment package's `pyproject.toml`.
|
|
206
315
|
|
|
207
316
|
Deps are inferred — the user never writes them. We add:
|
|
@@ -218,7 +327,13 @@ def _render_pyproject(config: DeployConfig, *, mcp_present: bool) -> str:
|
|
|
218
327
|
if provider_prefix and provider_prefix in _MODEL_PROVIDER_DEPS:
|
|
219
328
|
deps.append(_MODEL_PROVIDER_DEPS[provider_prefix])
|
|
220
329
|
|
|
221
|
-
|
|
330
|
+
# Add deps for subagent model providers.
|
|
331
|
+
for sp in subagent_model_providers or []:
|
|
332
|
+
dep = _MODEL_PROVIDER_DEPS.get(sp)
|
|
333
|
+
if dep and dep not in deps:
|
|
334
|
+
deps.append(dep)
|
|
335
|
+
|
|
336
|
+
if mcp_present or has_subagent_mcp:
|
|
222
337
|
deps.append("langchain-mcp-adapters")
|
|
223
338
|
|
|
224
339
|
_, partner_pkg = SANDBOX_BLOCKS.get(config.sandbox.provider, (None, None))
|
|
@@ -236,7 +351,7 @@ def _render_pyproject(config: DeployConfig, *, mcp_present: bool) -> str:
|
|
|
236
351
|
def print_bundle_summary(config: DeployConfig, build_dir: Path) -> None:
|
|
237
352
|
"""Print a human-readable summary of what was bundled."""
|
|
238
353
|
seed_path = build_dir / "_seed.json"
|
|
239
|
-
seed: dict[str,
|
|
354
|
+
seed: dict[str, Any] = {"memories": {}, "skills": {}}
|
|
240
355
|
if seed_path.exists():
|
|
241
356
|
try:
|
|
242
357
|
seed = json.loads(seed_path.read_text(encoding="utf-8"))
|
|
@@ -256,6 +371,12 @@ def print_bundle_summary(config: DeployConfig, build_dir: Path) -> None:
|
|
|
256
371
|
for f in memory_files:
|
|
257
372
|
print(f" {f}")
|
|
258
373
|
|
|
374
|
+
user_memory_files = sorted(seed.get("user_memories", {}).keys())
|
|
375
|
+
if user_memory_files:
|
|
376
|
+
print(f"\n User memory seed ({len(user_memory_files)} file(s)):")
|
|
377
|
+
for f in user_memory_files:
|
|
378
|
+
print(f" {f}")
|
|
379
|
+
|
|
259
380
|
skills_files = sorted(seed.get("skills", {}).keys())
|
|
260
381
|
if skills_files:
|
|
261
382
|
print(f"\n Skills seed ({len(skills_files)} file(s)):")
|
|
@@ -265,6 +386,14 @@ def print_bundle_summary(config: DeployConfig, build_dir: Path) -> None:
|
|
|
265
386
|
if (build_dir / "_mcp.json").exists():
|
|
266
387
|
print("\n MCP config: _mcp.json")
|
|
267
388
|
|
|
389
|
+
# Subagent summary.
|
|
390
|
+
sync_subagents = seed.get("subagents", {})
|
|
391
|
+
if sync_subagents:
|
|
392
|
+
print(f"\n Subagents ({len(sync_subagents)}):")
|
|
393
|
+
for name, sa_data in sync_subagents.items():
|
|
394
|
+
desc = sa_data.get("config", {}).get("description", "")
|
|
395
|
+
print(f" {name} \u2014 {desc}")
|
|
396
|
+
|
|
268
397
|
print(f"\n Sandbox: {config.sandbox.provider}")
|
|
269
398
|
print(f"\n Build directory: {build_dir}")
|
|
270
399
|
generated = sorted(f.name for f in build_dir.iterdir() if f.is_file())
|
|
@@ -6,6 +6,7 @@ Registered with the CLI via `setup_deploy_parsers` in `main.py`.
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
import argparse
|
|
9
|
+
import os
|
|
9
10
|
import subprocess
|
|
10
11
|
import tempfile
|
|
11
12
|
from pathlib import Path
|
|
@@ -402,12 +403,14 @@ def _run_langgraph_deploy(build_dir: Path, *, name: str) -> None:
|
|
|
402
403
|
|
|
403
404
|
config_path = str(build_dir / "langgraph.json")
|
|
404
405
|
cmd = ["langgraph", "deploy", "-c", config_path, "--name", name, "--verbose"]
|
|
406
|
+
env = os.environ.copy()
|
|
407
|
+
env["LANGGRAPH_CLI_ANALYTICS_SOURCE"] = "deepagents"
|
|
405
408
|
|
|
406
409
|
print("Deploying to LangSmith Deployments...")
|
|
407
410
|
print(f"Running: {' '.join(cmd)}")
|
|
408
411
|
print()
|
|
409
412
|
|
|
410
|
-
result = subprocess.run(cmd, cwd=str(build_dir), check=False)
|
|
413
|
+
result = subprocess.run(cmd, cwd=str(build_dir), check=False, env=env)
|
|
411
414
|
|
|
412
415
|
if result.returncode != 0:
|
|
413
416
|
print(f"\nDeployment failed (exit code {result.returncode}).")
|