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.
Files changed (198) hide show
  1. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/CHANGELOG.md +17 -0
  2. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/PKG-INFO +2 -2
  3. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_version.py +1 -1
  4. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/app.py +7 -3
  5. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/deploy/bundler.py +51 -22
  6. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/deploy/config.py +1 -0
  7. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/deploy/templates.py +140 -40
  8. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/message_store.py +34 -10
  9. deepagents_cli-0.0.38/examples/deploy-content-writer/.env.example +2 -0
  10. deepagents_cli-0.0.38/examples/deploy-content-writer/skills/blog-post/SKILL.md +15 -0
  11. deepagents_cli-0.0.38/examples/deploy-content-writer/skills/social-media/SKILL.md +15 -0
  12. deepagents_cli-0.0.38/examples/deploy-content-writer/user/context.md +15 -0
  13. deepagents_cli-0.0.38/examples/deploy-content-writer/user/preferences.md +11 -0
  14. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/pyproject.toml +2 -2
  15. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/test_compact_resume.py +6 -11
  16. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/deploy/test_bundler.py +106 -1
  17. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_message_store.py +71 -0
  18. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/uv.lock +92 -92
  19. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/.gitignore +0 -0
  20. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/DEV.md +0 -0
  21. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/Makefile +0 -0
  22. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/README.md +0 -0
  23. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/THREAT_MODEL.md +0 -0
  24. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/__init__.py +0 -0
  25. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/__main__.py +0 -0
  26. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_ask_user_types.py +0 -0
  27. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_cli_context.py +0 -0
  28. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_debug.py +0 -0
  29. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_env_vars.py +0 -0
  30. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_server_config.py +0 -0
  31. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_session_stats.py +0 -0
  32. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/_testing_models.py +0 -0
  33. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/agent.py +0 -0
  34. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/app.tcss +0 -0
  35. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/ask_user.py +0 -0
  36. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/built_in_skills/__init__.py +0 -0
  37. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/built_in_skills/remember/SKILL.md +0 -0
  38. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/built_in_skills/skill-creator/SKILL.md +0 -0
  39. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
  40. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
  41. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/clipboard.py +0 -0
  42. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/command_registry.py +0 -0
  43. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/config.py +0 -0
  44. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/configurable_model.py +0 -0
  45. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/default_agent_prompt.md +0 -0
  46. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/deploy/__init__.py +0 -0
  47. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/deploy/commands.py +0 -0
  48. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/editor.py +0 -0
  49. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/file_ops.py +0 -0
  50. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/formatting.py +0 -0
  51. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/hooks.py +0 -0
  52. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/input.py +0 -0
  53. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/integrations/__init__.py +0 -0
  54. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/integrations/sandbox_factory.py +0 -0
  55. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/integrations/sandbox_provider.py +0 -0
  56. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/local_context.py +0 -0
  57. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/main.py +0 -0
  58. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/mcp_tools.py +0 -0
  59. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/mcp_trust.py +0 -0
  60. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/media_utils.py +0 -0
  61. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/model_config.py +0 -0
  62. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/non_interactive.py +0 -0
  63. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/offload.py +0 -0
  64. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/output.py +0 -0
  65. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/project_utils.py +0 -0
  66. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/py.typed +0 -0
  67. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/remote_client.py +0 -0
  68. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/server.py +0 -0
  69. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/server_graph.py +0 -0
  70. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/server_manager.py +0 -0
  71. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/sessions.py +0 -0
  72. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/skills/__init__.py +0 -0
  73. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/skills/commands.py +0 -0
  74. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/skills/invocation.py +0 -0
  75. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/skills/load.py +0 -0
  76. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/subagents.py +0 -0
  77. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/system_prompt.md +0 -0
  78. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/textual_adapter.py +0 -0
  79. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/theme.py +0 -0
  80. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/token_state.py +0 -0
  81. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/tool_display.py +0 -0
  82. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/tools.py +0 -0
  83. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/ui.py +0 -0
  84. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/unicode_security.py +0 -0
  85. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/update_check.py +0 -0
  86. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/__init__.py +0 -0
  87. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/_links.py +0 -0
  88. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/approval.py +0 -0
  89. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/ask_user.py +0 -0
  90. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/autocomplete.py +0 -0
  91. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/chat_input.py +0 -0
  92. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/diff.py +0 -0
  93. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/history.py +0 -0
  94. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/loading.py +0 -0
  95. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/mcp_viewer.py +0 -0
  96. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/messages.py +0 -0
  97. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/model_selector.py +0 -0
  98. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/notification_settings.py +0 -0
  99. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/status.py +0 -0
  100. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/theme_selector.py +0 -0
  101. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/thread_selector.py +0 -0
  102. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/tool_renderers.py +0 -0
  103. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/tool_widgets.py +0 -0
  104. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/deepagents_cli/widgets/welcome.py +0 -0
  105. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/arxiv-search/SKILL.md +0 -0
  106. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/arxiv-search/arxiv_search.py +0 -0
  107. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/langgraph-docs/SKILL.md +0 -0
  108. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/skill-creator/SKILL.md +0 -0
  109. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
  110. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
  111. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/examples/skills/web-research/SKILL.md +0 -0
  112. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/images/cli.png +0 -0
  113. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/scripts/check_imports.py +0 -0
  114. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/scripts/debug_server.sh +0 -0
  115. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/scripts/install.sh +0 -0
  116. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/README.md +0 -0
  117. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/__init__.py +0 -0
  118. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/benchmarks/__init__.py +0 -0
  119. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
  120. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
  121. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/conftest.py +0 -0
  122. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/test_acp_mode.py +0 -0
  123. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/test_sandbox_factory.py +0 -0
  124. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/integration_tests/test_sandbox_operations.py +0 -0
  125. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/__init__.py +0 -0
  126. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/conftest.py +0 -0
  127. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/deploy/__init__.py +0 -0
  128. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/deploy/test_commands.py +0 -0
  129. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/deploy/test_config.py +0 -0
  130. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/skills/__init__.py +0 -0
  131. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/skills/test_commands.py +0 -0
  132. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/skills/test_load.py +0 -0
  133. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/skills/test_skills_json.py +0 -0
  134. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_agent.py +0 -0
  135. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_agent_friendly.py +0 -0
  136. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_app.py +0 -0
  137. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_approval.py +0 -0
  138. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_args.py +0 -0
  139. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_ask_user.py +0 -0
  140. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_ask_user_middleware.py +0 -0
  141. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_autocomplete.py +0 -0
  142. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_charset.py +0 -0
  143. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_chat_input.py +0 -0
  144. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_command_registry.py +0 -0
  145. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_compact_tool.py +0 -0
  146. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_config.py +0 -0
  147. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_configurable_model.py +0 -0
  148. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_debug.py +0 -0
  149. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_editor.py +0 -0
  150. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_end_to_end.py +0 -0
  151. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_env_vars.py +0 -0
  152. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_exception_handling.py +0 -0
  153. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_file_ops.py +0 -0
  154. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_history.py +0 -0
  155. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_hooks.py +0 -0
  156. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_imports.py +0 -0
  157. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_input_parsing.py +0 -0
  158. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_links.py +0 -0
  159. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_loading.py +0 -0
  160. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_local_context.py +0 -0
  161. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_main.py +0 -0
  162. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_main_acp_mode.py +0 -0
  163. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_main_args.py +0 -0
  164. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_mcp_tools.py +0 -0
  165. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_mcp_trust.py +0 -0
  166. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_mcp_viewer.py +0 -0
  167. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_media_utils.py +0 -0
  168. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_messages.py +0 -0
  169. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_model_config.py +0 -0
  170. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_model_selector.py +0 -0
  171. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_model_switch.py +0 -0
  172. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_non_interactive.py +0 -0
  173. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_offload.py +0 -0
  174. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_output.py +0 -0
  175. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_reload.py +0 -0
  176. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_remote_client.py +0 -0
  177. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_sandbox_factory.py +0 -0
  178. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_server.py +0 -0
  179. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_server_config.py +0 -0
  180. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_server_graph.py +0 -0
  181. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_server_helpers.py +0 -0
  182. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_server_manager.py +0 -0
  183. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_sessions.py +0 -0
  184. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_shell_allow_list.py +0 -0
  185. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_skill_invocation.py +0 -0
  186. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_status.py +0 -0
  187. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_subagents.py +0 -0
  188. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_textual_adapter.py +0 -0
  189. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_theme.py +0 -0
  190. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_thread_selector.py +0 -0
  191. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_token_tracker.py +0 -0
  192. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_ui.py +0 -0
  193. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_unicode_security.py +0 -0
  194. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_update_check.py +0 -0
  195. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_version.py +0 -0
  196. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/test_welcome.py +0 -0
  197. {deepagents_cli-0.0.37 → deepagents_cli-0.0.38}/tests/unit_tests/tools/__init__.py +0 -0
  198. {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.37
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.2
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.37" # x-release-please-version
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(config, mcp_present=mcp_present),
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[str, dict[str, str]]:
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
- `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.
143
+ {
144
+ "memories": { "/AGENTS.md": "..." },
145
+ "skills": { "/<skill>/SKILL.md": "...", ... },
146
+ "user_memories": { "/AGENTS.md": "..." }
147
+ }
139
148
 
140
- `skills` walks `skills/` if present. Keys are paths relative to the
141
- skills dir with a leading slash; the runtime namespace handles the
142
- scoping.
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
- return {"memories": memories, "skills": skills}
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)):")
@@ -41,6 +41,7 @@ DEFAULT_CONFIG_FILENAME = "deepagents.toml"
41
41
  # Canonical filenames inside the project root.
42
42
  AGENTS_MD_FILENAME = "AGENTS.md"
43
43
  SKILLS_DIRNAME = "skills"
44
+ USER_DIRNAME = "user"
44
45
  MCP_FILENAME = "mcp.json"
45
46
 
46
47
 
@@ -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 `deploy_graph.py` uses a `CompositeBackend` with two
7
- `StoreBackend` routes (memories and skills) and the configured sandbox
8
- as the default writable backend. Write access to `/memories/` and
9
- `/skills/` is denied via ``FilesystemPermission`` rules.
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 + two read-only routes):
202
+ # Store layout (CompositeBackend with sandbox default + routed stores):
200
203
  #
201
- # Mount Namespace Writable
202
- # ------------- -------------------------------- --------
203
- # /memories/ (assistant_id, "memories") no
204
- # /skills/ (assistant_id, "skills") no
205
- # default sandbox (per `[sandbox].scope`) yes
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
- # The bundler ships `_seed.json` containing both payloads; the factory
213
- # seeds each namespace once per (process, assistant_id).
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, "memories")
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, "skills")
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, section: str):
396
- """Return a namespace factory closed over an assistant id + section."""
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 (assistant_id, section)
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`` so we can pull the
434
- ``assistant_id`` out of ``configurable`` and scope all store reads
435
- and writes under it. Seeds the memories + skills namespaces once per
436
- (process, assistant_id), then assembles the deep agent graph.
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=[f"{{MEMORIES_PREFIX}}AGENTS.md"],
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
- for msg in self._messages:
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
- for msg_data in self._messages:
492
- if msg_data.id == message_id:
493
- for key, value in updates.items():
494
- setattr(msg_data, key, value)
495
- return True
496
- return False
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,2 @@
1
+ OPENAI_API_KEY=
2
+ LANGSMITH_API_KEY=
@@ -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.