deepagents-cli 0.0.37__tar.gz → 0.0.38__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.38}/CHANGELOG.md +17 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/PKG-INFO +2 -2
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_version.py +1 -1
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/app.py +7 -3
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/deploy/bundler.py +51 -22
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/deploy/config.py +1 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/deploy/templates.py +140 -40
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/message_store.py +34 -10
- deepagents_cli-0.0.38/examples/deploy-content-writer/.env.example +2 -0
- deepagents_cli-0.0.38/examples/deploy-content-writer/skills/blog-post/SKILL.md +15 -0
- deepagents_cli-0.0.38/examples/deploy-content-writer/skills/social-media/SKILL.md +15 -0
- deepagents_cli-0.0.38/examples/deploy-content-writer/user/context.md +15 -0
- deepagents_cli-0.0.38/examples/deploy-content-writer/user/preferences.md +11 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/pyproject.toml +2 -2
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/test_compact_resume.py +6 -11
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/deploy/test_bundler.py +106 -1
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_message_store.py +71 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/uv.lock +92 -92
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/.gitignore +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/DEV.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/Makefile +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/README.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/THREAT_MODEL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/__main__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_ask_user_types.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_cli_context.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_debug.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_env_vars.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_server_config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_session_stats.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_testing_models.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/agent.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/app.tcss +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/ask_user.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/built_in_skills/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/built_in_skills/remember/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/built_in_skills/skill-creator/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/clipboard.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/command_registry.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/configurable_model.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/default_agent_prompt.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/deploy/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/deploy/commands.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/editor.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/file_ops.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/formatting.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/hooks.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/input.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/integrations/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/integrations/sandbox_factory.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/integrations/sandbox_provider.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/local_context.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/main.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/mcp_tools.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/mcp_trust.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/media_utils.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/model_config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/non_interactive.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/offload.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/output.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/project_utils.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/py.typed +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/remote_client.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/server.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/server_graph.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/server_manager.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/sessions.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/skills/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/skills/commands.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/skills/invocation.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/skills/load.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/subagents.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/system_prompt.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/textual_adapter.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/theme.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/token_state.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/tool_display.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/tools.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/ui.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/unicode_security.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/update_check.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/_links.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/approval.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/ask_user.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/autocomplete.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/chat_input.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/diff.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/history.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/loading.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/mcp_viewer.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/messages.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/model_selector.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/notification_settings.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/status.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/theme_selector.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/thread_selector.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/tool_renderers.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/tool_widgets.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/welcome.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/arxiv-search/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/arxiv-search/arxiv_search.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/langgraph-docs/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/skill-creator/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/web-research/SKILL.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/images/cli.png +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/scripts/check_imports.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/scripts/debug_server.sh +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/scripts/install.sh +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/README.md +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/benchmarks/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/conftest.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/test_acp_mode.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/test_sandbox_factory.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/test_sandbox_operations.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/conftest.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/deploy/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/deploy/test_commands.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/deploy/test_config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/skills/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/skills/test_commands.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/skills/test_load.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/skills/test_skills_json.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_agent.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_agent_friendly.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_app.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_approval.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_args.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_ask_user.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_ask_user_middleware.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_autocomplete.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_charset.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_chat_input.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_command_registry.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_compact_tool.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_configurable_model.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_debug.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_editor.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_end_to_end.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_env_vars.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_exception_handling.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_file_ops.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_history.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_hooks.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_imports.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_input_parsing.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_links.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_loading.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_local_context.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_main.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_main_acp_mode.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_main_args.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_mcp_tools.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_mcp_trust.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_mcp_viewer.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_media_utils.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_messages.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_model_config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_model_selector.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_model_switch.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_non_interactive.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_offload.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_output.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_reload.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_remote_client.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_sandbox_factory.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_server.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_server_config.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_server_graph.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_server_helpers.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_server_manager.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_sessions.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_shell_allow_list.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_skill_invocation.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_status.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_subagents.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_textual_adapter.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_theme.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_thread_selector.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_token_tracker.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_ui.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_unicode_security.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_update_check.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_version.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_welcome.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/tools/__init__.py +0 -0
- {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/tools/test_fetch_url.py +0 -0
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.38](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.37...deepagents-cli==0.0.38) (2026-04-15)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **cli:** user scoped memory ([#2708](https://github.com/langchain-ai/deepagents/issues/2708)) ([23bfca6](https://github.com/langchain-ai/deepagents/commit/23bfca6e46e6f3e4fba6657d858ddd5a0b06626f))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **deepagents:** remove old integration tests ([#2728](https://github.com/langchain-ai/deepagents/issues/2728)) ([6653197](https://github.com/langchain-ai/deepagents/commit/6653197b6cbec6dd1ca23d9f90bc1439ca26e6e5))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Performance Improvements
|
|
17
|
+
|
|
18
|
+
* **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))
|
|
19
|
+
|
|
3
20
|
## [0.0.37](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.36...deepagents-cli==0.0.37) (2026-04-10)
|
|
4
21
|
|
|
5
22
|
### Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagents-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.38
|
|
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.38" # 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."""
|
|
@@ -1505,11 +1505,15 @@ class DeepAgentsApp(App):
|
|
|
1505
1505
|
|
|
1506
1506
|
try:
|
|
1507
1507
|
from deepagents_cli._version import __version__ as cli_version
|
|
1508
|
+
from deepagents_cli.config import _is_editable_install
|
|
1509
|
+
|
|
1510
|
+
if await asyncio.to_thread(_is_editable_install):
|
|
1511
|
+
heading = f"Now running v{cli_version}"
|
|
1512
|
+
else:
|
|
1513
|
+
heading = f"Updated to v{cli_version}"
|
|
1508
1514
|
|
|
1509
1515
|
await self._mount_message(
|
|
1510
|
-
AppMessage(
|
|
1511
|
-
f"Updated to v{cli_version}\nSee what's new: {CHANGELOG_URL}"
|
|
1512
|
-
)
|
|
1516
|
+
AppMessage(f"{heading}\nSee what's new: {CHANGELOG_URL}")
|
|
1513
1517
|
)
|
|
1514
1518
|
except Exception:
|
|
1515
1519
|
logger.debug("What's new banner display failed", exc_info=True)
|
|
@@ -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
|
|
@@ -25,6 +32,7 @@ from deepagents_cli.deploy.config import (
|
|
|
25
32
|
AGENTS_MD_FILENAME,
|
|
26
33
|
MCP_FILENAME,
|
|
27
34
|
SKILLS_DIRNAME,
|
|
35
|
+
USER_DIRNAME,
|
|
28
36
|
DeployConfig,
|
|
29
37
|
)
|
|
30
38
|
from deepagents_cli.deploy.templates import (
|
|
@@ -75,9 +83,10 @@ def bundle(
|
|
|
75
83
|
encoding="utf-8",
|
|
76
84
|
)
|
|
77
85
|
logger.info(
|
|
78
|
-
"Wrote _seed.json (memories: %d, skills: %d)",
|
|
86
|
+
"Wrote _seed.json (memories: %d, skills: %d, user_memories: %d)",
|
|
79
87
|
len(seed["memories"]),
|
|
80
88
|
len(seed["skills"]),
|
|
89
|
+
len(seed.get("user_memories", {})),
|
|
81
90
|
)
|
|
82
91
|
|
|
83
92
|
# 3. Copy mcp.json if present.
|
|
@@ -96,8 +105,13 @@ def bundle(
|
|
|
96
105
|
logger.info("Copied %s → .env", env_src)
|
|
97
106
|
|
|
98
107
|
# 4. Render deploy_graph.py.
|
|
108
|
+
has_user_memories = (project_root / USER_DIRNAME).is_dir()
|
|
99
109
|
(build_dir / "deploy_graph.py").write_text(
|
|
100
|
-
_render_deploy_graph(
|
|
110
|
+
_render_deploy_graph(
|
|
111
|
+
config,
|
|
112
|
+
mcp_present=mcp_present,
|
|
113
|
+
has_user_memories=has_user_memories,
|
|
114
|
+
),
|
|
101
115
|
encoding="utf-8",
|
|
102
116
|
)
|
|
103
117
|
logger.info("Generated deploy_graph.py")
|
|
@@ -121,32 +135,25 @@ def _build_seed(
|
|
|
121
135
|
config: DeployConfig, # noqa: ARG001
|
|
122
136
|
project_root: Path,
|
|
123
137
|
system_prompt: str,
|
|
124
|
-
) -> dict
|
|
138
|
+
) -> dict:
|
|
125
139
|
"""Build the `_seed.json` payload.
|
|
126
140
|
|
|
127
|
-
Layout
|
|
128
|
-
|
|
129
|
-
```txt
|
|
130
|
-
{
|
|
131
|
-
"memories": { "/AGENTS.md": "..." },
|
|
132
|
-
"skills": { "/<skill>/SKILL.md": "...", ... }
|
|
133
|
-
}
|
|
134
|
-
```
|
|
141
|
+
Layout::
|
|
135
142
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
143
|
+
{
|
|
144
|
+
"memories": { "/AGENTS.md": "..." },
|
|
145
|
+
"skills": { "/<skill>/SKILL.md": "...", ... },
|
|
146
|
+
"user_memories": { "/AGENTS.md": "..." }
|
|
147
|
+
}
|
|
139
148
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
149
|
+
``memories`` and ``skills`` are read-only at runtime.
|
|
150
|
+
``user_memories`` contains a single writable ``AGENTS.md`` mounted at
|
|
151
|
+
``/memories/user/``, namespaced per user_id. If the project has a
|
|
152
|
+
``user/`` directory (even if empty), an ``AGENTS.md`` is always seeded.
|
|
143
153
|
"""
|
|
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
154
|
memories: dict[str, str] = {f"/{AGENTS_MD_FILENAME}": system_prompt}
|
|
149
155
|
skills: dict[str, str] = {}
|
|
156
|
+
user_memories: dict[str, str] = {}
|
|
150
157
|
|
|
151
158
|
skills_dir = project_root / SKILLS_DIRNAME
|
|
152
159
|
if skills_dir.is_dir():
|
|
@@ -155,13 +162,28 @@ def _build_seed(
|
|
|
155
162
|
rel = f.relative_to(skills_dir).as_posix()
|
|
156
163
|
skills[f"/{rel}"] = f.read_text(encoding="utf-8")
|
|
157
164
|
|
|
158
|
-
|
|
165
|
+
user_dir = project_root / USER_DIRNAME
|
|
166
|
+
if user_dir.is_dir():
|
|
167
|
+
user_agents_md = user_dir / AGENTS_MD_FILENAME
|
|
168
|
+
content = (
|
|
169
|
+
user_agents_md.read_text(encoding="utf-8")
|
|
170
|
+
if user_agents_md.is_file()
|
|
171
|
+
else ""
|
|
172
|
+
)
|
|
173
|
+
user_memories[f"/{AGENTS_MD_FILENAME}"] = content
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
"memories": memories,
|
|
177
|
+
"skills": skills,
|
|
178
|
+
"user_memories": user_memories,
|
|
179
|
+
}
|
|
159
180
|
|
|
160
181
|
|
|
161
182
|
def _render_deploy_graph(
|
|
162
183
|
config: DeployConfig,
|
|
163
184
|
*,
|
|
164
185
|
mcp_present: bool,
|
|
186
|
+
has_user_memories: bool = False,
|
|
165
187
|
) -> str:
|
|
166
188
|
"""Render the generated `deploy_graph.py`."""
|
|
167
189
|
provider = config.sandbox.provider
|
|
@@ -186,6 +208,7 @@ def _render_deploy_graph(
|
|
|
186
208
|
mcp_tools_block=mcp_tools_block,
|
|
187
209
|
mcp_tools_load_call=mcp_tools_load_call,
|
|
188
210
|
default_assistant_id=config.agent.name,
|
|
211
|
+
has_user_memories=has_user_memories,
|
|
189
212
|
)
|
|
190
213
|
|
|
191
214
|
|
|
@@ -256,6 +279,12 @@ def print_bundle_summary(config: DeployConfig, build_dir: Path) -> None:
|
|
|
256
279
|
for f in memory_files:
|
|
257
280
|
print(f" {f}")
|
|
258
281
|
|
|
282
|
+
user_memory_files = sorted(seed.get("user_memories", {}).keys())
|
|
283
|
+
if user_memory_files:
|
|
284
|
+
print(f"\n User memory seed ({len(user_memory_files)} file(s)):")
|
|
285
|
+
for f in user_memory_files:
|
|
286
|
+
print(f" {f}")
|
|
287
|
+
|
|
259
288
|
skills_files = sorted(seed.get("skills", {}).keys())
|
|
260
289
|
if skills_files:
|
|
261
290
|
print(f"\n Skills seed ({len(skills_files)} file(s)):")
|
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
These templates are rendered by the bundler with values from
|
|
4
4
|
`~deepagents_cli.deploy.config.DeployConfig`.
|
|
5
5
|
|
|
6
|
-
The generated
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
The generated ``deploy_graph.py`` uses a ``CompositeBackend`` with all
|
|
7
|
+
managed content under ``/memories/`` — ``/memories/AGENTS.md``,
|
|
8
|
+
``/memories/skills/``, and ``/memories/user/`` (per-user templates) —
|
|
9
|
+
backed by ``StoreBackend`` instances. The configured sandbox is the
|
|
10
|
+
default writable backend. Write access is controlled via
|
|
11
|
+
``FilesystemPermission`` rules derived from each file's YAML frontmatter
|
|
12
|
+
``permissions`` field.
|
|
10
13
|
|
|
11
14
|
There is no hub path and no custom Python tools.
|
|
12
15
|
"""
|
|
@@ -196,21 +199,28 @@ async def _load_mcp_tools():
|
|
|
196
199
|
# ---------------------------------------------------------------------------
|
|
197
200
|
# deploy_graph.py — the generated server entry point
|
|
198
201
|
#
|
|
199
|
-
# Store layout (CompositeBackend with sandbox default +
|
|
202
|
+
# Store layout (CompositeBackend with sandbox default + routed stores):
|
|
200
203
|
#
|
|
201
|
-
# Mount
|
|
202
|
-
#
|
|
203
|
-
# /memories/
|
|
204
|
-
# /skills/
|
|
205
|
-
#
|
|
204
|
+
# Mount Namespace Writable
|
|
205
|
+
# ----------------- ------------------------------------------ --------
|
|
206
|
+
# /memories/user/ (assistant_id, user_id) yes [user AGENTS.md]
|
|
207
|
+
# /memories/skills/ (assistant_id,) no
|
|
208
|
+
# /memories/ (assistant_id,) no [AGENTS.md]
|
|
209
|
+
# default sandbox (per scope) yes
|
|
206
210
|
#
|
|
207
211
|
# `make_graph` takes the `RunnableConfig` at factory time, pulls
|
|
208
212
|
# `assistant_id` from `config["configurable"]`, and uses it as the
|
|
209
213
|
# top-level namespace component so different assistants built from the
|
|
210
214
|
# same graph have isolated memories and skills.
|
|
211
215
|
#
|
|
212
|
-
#
|
|
213
|
-
#
|
|
216
|
+
# User memories are namespaced per (assistant_id, user_id) so each
|
|
217
|
+
# user gets their own copy. Template files are seeded on first access
|
|
218
|
+
# (only if not already present). Write access is controlled per-file
|
|
219
|
+
# via frontmatter ``permissions: read-write`` declarations.
|
|
220
|
+
#
|
|
221
|
+
# The bundler ships `_seed.json` containing all payloads; the factory
|
|
222
|
+
# seeds each namespace once per (process, assistant_id) and user
|
|
223
|
+
# memories once per (process, assistant_id, user_id).
|
|
214
224
|
# ---------------------------------------------------------------------------
|
|
215
225
|
|
|
216
226
|
DEPLOY_GRAPH_TEMPLATE = '''\
|
|
@@ -251,8 +261,13 @@ SANDBOX_TEMPLATE = {sandbox_template!r}
|
|
|
251
261
|
SANDBOX_IMAGE = {sandbox_image!r}
|
|
252
262
|
|
|
253
263
|
# Mount points inside the composite backend.
|
|
264
|
+
# Everything lives under /memories/ — longest-prefix-first routing
|
|
265
|
+
# ensures /memories/user/ and /memories/skills/ match before /memories/.
|
|
254
266
|
MEMORIES_PREFIX = "/memories/"
|
|
255
|
-
SKILLS_PREFIX = "/skills/"
|
|
267
|
+
SKILLS_PREFIX = "/memories/skills/"
|
|
268
|
+
USER_PREFIX = "/memories/user/"
|
|
269
|
+
|
|
270
|
+
HAS_USER_MEMORIES = {has_user_memories!r}
|
|
256
271
|
|
|
257
272
|
# What to seed into the store on first run.
|
|
258
273
|
SEED_PATH = Path(__file__).parent / "_seed.json"
|
|
@@ -346,19 +361,22 @@ def _load_seed() -> dict:
|
|
|
346
361
|
if _SEED_CACHE is not None:
|
|
347
362
|
return _SEED_CACHE
|
|
348
363
|
if not SEED_PATH.exists():
|
|
349
|
-
_SEED_CACHE = {{"memories": {{}}, "skills": {{}}}}
|
|
364
|
+
_SEED_CACHE = {{"memories": {{}}, "skills": {{}}, "user_memories": {{}}}}
|
|
350
365
|
return _SEED_CACHE
|
|
351
366
|
try:
|
|
352
367
|
_SEED_CACHE = json.loads(SEED_PATH.read_text(encoding="utf-8"))
|
|
353
368
|
except Exception as exc: # noqa: BLE001
|
|
354
369
|
logger.warning("Failed to parse _seed.json: %s", exc)
|
|
355
|
-
_SEED_CACHE = {{"memories": {{}}, "skills": {{}}}}
|
|
370
|
+
_SEED_CACHE = {{"memories": {{}}, "skills": {{}}, "user_memories": {{}}}}
|
|
356
371
|
return _SEED_CACHE
|
|
357
372
|
|
|
358
373
|
|
|
359
374
|
# Per-(process, assistant_id) gate.
|
|
360
375
|
_SEEDED_ASSISTANTS: set[str] = set()
|
|
361
376
|
|
|
377
|
+
# Per-(process, assistant_id, user_id) gate for user memories.
|
|
378
|
+
_SEEDED_USERS: set[tuple[str, str]] = set()
|
|
379
|
+
|
|
362
380
|
|
|
363
381
|
async def _seed_store_if_needed(store, assistant_id: str) -> None:
|
|
364
382
|
"""Seed memories + skills under ``assistant_id`` once per process."""
|
|
@@ -368,7 +386,7 @@ async def _seed_store_if_needed(store, assistant_id: str) -> None:
|
|
|
368
386
|
|
|
369
387
|
seed = _load_seed()
|
|
370
388
|
|
|
371
|
-
memories_ns = (assistant_id,
|
|
389
|
+
memories_ns = (assistant_id,)
|
|
372
390
|
for path, content in seed.get("memories", {{}}).items():
|
|
373
391
|
if await store.aget(memories_ns, path) is None:
|
|
374
392
|
await store.aput(
|
|
@@ -377,7 +395,7 @@ async def _seed_store_if_needed(store, assistant_id: str) -> None:
|
|
|
377
395
|
{{"content": content, "encoding": "utf-8"}},
|
|
378
396
|
)
|
|
379
397
|
|
|
380
|
-
skills_ns = (assistant_id,
|
|
398
|
+
skills_ns = (assistant_id,)
|
|
381
399
|
for path, content in seed.get("skills", {{}}).items():
|
|
382
400
|
if await store.aget(skills_ns, path) is None:
|
|
383
401
|
await store.aput(
|
|
@@ -387,15 +405,67 @@ async def _seed_store_if_needed(store, assistant_id: str) -> None:
|
|
|
387
405
|
)
|
|
388
406
|
|
|
389
407
|
|
|
408
|
+
async def _seed_user_memories_if_needed(
|
|
409
|
+
store, assistant_id: str, user_id: str,
|
|
410
|
+
) -> None:
|
|
411
|
+
"""Seed user memory templates once per (assistant_id, user_id).
|
|
412
|
+
|
|
413
|
+
Only writes entries that do not yet exist in the store, so
|
|
414
|
+
user-modified memories are never overwritten.
|
|
415
|
+
"""
|
|
416
|
+
key = (assistant_id, user_id)
|
|
417
|
+
if key in _SEEDED_USERS:
|
|
418
|
+
return
|
|
419
|
+
_SEEDED_USERS.add(key)
|
|
420
|
+
|
|
421
|
+
seed = _load_seed()
|
|
422
|
+
user_memories = seed.get("user_memories", {{}})
|
|
423
|
+
if not user_memories:
|
|
424
|
+
return
|
|
425
|
+
|
|
426
|
+
user_ns = (assistant_id, user_id)
|
|
427
|
+
for path, content in user_memories.items():
|
|
428
|
+
if await store.aget(user_ns, path) is None:
|
|
429
|
+
await store.aput(
|
|
430
|
+
user_ns,
|
|
431
|
+
path,
|
|
432
|
+
{{"content": content, "encoding": "utf-8"}},
|
|
433
|
+
)
|
|
434
|
+
logger.info(
|
|
435
|
+
"Seeded %d user memory template(s) for user %s",
|
|
436
|
+
len(user_memories),
|
|
437
|
+
user_id,
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
|
|
390
441
|
{sandbox_block}
|
|
391
442
|
|
|
392
443
|
{mcp_tools_block}
|
|
393
444
|
|
|
394
445
|
|
|
395
|
-
def _make_namespace_factory(assistant_id: str,
|
|
396
|
-
"""Return a namespace factory closed over an assistant id +
|
|
446
|
+
def _make_namespace_factory(assistant_id: str, *extra: str):
|
|
447
|
+
"""Return a namespace factory closed over an assistant id + extra."""
|
|
448
|
+
ns = (assistant_id, *extra)
|
|
397
449
|
def _factory(ctx): # noqa: ARG001
|
|
398
|
-
return
|
|
450
|
+
return ns
|
|
451
|
+
return _factory
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def _make_user_namespace_factory(assistant_id: str):
|
|
455
|
+
"""Return a namespace factory that includes the user_id.
|
|
456
|
+
|
|
457
|
+
Uses ``rt.server_info.user.identity`` from custom auth. The platform
|
|
458
|
+
always injects user_id from auth, so no configurable fallback is needed.
|
|
459
|
+
"""
|
|
460
|
+
def _factory(rt):
|
|
461
|
+
user = getattr(rt.server_info, "user", None) if rt.server_info else None
|
|
462
|
+
identity = getattr(user, "identity", None) if user else None
|
|
463
|
+
if not identity:
|
|
464
|
+
raise ValueError(
|
|
465
|
+
"user_id is required when user memories are enabled. "
|
|
466
|
+
"Set it via custom auth (runtime.user.identity)."
|
|
467
|
+
)
|
|
468
|
+
return (assistant_id, str(identity))
|
|
399
469
|
return _factory
|
|
400
470
|
|
|
401
471
|
|
|
@@ -413,16 +483,24 @@ def _build_backend_factory(assistant_id: str):
|
|
|
413
483
|
thread_id = get_config().get("configurable", {{}}).get("thread_id", "local")
|
|
414
484
|
cache_key = f"thread:{{thread_id}}"
|
|
415
485
|
sandbox_backend = _get_or_create_sandbox(cache_key)
|
|
486
|
+
|
|
487
|
+
routes = {{
|
|
488
|
+
MEMORIES_PREFIX: StoreBackend(
|
|
489
|
+
namespace=_make_namespace_factory(assistant_id),
|
|
490
|
+
),
|
|
491
|
+
SKILLS_PREFIX: StoreBackend(
|
|
492
|
+
namespace=_make_namespace_factory(assistant_id),
|
|
493
|
+
),
|
|
494
|
+
}}
|
|
495
|
+
|
|
496
|
+
if HAS_USER_MEMORIES:
|
|
497
|
+
routes[USER_PREFIX] = StoreBackend(
|
|
498
|
+
namespace=_make_user_namespace_factory(assistant_id),
|
|
499
|
+
)
|
|
500
|
+
|
|
416
501
|
return CompositeBackend(
|
|
417
502
|
default=sandbox_backend,
|
|
418
|
-
routes=
|
|
419
|
-
MEMORIES_PREFIX: StoreBackend(
|
|
420
|
-
namespace=_make_namespace_factory(assistant_id, "memories"),
|
|
421
|
-
),
|
|
422
|
-
SKILLS_PREFIX: StoreBackend(
|
|
423
|
-
namespace=_make_namespace_factory(assistant_id, "skills"),
|
|
424
|
-
),
|
|
425
|
-
}},
|
|
503
|
+
routes=routes,
|
|
426
504
|
)
|
|
427
505
|
return _factory
|
|
428
506
|
|
|
@@ -430,36 +508,58 @@ def _build_backend_factory(assistant_id: str):
|
|
|
430
508
|
async def make_graph(config: RunnableConfig, runtime: "ServerRuntime"):
|
|
431
509
|
"""Async graph factory.
|
|
432
510
|
|
|
433
|
-
Accepts the invocation's ``RunnableConfig``
|
|
434
|
-
``
|
|
435
|
-
|
|
436
|
-
(process, assistant_id
|
|
511
|
+
Accepts the invocation's ``RunnableConfig`` for ``assistant_id`` and
|
|
512
|
+
the ``ServerRuntime`` for ``store`` and ``user.identity``. Seeds
|
|
513
|
+
memories + skills once per (process, assistant_id), and user memories
|
|
514
|
+
once per (process, assistant_id, user_id). Gracefully skips user
|
|
515
|
+
memory features when no user_id is available.
|
|
437
516
|
"""
|
|
438
517
|
configurable = (config or {{}}).get("configurable", {{}}) or {{}}
|
|
439
518
|
assistant_id = str(configurable.get("assistant_id") or {default_assistant_id!r})
|
|
440
519
|
|
|
441
520
|
store = getattr(runtime, "store", None)
|
|
521
|
+
user_id = None
|
|
522
|
+
if HAS_USER_MEMORIES:
|
|
523
|
+
user = getattr(runtime, "user", None)
|
|
524
|
+
identity = getattr(user, "identity", None) if user else None
|
|
525
|
+
user_id = str(identity) if identity else None
|
|
526
|
+
if HAS_USER_MEMORIES and not user_id:
|
|
527
|
+
logger.warning(
|
|
528
|
+
"User memories are enabled but no user_id found "
|
|
529
|
+
"(runtime.user.identity is empty). User memory features "
|
|
530
|
+
"will be skipped for this invocation."
|
|
531
|
+
)
|
|
442
532
|
if store is not None:
|
|
443
533
|
await _seed_store_if_needed(store, assistant_id)
|
|
534
|
+
if HAS_USER_MEMORIES and user_id:
|
|
535
|
+
await _seed_user_memories_if_needed(store, assistant_id, user_id)
|
|
444
536
|
|
|
445
537
|
tools: list = []
|
|
446
538
|
{mcp_tools_load_call}
|
|
447
539
|
|
|
448
540
|
backend_factory = _build_backend_factory(assistant_id)
|
|
449
541
|
|
|
542
|
+
# Preload AGENTS.md + user memory into the agent's context.
|
|
543
|
+
memory_sources = [f"{{MEMORIES_PREFIX}}AGENTS.md"]
|
|
544
|
+
if HAS_USER_MEMORIES and user_id:
|
|
545
|
+
memory_sources.append(f"{{USER_PREFIX}}AGENTS.md")
|
|
546
|
+
|
|
547
|
+
# AGENTS.md and skills are read-only; user memories are writable.
|
|
548
|
+
permissions = [
|
|
549
|
+
FilesystemPermission(
|
|
550
|
+
operations=["write"],
|
|
551
|
+
paths=[f"{{MEMORIES_PREFIX}}AGENTS.md", f"{{SKILLS_PREFIX}}**"],
|
|
552
|
+
mode="deny",
|
|
553
|
+
),
|
|
554
|
+
]
|
|
555
|
+
|
|
450
556
|
return create_deep_agent(
|
|
451
557
|
model={model!r},
|
|
452
|
-
memory=
|
|
558
|
+
memory=memory_sources,
|
|
453
559
|
skills=[SKILLS_PREFIX],
|
|
454
560
|
tools=tools,
|
|
455
561
|
backend=backend_factory,
|
|
456
|
-
permissions=
|
|
457
|
-
FilesystemPermission(
|
|
458
|
-
operations=["write"],
|
|
459
|
-
paths=["/memories/**", "/skills/**"],
|
|
460
|
-
mode="deny",
|
|
461
|
-
),
|
|
462
|
-
],
|
|
562
|
+
permissions=permissions,
|
|
463
563
|
middleware=[
|
|
464
564
|
SandboxSyncMiddleware(backend=backend_factory, sources=[SKILLS_PREFIX]),
|
|
465
565
|
],
|
|
@@ -375,6 +375,13 @@ class MessageStore:
|
|
|
375
375
|
def __init__(self) -> None:
|
|
376
376
|
"""Initialize the message store."""
|
|
377
377
|
self._messages: list[MessageData] = []
|
|
378
|
+
self._index: dict[str, MessageData] = {}
|
|
379
|
+
"""ID -> MessageData lookup.
|
|
380
|
+
|
|
381
|
+
Must contain exactly one entry per element of `_messages`. Any method
|
|
382
|
+
that adds to or removes from `_messages` must update `_index`
|
|
383
|
+
in lockstep.
|
|
384
|
+
"""
|
|
378
385
|
self._visible_start: int = 0
|
|
379
386
|
self._visible_end: int = 0
|
|
380
387
|
|
|
@@ -407,7 +414,14 @@ class MessageStore:
|
|
|
407
414
|
Args:
|
|
408
415
|
message: The message data to add.
|
|
409
416
|
"""
|
|
417
|
+
if message.id in self._index:
|
|
418
|
+
logger.warning(
|
|
419
|
+
"Duplicate message ID %r appended; previous entry will be "
|
|
420
|
+
"unreachable via get_message()",
|
|
421
|
+
message.id,
|
|
422
|
+
)
|
|
410
423
|
self._messages.append(message)
|
|
424
|
+
self._index[message.id] = message
|
|
411
425
|
self._visible_end = len(self._messages)
|
|
412
426
|
|
|
413
427
|
def bulk_load(
|
|
@@ -426,6 +440,14 @@ class MessageStore:
|
|
|
426
440
|
Tuple of (archived, visible) message lists.
|
|
427
441
|
"""
|
|
428
442
|
self._messages.extend(messages)
|
|
443
|
+
for msg in messages:
|
|
444
|
+
if msg.id in self._index:
|
|
445
|
+
logger.warning(
|
|
446
|
+
"Duplicate message ID %r in bulk_load; previous entry "
|
|
447
|
+
"will be unreachable via get_message()",
|
|
448
|
+
msg.id,
|
|
449
|
+
)
|
|
450
|
+
self._index[msg.id] = msg
|
|
429
451
|
total = len(self._messages)
|
|
430
452
|
|
|
431
453
|
if total <= self.WINDOW_SIZE:
|
|
@@ -448,10 +470,7 @@ class MessageStore:
|
|
|
448
470
|
Returns:
|
|
449
471
|
The message data, or None if not found.
|
|
450
472
|
"""
|
|
451
|
-
|
|
452
|
-
if msg.id == message_id:
|
|
453
|
-
return msg
|
|
454
|
-
return None
|
|
473
|
+
return self._index.get(message_id)
|
|
455
474
|
|
|
456
475
|
def get_message_at_index(self, index: int) -> MessageData | None:
|
|
457
476
|
"""Get a message by its index.
|
|
@@ -488,12 +507,16 @@ class MessageStore:
|
|
|
488
507
|
msg = f"Cannot update unknown or protected fields: {unknown}"
|
|
489
508
|
raise ValueError(msg)
|
|
490
509
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
510
|
+
msg_data = self._index.get(message_id)
|
|
511
|
+
if msg_data is None:
|
|
512
|
+
logger.warning(
|
|
513
|
+
"update_message called for unknown ID %r; update discarded",
|
|
514
|
+
message_id,
|
|
515
|
+
)
|
|
516
|
+
return False
|
|
517
|
+
for key, value in updates.items():
|
|
518
|
+
setattr(msg_data, key, value)
|
|
519
|
+
return True
|
|
497
520
|
|
|
498
521
|
def set_active_message(self, message_id: str | None) -> None:
|
|
499
522
|
"""Set the currently active (streaming) message.
|
|
@@ -646,6 +669,7 @@ class MessageStore:
|
|
|
646
669
|
def clear(self) -> None:
|
|
647
670
|
"""Clear all messages."""
|
|
648
671
|
self._messages.clear()
|
|
672
|
+
self._index.clear()
|
|
649
673
|
self._visible_start = 0
|
|
650
674
|
self._visible_end = 0
|
|
651
675
|
self._active_message_id = None
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blog-post
|
|
3
|
+
description: Write long-form blog posts with SEO optimization and clear structure.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Blog Post Writing
|
|
7
|
+
|
|
8
|
+
Write blog posts that are informative, engaging, and optimized for search.
|
|
9
|
+
|
|
10
|
+
## Structure
|
|
11
|
+
|
|
12
|
+
1. **Title** — Clear, compelling, includes target keyword
|
|
13
|
+
2. **Introduction** — Hook the reader, state the problem, preview the solution
|
|
14
|
+
3. **Body** — 3-5 sections with headers, each making one clear point
|
|
15
|
+
4. **Conclusion** — Summarize key takeaways, include a call to action
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: social-media
|
|
3
|
+
description: Create social media posts optimized for engagement across platforms.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Social Media Content
|
|
7
|
+
|
|
8
|
+
Create platform-appropriate social media posts.
|
|
9
|
+
|
|
10
|
+
## Guidelines
|
|
11
|
+
|
|
12
|
+
- Keep it concise and punchy
|
|
13
|
+
- Lead with a hook
|
|
14
|
+
- Include a clear call to action
|
|
15
|
+
- Adapt tone per platform (LinkedIn vs Twitter vs etc.)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: context
|
|
3
|
+
description: Company and product context for content creation
|
|
4
|
+
permissions: read
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Company Context
|
|
8
|
+
|
|
9
|
+
Update this file with your company's specific context:
|
|
10
|
+
|
|
11
|
+
- Company name and description
|
|
12
|
+
- Product names and key features
|
|
13
|
+
- Target audience details
|
|
14
|
+
- Competitor landscape
|
|
15
|
+
- Key differentiators
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: preferences
|
|
3
|
+
description: User content preferences and style overrides
|
|
4
|
+
permissions: read-write
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Content Preferences
|
|
8
|
+
|
|
9
|
+
No preferences set yet. The agent will update this file as it learns
|
|
10
|
+
about the user's preferred topics, tone adjustments, and formatting
|
|
11
|
+
choices.
|