deepagents-cli 0.0.44__tar.gz → 0.0.45__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 (265) hide show
  1. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/CHANGELOG.md +7 -0
  2. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/PKG-INFO +2 -2
  3. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/_version.py +1 -1
  4. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/project_utils.py +62 -10
  5. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/chat_input.py +6 -0
  6. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/loading.py +1 -1
  7. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/pyproject.toml +2 -2
  8. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_chat_input.py +50 -1
  9. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_config.py +139 -6
  10. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/uv.lock +2 -2
  11. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/.gitignore +0 -0
  12. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/DEV.md +0 -0
  13. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/Makefile +0 -0
  14. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/README.md +0 -0
  15. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/THREAT_MODEL.md +0 -0
  16. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/__init__.py +0 -0
  17. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/__main__.py +0 -0
  18. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/_ask_user_types.py +0 -0
  19. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/_cli_context.py +0 -0
  20. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/_debug.py +0 -0
  21. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/_env_vars.py +0 -0
  22. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/_git.py +0 -0
  23. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/_server_config.py +0 -0
  24. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/_session_stats.py +0 -0
  25. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/_testing_models.py +0 -0
  26. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/_textual_patches.py +0 -0
  27. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/agent.py +0 -0
  28. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/app.py +0 -0
  29. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/app.tcss +0 -0
  30. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/ask_user.py +0 -0
  31. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/built_in_skills/__init__.py +0 -0
  32. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/built_in_skills/remember/SKILL.md +0 -0
  33. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/built_in_skills/skill-creator/SKILL.md +0 -0
  34. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
  35. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
  36. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/clipboard.py +0 -0
  37. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/command_registry.py +0 -0
  38. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/config.py +0 -0
  39. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/configurable_model.py +0 -0
  40. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/default_agent_prompt.md +0 -0
  41. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/__init__.py +0 -0
  42. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/bundler.py +0 -0
  43. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/commands.py +0 -0
  44. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/config.py +0 -0
  45. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/frontend_dist/assets/anonymous-B9UzAXQd.js +0 -0
  46. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/frontend_dist/assets/clerk-5xHgyQyG.js +0 -0
  47. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/frontend_dist/assets/highlighted-body-OFNGDK62-rX-7qT8o.js +0 -0
  48. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/frontend_dist/assets/index-DM3gptpu.js +0 -0
  49. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/frontend_dist/assets/index-Ddy7F6KI.css +0 -0
  50. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/frontend_dist/assets/supabase-S6NACDgm.js +0 -0
  51. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/frontend_dist/index.html +0 -0
  52. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/frontend_dist/logo-dark.svg +0 -0
  53. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/frontend_dist/logo-light.svg +0 -0
  54. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/deploy/templates.py +0 -0
  55. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/editor.py +0 -0
  56. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/extras_info.py +0 -0
  57. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/file_ops.py +0 -0
  58. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/formatting.py +0 -0
  59. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/hooks.py +0 -0
  60. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/input.py +0 -0
  61. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/integrations/__init__.py +0 -0
  62. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/integrations/sandbox_factory.py +0 -0
  63. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/integrations/sandbox_provider.py +0 -0
  64. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/local_context.py +0 -0
  65. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/main.py +0 -0
  66. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/mcp_tools.py +0 -0
  67. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/mcp_trust.py +0 -0
  68. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/media_utils.py +0 -0
  69. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/model_config.py +0 -0
  70. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/non_interactive.py +0 -0
  71. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/notifications.py +0 -0
  72. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/offload.py +0 -0
  73. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/output.py +0 -0
  74. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/py.typed +0 -0
  75. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/remote_client.py +0 -0
  76. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/server.py +0 -0
  77. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/server_graph.py +0 -0
  78. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/server_manager.py +0 -0
  79. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/sessions.py +0 -0
  80. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/skills/__init__.py +0 -0
  81. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/skills/commands.py +0 -0
  82. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/skills/invocation.py +0 -0
  83. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/skills/load.py +0 -0
  84. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/subagents.py +0 -0
  85. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/system_prompt.md +0 -0
  86. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/terminal_capabilities.py +0 -0
  87. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/textual_adapter.py +0 -0
  88. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/theme.py +0 -0
  89. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/token_state.py +0 -0
  90. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/tool_display.py +0 -0
  91. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/tools.py +0 -0
  92. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/ui.py +0 -0
  93. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/unicode_security.py +0 -0
  94. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/update_check.py +0 -0
  95. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/__init__.py +0 -0
  96. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/_links.py +0 -0
  97. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/agent_selector.py +0 -0
  98. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/approval.py +0 -0
  99. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/ask_user.py +0 -0
  100. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/autocomplete.py +0 -0
  101. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/diff.py +0 -0
  102. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/history.py +0 -0
  103. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/mcp_viewer.py +0 -0
  104. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/message_store.py +0 -0
  105. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/messages.py +0 -0
  106. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/model_selector.py +0 -0
  107. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/notification_center.py +0 -0
  108. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/notification_detail.py +0 -0
  109. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/notification_settings.py +0 -0
  110. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/status.py +0 -0
  111. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/theme_selector.py +0 -0
  112. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/thread_selector.py +0 -0
  113. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/tool_renderers.py +0 -0
  114. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/tool_widgets.py +0 -0
  115. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/update_available.py +0 -0
  116. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/deepagents_cli/widgets/welcome.py +0 -0
  117. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/deploy-content-writer/.env.example +0 -0
  118. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/deploy-content-writer/skills/blog-post/SKILL.md +0 -0
  119. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/deploy-content-writer/skills/social-media/SKILL.md +0 -0
  120. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/deploy-content-writer/user/context.md +0 -0
  121. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/deploy-content-writer/user/preferences.md +0 -0
  122. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/skills/arxiv-search/SKILL.md +0 -0
  123. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/skills/arxiv-search/arxiv_search.py +0 -0
  124. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/skills/langgraph-docs/SKILL.md +0 -0
  125. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/skills/skill-creator/SKILL.md +0 -0
  126. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
  127. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
  128. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/examples/skills/web-research/SKILL.md +0 -0
  129. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/.nvmrc +0 -0
  130. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/index.html +0 -0
  131. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/package-lock.json +0 -0
  132. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/package.json +0 -0
  133. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/postcss.config.js +0 -0
  134. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/public/logo-dark.svg +0 -0
  135. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/public/logo-light.svg +0 -0
  136. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/App.tsx +0 -0
  137. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/ThemeProvider.tsx +0 -0
  138. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/auth/anonymous.tsx +0 -0
  139. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/auth/clerk.tsx +0 -0
  140. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/auth/loader.tsx +0 -0
  141. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/auth/supabase.tsx +0 -0
  142. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/auth/types.ts +0 -0
  143. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/AppHeader.tsx +0 -0
  144. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/FilePanels.tsx +0 -0
  145. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/MessageList.tsx +0 -0
  146. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/SubagentActivity.tsx +0 -0
  147. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/ThreadPicker.tsx +0 -0
  148. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/TodosPanel.tsx +0 -0
  149. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/ToolCallCard.tsx +0 -0
  150. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/toolcalls/FileToolCard.tsx +0 -0
  151. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/toolcalls/SearchCard.tsx +0 -0
  152. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/toolcalls/ThinkCard.tsx +0 -0
  153. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/toolcalls/TodosCard.tsx +0 -0
  154. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/components/toolcalls/index.ts +0 -0
  155. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/constants.ts +0 -0
  156. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/index.css +0 -0
  157. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/main.tsx +0 -0
  158. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/runtimeConfig.ts +0 -0
  159. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/types.ts +0 -0
  160. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/src/vite-env.d.ts +0 -0
  161. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/tsconfig.json +0 -0
  162. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/frontend/vite.config.ts +0 -0
  163. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/images/cli.png +0 -0
  164. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/scripts/check_imports.py +0 -0
  165. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/scripts/debug_server.sh +0 -0
  166. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/scripts/install.sh +0 -0
  167. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/README.md +0 -0
  168. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/integration_tests/__init__.py +0 -0
  169. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/integration_tests/benchmarks/__init__.py +0 -0
  170. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
  171. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
  172. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/integration_tests/conftest.py +0 -0
  173. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/integration_tests/test_acp_mode.py +0 -0
  174. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/integration_tests/test_compact_resume.py +0 -0
  175. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/integration_tests/test_sandbox_factory.py +0 -0
  176. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/integration_tests/test_sandbox_operations.py +0 -0
  177. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/__init__.py +0 -0
  178. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/conftest.py +0 -0
  179. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/deploy/__init__.py +0 -0
  180. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/deploy/test_bundler.py +0 -0
  181. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/deploy/test_commands.py +0 -0
  182. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/deploy/test_config.py +0 -0
  183. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/deploy/test_frontend_bundle.py +0 -0
  184. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/deploy/test_frontend_config.py +0 -0
  185. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/skills/__init__.py +0 -0
  186. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/skills/test_commands.py +0 -0
  187. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/skills/test_load.py +0 -0
  188. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/skills/test_skills_json.py +0 -0
  189. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_agent.py +0 -0
  190. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_agent_friendly.py +0 -0
  191. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_agent_selector.py +0 -0
  192. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_app.py +0 -0
  193. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_approval.py +0 -0
  194. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_args.py +0 -0
  195. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_ask_user.py +0 -0
  196. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_ask_user_middleware.py +0 -0
  197. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_autocomplete.py +0 -0
  198. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_charset.py +0 -0
  199. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_command_registry.py +0 -0
  200. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_compact_tool.py +0 -0
  201. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_configurable_model.py +0 -0
  202. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_debug.py +0 -0
  203. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_editor.py +0 -0
  204. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_end_to_end.py +0 -0
  205. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_env_vars.py +0 -0
  206. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_exception_handling.py +0 -0
  207. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_extras_info.py +0 -0
  208. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_file_ops.py +0 -0
  209. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_formatting.py +0 -0
  210. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_git.py +0 -0
  211. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_history.py +0 -0
  212. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_hooks.py +0 -0
  213. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_imports.py +0 -0
  214. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_input_parsing.py +0 -0
  215. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_links.py +0 -0
  216. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_loading.py +0 -0
  217. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_local_context.py +0 -0
  218. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_main.py +0 -0
  219. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_main_acp_mode.py +0 -0
  220. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_main_args.py +0 -0
  221. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_mcp_tools.py +0 -0
  222. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_mcp_trust.py +0 -0
  223. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_mcp_viewer.py +0 -0
  224. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_media_utils.py +0 -0
  225. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_message_store.py +0 -0
  226. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_messages.py +0 -0
  227. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_model_config.py +0 -0
  228. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_model_selector.py +0 -0
  229. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_model_switch.py +0 -0
  230. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_non_interactive.py +0 -0
  231. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_notification_center.py +0 -0
  232. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_notification_detail.py +0 -0
  233. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_notifications.py +0 -0
  234. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_offload.py +0 -0
  235. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_offload_dict_messages.py +0 -0
  236. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_output.py +0 -0
  237. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_reload.py +0 -0
  238. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_remote_client.py +0 -0
  239. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_sandbox_factory.py +0 -0
  240. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_server.py +0 -0
  241. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_server_config.py +0 -0
  242. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_server_graph.py +0 -0
  243. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_server_helpers.py +0 -0
  244. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_server_manager.py +0 -0
  245. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_session_stats.py +0 -0
  246. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_sessions.py +0 -0
  247. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_shell_allow_list.py +0 -0
  248. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_skill_invocation.py +0 -0
  249. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_status.py +0 -0
  250. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_subagents.py +0 -0
  251. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_terminal_capabilities.py +0 -0
  252. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_textual_adapter.py +0 -0
  253. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_textual_patches.py +0 -0
  254. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_theme.py +0 -0
  255. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_thread_selector.py +0 -0
  256. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_token_tracker.py +0 -0
  257. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_tool_display.py +0 -0
  258. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_ui.py +0 -0
  259. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_unicode_security.py +0 -0
  260. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_update_available.py +0 -0
  261. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_update_check.py +0 -0
  262. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_version.py +0 -0
  263. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/test_welcome.py +0 -0
  264. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/tools/__init__.py +0 -0
  265. {deepagents_cli-0.0.44 → deepagents_cli-0.0.45}/tests/unit_tests/tools/test_fetch_url.py +0 -0
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.45](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.44...deepagents-cli==0.0.45) (2026-04-30)
4
+
5
+ ### Bug Fixes
6
+
7
+ * Reject out-of-tree symlinked project `AGENTS.md` ([#3039](https://github.com/langchain-ai/deepagents/issues/3039)) ([4618098](https://github.com/langchain-ai/deepagents/commit/4618098e59c8c8d1d3f91354e4f4163a9582f710))
8
+ * Support modified backspace word deletion ([#3037](https://github.com/langchain-ai/deepagents/issues/3037)) ([bc3d019](https://github.com/langchain-ai/deepagents/commit/bc3d01916d2ba34be07d56c9eb9b813008c1584c))
9
+
3
10
  ## [0.0.44](https://github.com/langchain-ai/deepagents/compare/deepagents-cli==0.0.43...deepagents-cli==0.0.44) (2026-04-29)
4
11
 
5
12
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepagents-cli
3
- Version: 0.0.44
3
+ Version: 0.0.45
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.4
30
+ Requires-Dist: deepagents==0.5.5
31
31
  Requires-Dist: httpx<1.0.0,>=0.28.1
32
32
  Requires-Dist: langchain-anthropic<2.0.0,>=1.4.2
33
33
  Requires-Dist: langchain-google-genai<5.0.0,>=4.2.2
@@ -1,6 +1,6 @@
1
1
  """Version information and lightweight constants for `deepagents-cli`."""
2
2
 
3
- __version__ = "0.0.44" # x-release-please-version
3
+ __version__ = "0.0.45" # 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."""
@@ -156,24 +156,76 @@ def find_project_agent_md(project_root: Path) -> list[Path]:
156
156
 
157
157
  Both files will be loaded and combined if both exist.
158
158
 
159
+ Candidates with symlinked path components are followed only when the
160
+ resolved target stays inside `project_root`. The returned `Path` is the
161
+ resolved target when any symlink component was traversed, so
162
+ `FilesystemBackend.download_files` opens a regular file rather than
163
+ tripping `O_NOFOLLOW` on the link itself. Symlinks pointing outside the
164
+ project root, symlink loops, and unreadable parents are skipped with a
165
+ warning. Broken symlinks are treated as missing files (no warning),
166
+ matching the pre-existing behavior for absent candidates.
167
+
168
+ Why: project AGENTS.md is auto-discovered and loaded into the system
169
+ prompt before the first model call. Without the in-tree check, a
170
+ malicious clone could ship `AGENTS.md -> ~/.ssh/config` (or any other
171
+ locally-readable file) and have its contents injected as agent
172
+ instructions on first run.
173
+
159
174
  Args:
160
175
  project_root: Path to the project root directory.
161
176
 
162
177
  Returns:
163
- Existing AGENTS.md paths.
164
-
165
- Empty if neither file exists, one entry if only one is present, or
166
- two entries if both locations have the file.
178
+ Existing AGENTS.md paths, with in-tree symlinked path components
179
+ pre-resolved to their targets. Empty if neither file exists, one
180
+ entry if only one is present, or two entries if both locations
181
+ have the file.
167
182
  """
183
+ # Resolve the root once so the candidate-equality check below works even
184
+ # when the caller passes a non-canonical `project_root` (e.g., macOS
185
+ # `/var` -> `/private/var`, or a path with symlinked ancestors).
186
+ project_root_resolved = project_root.resolve()
168
187
  candidates = [
169
- project_root / ".deepagents" / "AGENTS.md",
170
- project_root / "AGENTS.md",
188
+ project_root_resolved / ".deepagents" / "AGENTS.md",
189
+ project_root_resolved / "AGENTS.md",
171
190
  ]
172
191
  paths: list[Path] = []
173
192
  for candidate in candidates:
174
193
  try:
175
- if candidate.exists():
176
- paths.append(candidate)
177
- except OSError:
178
- pass
194
+ # Single syscall handles existence, broken symlinks, loops, and
195
+ # canonicalization. `strict=True` raises rather than returning
196
+ # the unresolved path.
197
+ resolved = candidate.resolve(strict=True)
198
+ except FileNotFoundError:
199
+ # Absent file or broken symlink — matches the pre-existing
200
+ # silent-skip behavior for missing candidates. `Path.exists()`
201
+ # also returns False for symlink loops on some Python versions,
202
+ # so loops do NOT come through here; see the OSError branch.
203
+ continue
204
+ except (OSError, RuntimeError) as exc:
205
+ # `OSError(ELOOP)` on Python 3.13+, `RuntimeError("Symlink loop
206
+ # ...")` on 3.11-3.12; bare `OSError` for permission/unreadable
207
+ # parent. Security-relevant — warn and skip.
208
+ logger.warning(
209
+ "Skipping AGENTS.md candidate %s: %s",
210
+ candidate,
211
+ exc,
212
+ )
213
+ continue
214
+
215
+ try:
216
+ resolved.relative_to(project_root_resolved)
217
+ except ValueError:
218
+ logger.warning(
219
+ "Skipping AGENTS.md symlink %s: target %s is outside "
220
+ "the project root %s",
221
+ candidate,
222
+ resolved,
223
+ project_root_resolved,
224
+ )
225
+ continue
226
+
227
+ if candidate.absolute() == resolved:
228
+ paths.append(candidate)
229
+ else:
230
+ paths.append(resolved)
179
231
  return paths
@@ -332,6 +332,12 @@ class ChatTextArea(TextArea):
332
332
  show=False,
333
333
  priority=True,
334
334
  ),
335
+ Binding(
336
+ "ctrl+backspace,alt+backspace",
337
+ "delete_word_left",
338
+ "Delete left to start of word",
339
+ show=False,
340
+ ),
335
341
  ]
336
342
  """Key bindings for the chat text area.
337
343
 
@@ -60,7 +60,7 @@ class LoadingWidget(Static):
60
60
  LoadingWidget {
61
61
  height: auto;
62
62
  padding: 0 1;
63
- margin-top: 1;
63
+ margin: 0 0 1 0;
64
64
  }
65
65
 
66
66
  LoadingWidget .loading-container {
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "deepagents-cli"
7
- version = "0.0.44"
7
+ version = "0.0.45"
8
8
  description = "Terminal interface for Deep Agents - interactive AI agent with file operations, shell access, and sub-agent capabilities."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -26,7 +26,7 @@ classifiers = [
26
26
  ]
27
27
  dependencies = [
28
28
  # Framework
29
- "deepagents==0.5.4",
29
+ "deepagents==0.5.5",
30
30
  "langchain>=1.2.15,<2.0.0",
31
31
  "langgraph>=1.1.6,<2.0.0",
32
32
  "langgraph-checkpoint-sqlite>=3.0.0,<4.0.0",
@@ -25,7 +25,6 @@ from deepagents_cli.widgets.chat_input import (
25
25
  if TYPE_CHECKING:
26
26
  from pathlib import Path
27
27
 
28
- import pytest
29
28
  from textual.pilot import Pilot
30
29
 
31
30
 
@@ -225,6 +224,34 @@ class _RecordingApp(App[None]):
225
224
  self.submitted.append(event)
226
225
 
227
226
 
227
+ class TestChatTextAreaKeybindings:
228
+ """Regression tests for terminal key aliases in the chat input."""
229
+
230
+ def test_newline_bindings_do_not_shadow_enter_alias(self) -> None:
231
+ """`ctrl+m` is carriage return in terminals, so it must remain plain Enter."""
232
+ newline_keys = {
233
+ key.strip()
234
+ for binding in ChatTextArea.BINDINGS
235
+ if binding.action == "insert_newline"
236
+ for key in binding.key.split(",")
237
+ }
238
+
239
+ assert "ctrl+m" not in newline_keys
240
+ assert "ctrl+m" not in ChatTextArea._NEWLINE_KEYS
241
+
242
+ def test_modified_backspace_deletes_word_left(self) -> None:
243
+ """Modified Backspace aliases should delete the previous word."""
244
+ word_delete_keys = {
245
+ key.strip()
246
+ for binding in ChatTextArea.BINDINGS
247
+ if binding.action == "delete_word_left"
248
+ for key in binding.key.split(",")
249
+ }
250
+
251
+ assert "ctrl+backspace" in word_delete_keys
252
+ assert "alt+backspace" in word_delete_keys
253
+
254
+
228
255
  class _ImagePasteApp(App[None]):
229
256
  """App that wires a shared tracker into ChatInput for paste tests."""
230
257
 
@@ -2206,6 +2233,28 @@ class TestCtrlUDeleteToLineStart:
2206
2233
  assert ta.cursor_location == (1, 0)
2207
2234
 
2208
2235
 
2236
+ class TestModifiedBackspaceDeleteWordLeft:
2237
+ """Test modified Backspace aliases for word deletion."""
2238
+
2239
+ @pytest.mark.parametrize("key", ["ctrl+backspace", "alt+backspace"])
2240
+ async def test_modified_backspace_deletes_previous_word(self, key: str) -> None:
2241
+ """Modified Backspace should delete the word before the cursor."""
2242
+ app = _ChatInputTestApp()
2243
+ async with app.run_test() as pilot:
2244
+ chat = app.query_one(ChatInput)
2245
+ ta = chat._text_area
2246
+ assert ta is not None
2247
+
2248
+ ta.insert("hello world")
2249
+ await pilot.pause()
2250
+
2251
+ await pilot.press(key)
2252
+ await pilot.pause()
2253
+
2254
+ assert ta.text == "hello "
2255
+ assert ta.cursor_location == (0, 6)
2256
+
2257
+
2209
2258
  class _TextAreaTypingApp(App[None]):
2210
2259
  """Minimal app that captures ChatTextArea.Typing and ChatInput.Typing events."""
2211
2260
 
@@ -222,26 +222,159 @@ class TestProjectAgentMdFinding:
222
222
  assert result == []
223
223
 
224
224
  def test_skips_paths_with_permission_errors(self, tmp_path: Path) -> None:
225
- """Test that OSError from Path.exists() is caught gracefully."""
225
+ """`OSError` from `Path.resolve()` is caught and the candidate is skipped."""
226
226
  project_root = tmp_path / "project"
227
227
  project_root.mkdir()
228
228
 
229
229
  real_md = project_root / "AGENTS.md"
230
230
  real_md.write_text("root instructions")
231
231
 
232
- original_exists = Path.exists
232
+ original_resolve = Path.resolve
233
233
 
234
- def patched_exists(self: Path) -> bool:
234
+ def patched_resolve(self: Path, *args: object, **kwargs: object) -> Path:
235
235
  if self.name == "AGENTS.md" and ".deepagents" in str(self):
236
236
  msg = "Permission denied"
237
237
  raise PermissionError(msg)
238
- return original_exists(self)
238
+ return original_resolve(self, *args, **kwargs) # type: ignore[arg-type]
239
239
 
240
- with patch.object(Path, "exists", patched_exists):
240
+ with patch.object(Path, "resolve", patched_resolve):
241
241
  result = _find_project_agent_md(project_root)
242
242
 
243
243
  assert len(result) == 1
244
- assert result[0] == real_md
244
+ assert result[0].samefile(real_md)
245
+ assert not result[0].is_symlink()
246
+
247
+ def test_in_tree_symlink_resolves_to_target(self, tmp_path: Path) -> None:
248
+ """`AGENTS.md -> CLAUDE.md` returns a non-symlink path same-file as target."""
249
+ project_root = tmp_path / "project"
250
+ project_root.mkdir()
251
+
252
+ target = project_root / "CLAUDE.md"
253
+ target.write_text("real instructions")
254
+
255
+ link = project_root / "AGENTS.md"
256
+ link.symlink_to(target)
257
+
258
+ result = _find_project_agent_md(project_root)
259
+
260
+ assert len(result) == 1
261
+ # Returned path must be the resolved target — not the symlink — so
262
+ # `FilesystemBackend.download_files` opens the regular file rather
263
+ # than tripping `O_NOFOLLOW` on the link itself.
264
+ assert not result[0].is_symlink()
265
+ assert result[0].samefile(target)
266
+ assert result[0].is_relative_to(project_root.resolve())
267
+
268
+ def test_out_of_tree_symlink_skipped(
269
+ self, tmp_path: Path, caplog: pytest.LogCaptureFixture
270
+ ) -> None:
271
+ """Symlink pointing outside project root is skipped with a warning."""
272
+ project_root = tmp_path / "project"
273
+ project_root.mkdir()
274
+ outside_target = tmp_path / "outside.md"
275
+ outside_target.write_text("attacker-controlled")
276
+
277
+ link = project_root / "AGENTS.md"
278
+ link.symlink_to(outside_target)
279
+
280
+ with caplog.at_level(logging.WARNING, logger="deepagents_cli.project_utils"):
281
+ result = _find_project_agent_md(project_root)
282
+
283
+ assert result == []
284
+ assert any("outside the project root" in r.getMessage() for r in caplog.records)
285
+
286
+ def test_out_of_tree_parent_symlink_skipped(
287
+ self, tmp_path: Path, caplog: pytest.LogCaptureFixture
288
+ ) -> None:
289
+ """Parent directory symlink cannot bypass project-root containment."""
290
+ project_root = tmp_path / "project"
291
+ project_root.mkdir()
292
+ outside = tmp_path / "outside"
293
+ outside.mkdir()
294
+ outside_agent_md = outside / "AGENTS.md"
295
+ outside_agent_md.write_text("attacker-controlled")
296
+
297
+ (project_root / ".deepagents").symlink_to(outside, target_is_directory=True)
298
+
299
+ with caplog.at_level(logging.WARNING, logger="deepagents_cli.project_utils"):
300
+ result = _find_project_agent_md(project_root)
301
+
302
+ assert result == []
303
+ assert any("outside the project root" in r.getMessage() for r in caplog.records)
304
+
305
+ def test_broken_symlink_skipped(self, tmp_path: Path) -> None:
306
+ """Symlink whose target does not exist is skipped without crashing."""
307
+ project_root = tmp_path / "project"
308
+ project_root.mkdir()
309
+
310
+ link = project_root / "AGENTS.md"
311
+ link.symlink_to(project_root / "missing.md")
312
+
313
+ result = _find_project_agent_md(project_root)
314
+
315
+ # `Path.exists()` returns False for broken symlinks, so the candidate
316
+ # is silently skipped — matches pre-existing behavior for absent files.
317
+ assert result == []
318
+
319
+ def test_symlink_loop_skipped(
320
+ self, tmp_path: Path, caplog: pytest.LogCaptureFixture
321
+ ) -> None:
322
+ """Symlink loop is skipped with a warning instead of crashing the agent."""
323
+ project_root = tmp_path / "project"
324
+ project_root.mkdir()
325
+
326
+ a = project_root / "AGENTS.md"
327
+ b = project_root / "loop.md"
328
+ a.symlink_to(b)
329
+ b.symlink_to(a)
330
+
331
+ with caplog.at_level(logging.WARNING, logger="deepagents_cli.project_utils"):
332
+ result = _find_project_agent_md(project_root)
333
+
334
+ assert result == []
335
+ assert any(
336
+ "Skipping AGENTS.md candidate" in r.getMessage() for r in caplog.records
337
+ )
338
+
339
+ def test_regular_file_unchanged_by_resolution(self, tmp_path: Path) -> None:
340
+ """Regular (non-symlink) AGENTS.md returns a non-symlink, in-tree path."""
341
+ project_root = tmp_path / "project"
342
+ project_root.mkdir()
343
+
344
+ agent_md = project_root / "AGENTS.md"
345
+ agent_md.write_text("plain file")
346
+
347
+ result = _find_project_agent_md(project_root)
348
+
349
+ assert len(result) == 1
350
+ assert not result[0].is_symlink()
351
+ assert result[0].samefile(agent_md)
352
+ assert result[0].is_relative_to(project_root.resolve())
353
+
354
+ def test_non_canonical_project_root_handled(self, tmp_path: Path) -> None:
355
+ """Non-canonical `project_root` (symlinked ancestor) still locates AGENTS.md.
356
+
357
+ Regression test: a symlinked `project_root` previously caused the
358
+ regular-file candidate to fail the absolute-vs-resolved equality check
359
+ and be returned as the canonical target rather than reported as missing.
360
+ Pin behavior so that callers passing an uncanonicalized root (common
361
+ when `Settings.project_root` originates from an unresolved cwd) still
362
+ find a regular AGENTS.md.
363
+ """
364
+ real_root = tmp_path / "real"
365
+ real_root.mkdir()
366
+ agent_md = real_root / "AGENTS.md"
367
+ agent_md.write_text("instructions")
368
+
369
+ link_root = tmp_path / "link"
370
+ link_root.symlink_to(real_root, target_is_directory=True)
371
+
372
+ result = _find_project_agent_md(link_root)
373
+
374
+ assert len(result) == 1
375
+ assert not result[0].is_symlink()
376
+ assert result[0].samefile(agent_md)
377
+ assert result[0].is_relative_to(link_root.resolve())
245
378
 
246
379
 
247
380
  class TestSettingsGetProjectAgentMdPath:
@@ -967,7 +967,7 @@ wheels = [
967
967
 
968
968
  [[package]]
969
969
  name = "deepagents"
970
- version = "0.5.4"
970
+ version = "0.5.5"
971
971
  source = { editable = "../deepagents" }
972
972
  dependencies = [
973
973
  { name = "langchain" },
@@ -1023,7 +1023,7 @@ wheels = [
1023
1023
 
1024
1024
  [[package]]
1025
1025
  name = "deepagents-cli"
1026
- version = "0.0.44"
1026
+ version = "0.0.45"
1027
1027
  source = { editable = "." }
1028
1028
  dependencies = [
1029
1029
  { name = "aiosqlite" },
File without changes