agentic-cli 0.5.0__tar.gz → 0.5.2__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 (221) hide show
  1. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/CHANGELOG.md +38 -0
  2. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/CLAUDE.md +7 -3
  3. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/PKG-INFO +46 -36
  4. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/README.md +43 -35
  5. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/research_demo/agents.py +1 -1
  6. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/pyproject.toml +3 -1
  7. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/__init__.py +1 -1
  8. agentic_cli-0.5.2/src/agentic_cli/knowledge_base/_bm25_backends.py +137 -0
  9. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/knowledge_base/concepts.py +43 -1
  10. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/knowledge_base/manager.py +61 -18
  11. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/__init__.py +13 -8
  12. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/adk/state_tools.py +6 -0
  13. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/arxiv_tools.py +7 -4
  14. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/execution_tools.py +2 -2
  15. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/executor.py +35 -8
  16. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/factories.py +87 -87
  17. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/file_read.py +3 -5
  18. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/file_write.py +3 -4
  19. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/glob_tool.py +3 -5
  20. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/grep_tool.py +2 -4
  21. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/interaction_tools.py +2 -2
  22. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/knowledge_tools.py +279 -119
  23. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/langgraph/state_tools.py +6 -0
  24. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/memory_tools.py +87 -48
  25. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/reflection_tools.py +3 -2
  26. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/registry.py +49 -25
  27. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/sandbox/__init__.py +3 -2
  28. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/search.py +2 -2
  29. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/executor.py +3 -3
  30. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/sandbox.py +33 -1
  31. agentic_cli-0.5.2/src/agentic_cli/tools/webfetch/fetcher.py +236 -0
  32. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/webfetch_tool.py +3 -2
  33. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/adk/__init__.py +1 -1
  34. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/adk/manager.py +3 -2
  35. agentic_cli-0.5.2/src/agentic_cli/workflow/adk/permission_plugin.py +72 -0
  36. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/adk/plugins.py +1 -97
  37. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/base_manager.py +23 -5
  38. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/langgraph/graph_builder.py +2 -45
  39. agentic_cli-0.5.2/src/agentic_cli/workflow/langgraph/permission_wrap.py +58 -0
  40. agentic_cli-0.5.2/src/agentic_cli/workflow/permissions/__init__.py +37 -0
  41. agentic_cli-0.5.2/src/agentic_cli/workflow/permissions/capabilities.py +44 -0
  42. agentic_cli-0.5.2/src/agentic_cli/workflow/permissions/engine.py +226 -0
  43. agentic_cli-0.5.2/src/agentic_cli/workflow/permissions/matchers.py +193 -0
  44. agentic_cli-0.5.2/src/agentic_cli/workflow/permissions/prompt.py +66 -0
  45. agentic_cli-0.5.2/src/agentic_cli/workflow/permissions/rules.py +39 -0
  46. agentic_cli-0.5.2/src/agentic_cli/workflow/permissions/store.py +114 -0
  47. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/service_registry.py +4 -3
  48. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/settings.py +26 -6
  49. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/tool_summaries.py +3 -1
  50. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/integration/test_kb_research_flow.py +2 -2
  51. agentic_cli-0.5.2/tests/integration/test_permission_adk.py +141 -0
  52. agentic_cli-0.5.2/tests/integration/test_permission_langgraph.py +110 -0
  53. agentic_cli-0.5.2/tests/permissions/test_capabilities.py +47 -0
  54. agentic_cli-0.5.2/tests/permissions/test_engine.py +435 -0
  55. agentic_cli-0.5.2/tests/permissions/test_matchers.py +175 -0
  56. agentic_cli-0.5.2/tests/permissions/test_prompt.py +92 -0
  57. agentic_cli-0.5.2/tests/permissions/test_rules.py +47 -0
  58. agentic_cli-0.5.2/tests/permissions/test_store.py +179 -0
  59. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_adk_plugins.py +4 -228
  60. agentic_cli-0.5.2/tests/test_bm25_index.py +188 -0
  61. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_concept_store.py +37 -0
  62. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_config.py +0 -4
  63. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_kb_sidecar.py +75 -4
  64. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_kb_tool_renames.py +25 -5
  65. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_knowledge_tools.py +77 -24
  66. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_memory.py +83 -0
  67. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_tool_summaries.py +5 -2
  68. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_tools.py +34 -89
  69. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_webfetch.py +102 -14
  70. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/tools/test_factories.py +6 -4
  71. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/tools/test_os_sandbox.py +54 -18
  72. agentic_cli-0.5.2/tests/tools/test_registry.py +66 -0
  73. agentic_cli-0.5.2/tests/workflow/__init__.py +0 -0
  74. agentic_cli-0.5.2/tests/workflow/test_backend_isolation.py +56 -0
  75. agentic_cli-0.5.2/tests/workflow/test_base_manager_permissions.py +27 -0
  76. agentic_cli-0.5.2/tests/workflow/test_settings_permissions.py +37 -0
  77. agentic_cli-0.5.0/src/agentic_cli/tools/hitl_tools.py +0 -81
  78. agentic_cli-0.5.0/src/agentic_cli/tools/webfetch/fetcher.py +0 -203
  79. agentic_cli-0.5.0/tests/test_bm25_index.py +0 -67
  80. agentic_cli-0.5.0/tests/test_hitl.py +0 -61
  81. agentic_cli-0.5.0/tests/test_langgraph_confirmation.py +0 -134
  82. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/.gitignore +0 -0
  83. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/LICENSE +0 -0
  84. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/environment.yml +0 -0
  85. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/arxiv_demo.py +0 -0
  86. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/fileops_demo.py +0 -0
  87. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/hello_agent.py +0 -0
  88. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/hello_langgraph.py +0 -0
  89. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/memory_demo.py +0 -0
  90. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/research_demo/__init__.py +0 -0
  91. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/research_demo/__main__.py +0 -0
  92. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/research_demo/app.py +0 -0
  93. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/research_demo/commands.py +0 -0
  94. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/research_demo/settings.py +0 -0
  95. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/shell_demo.py +0 -0
  96. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/webfetch_demo.py +0 -0
  97. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/examples/websearch_demo.py +0 -0
  98. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/requirements-dev.txt +0 -0
  99. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/requirements.txt +0 -0
  100. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/cli/__init__.py +0 -0
  101. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/cli/app.py +0 -0
  102. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/cli/builtin_commands.py +0 -0
  103. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/cli/commands.py +0 -0
  104. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/cli/message_processor.py +0 -0
  105. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/cli/settings_command.py +0 -0
  106. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/cli/settings_introspection.py +0 -0
  107. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/cli/usage_tracker.py +0 -0
  108. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/cli/workflow_controller.py +0 -0
  109. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/config.py +0 -0
  110. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/constants.py +0 -0
  111. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/file_utils.py +0 -0
  112. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/knowledge_base/__init__.py +0 -0
  113. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/knowledge_base/_mock_bm25.py +0 -0
  114. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/knowledge_base/_mocks.py +0 -0
  115. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/knowledge_base/bm25_index.py +0 -0
  116. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/knowledge_base/embeddings.py +0 -0
  117. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/knowledge_base/models.py +0 -0
  118. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/knowledge_base/sidecar.py +0 -0
  119. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/knowledge_base/sources.py +0 -0
  120. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/knowledge_base/vector_store.py +0 -0
  121. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/logging.py +0 -0
  122. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/persistence/__init__.py +0 -0
  123. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/persistence/session.py +0 -0
  124. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/settings_mixins.py +0 -0
  125. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/settings_persistence.py +0 -0
  126. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/_core/__init__.py +0 -0
  127. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/_core/planning.py +0 -0
  128. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/_core/tasks.py +0 -0
  129. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/adk/__init__.py +0 -0
  130. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/arxiv_source.py +0 -0
  131. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/langgraph/__init__.py +0 -0
  132. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/pdf_utils.py +0 -0
  133. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/sandbox/backends/__init__.py +0 -0
  134. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/sandbox/backends/base.py +0 -0
  135. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/sandbox/backends/jupyter_local.py +0 -0
  136. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/sandbox/manager.py +0 -0
  137. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/sandbox/models.py +0 -0
  138. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/__init__.py +0 -0
  139. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/audit.py +0 -0
  140. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/classifier.py +0 -0
  141. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/config.py +0 -0
  142. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/models.py +0 -0
  143. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/os_sandbox/__init__.py +0 -0
  144. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/os_sandbox/base.py +0 -0
  145. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/os_sandbox/bubblewrap.py +0 -0
  146. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/os_sandbox/detect.py +0 -0
  147. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/os_sandbox/policy.py +0 -0
  148. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/os_sandbox/seatbelt.py +0 -0
  149. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/os_sandbox/seatbelt_profile.py +0 -0
  150. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/path_analyzer.py +0 -0
  151. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/preprocessor.py +0 -0
  152. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/risk_assessor.py +0 -0
  153. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/shell/tokenizer.py +0 -0
  154. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/webfetch/__init__.py +0 -0
  155. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/webfetch/converter.py +0 -0
  156. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/webfetch/robots.py +0 -0
  157. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/webfetch/summarizer.py +0 -0
  158. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/tools/webfetch/validator.py +0 -0
  159. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/__init__.py +0 -0
  160. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/adk/event_processor.py +0 -0
  161. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/adk/task_progress_plugin.py +0 -0
  162. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/config.py +0 -0
  163. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/events.py +0 -0
  164. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/factory.py +0 -0
  165. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/langgraph/__init__.py +0 -0
  166. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/langgraph/manager.py +0 -0
  167. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/langgraph/persistence/__init__.py +0 -0
  168. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/langgraph/persistence/checkpointers.py +0 -0
  169. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/langgraph/persistence/stores.py +0 -0
  170. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/langgraph/state.py +0 -0
  171. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/models.py +0 -0
  172. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/src/agentic_cli/workflow/retry.py +0 -0
  173. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/__init__.py +0 -0
  174. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/conftest.py +0 -0
  175. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/integration/__init__.py +0 -0
  176. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/integration/conftest.py +0 -0
  177. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/integration/helpers.py +0 -0
  178. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/integration/test_adk_integration.py +0 -0
  179. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/integration/test_langgraph_integration.py +0 -0
  180. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/integration/test_live.py +0 -0
  181. {agentic_cli-0.5.0/tests/workflow → agentic_cli-0.5.2/tests/permissions}/__init__.py +0 -0
  182. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_adk_event_processor.py +0 -0
  183. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_arxiv_tools.py +0 -0
  184. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_commands.py +0 -0
  185. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_concurrent_stores.py +0 -0
  186. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_context_trimming.py +0 -0
  187. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_context_window.py +0 -0
  188. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_dual_thinking_boxes.py +0 -0
  189. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_embeddings.py +0 -0
  190. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_grep_tool_cache.py +0 -0
  191. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_input_callback.py +0 -0
  192. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_kb_concepts.py +0 -0
  193. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_kb_helpers.py +0 -0
  194. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_kb_index_md.py +0 -0
  195. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_kb_ingest_log.py +0 -0
  196. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_knowledge_base.py +0 -0
  197. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_langgraph.py +0 -0
  198. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_model_registry.py +0 -0
  199. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_persistence.py +0 -0
  200. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_planning.py +0 -0
  201. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_reflection.py +0 -0
  202. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_session_save_resume.py +0 -0
  203. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_settings_persistence.py +0 -0
  204. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_task_tools.py +0 -0
  205. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_token_caching.py +0 -0
  206. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_update_setting.py +0 -0
  207. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_usage_tracker.py +0 -0
  208. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_vector_store.py +0 -0
  209. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_workflow.py +0 -0
  210. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/test_workflow_controller.py +0 -0
  211. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/tools/__init__.py +0 -0
  212. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/tools/test_adk_state_tools.py +0 -0
  213. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/tools/test_bubblewrap.py +0 -0
  214. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/tools/test_core_planning.py +0 -0
  215. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/tools/test_core_tasks.py +0 -0
  216. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/tools/test_langgraph_state_tools.py +0 -0
  217. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/tools/test_sandbox.py +0 -0
  218. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/tools/test_seatbelt_profile.py +0 -0
  219. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/tools/test_shell_security.py +0 -0
  220. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/workflow/test_task_progress_plugin.py +0 -0
  221. {agentic_cli-0.5.0 → agentic_cli-0.5.2}/tests/workflow/test_tool_assembly.py +0 -0
@@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.2] - 2026-05-01
9
+
10
+ ### Security
11
+ - **`kb_ingest` split into `kb_ingest_text` / `kb_ingest_file` / `kb_ingest_url`**: the previous unified tool declared only `kb.write` (auto-allowed by the builtin `kb.*` rule) while internally reading arbitrary local paths and fetching arbitrary URLs. The split lets the permission engine gate `filesystem.read` and `http.read` at the right entry point, and the URL path now flows through the hardened `ContentFetcher` (URLValidator + manual redirect revalidation + DNS-rebinding mitigation).
12
+ - **Concept-page slug traversal blocked** (`ConceptStore`): explicit slugs are validated against a strict `[a-z0-9-]+` allowlist, and `_concept_path` asserts the resolved path stays under `base_dir`. A slug like `../../etc/passwd` is now rejected before any filesystem write.
13
+ - **Webfetch redirect handling rewritten**: `ContentFetcher.fetch` no longer follows redirects via httpx. Each `Location` is revalidated through `URLValidator` *before* the next GET, cross-host redirects are reported without contacting the new origin, same-host redirects re-check `robots.txt`, and the redirect chain is capped at 5.
14
+ - **`os_sandbox_enabled=True` now fails closed**: when shell or Python execution is requested with sandboxing on, both executors refuse to run if the resolved sandbox is `NoOpSandbox` or wrap fails — instead of silently dropping back to plain subprocess/ulimit.
15
+
16
+ ### Changed
17
+ - **`kb_ingest` tool removed** in favor of the three split entry points; `KB_WRITER_TOOLS` and `make_kb_tools` updated. Internal helper renamed: `_ingest_document_with_kb` → `_ingest_text_with_kb` / `_ingest_file_with_kb` / `_ingest_url_with_kb`.
18
+ - **research_demo agent guide** updated to direct ingestion at `ingest_arxiv_paper` for arXiv papers and at the typed `kb_ingest_*` tools for everything else.
19
+
20
+ ## [0.5.1] - 2026-04-18
21
+
22
+ ### Added
23
+ - **Capability-based permission engine** (PR #72): Framework-independent engine that replaces the ConfirmationPlugin-based HITL. Tools declare capabilities (e.g. `filesystem.write(path=...)`, `http.read`, `shell.exec`) via `@register_tool(capabilities=...)`. Rules are evaluated from four sources — builtin defaults, user `~/.{app_name}/settings.json`, project `./.{app_name}/settings.json`, and in-memory session. When no rule matches, the user is prompted with `Allow once / for session / always (save to project) / Deny`; "always" grants persist into project settings.
24
+ - **Matchers**: `PathMatcher` (with `**` glob), `URLMatcher`, `ShellMatcher`, and `StringGlobMatcher`.
25
+ - **ADK `PermissionPlugin`**: capability gating for ADK agents.
26
+ - **LangGraph `wrap_tool_for_permission`**: per-tool permission wrapping for LangGraph `ToolNode`.
27
+ - **`permissions` and `permissions_enabled` settings** for runtime control.
28
+ - **`EXEMPT` sentinel**: opts a tool out of the permission engine (used for backend state tools and ADK `transfer_to_agent`).
29
+ - **Real BM25 backends**: `bm25s` (preferred) and `rank_bm25` fallback.
30
+
31
+ ### Changed
32
+ - **`@register_tool`** now requires the `capabilities=` kwarg.
33
+ - **Default grants broadened**: `filesystem.*` auto-extends to the parent directory; `memory.*` and `kb.*` are allowed by default.
34
+ - **Ask prompt** now shows the directory-scope target.
35
+ - **Workflow init**: `_ensure_managers_initialized` runs in a worker thread to avoid blocking the event loop.
36
+ - **HITL confirmation** extracted into a backend-neutral module during the permissions migration.
37
+ - **Memory tools**: deduped registry-bound and factory-bound entry points.
38
+
39
+ ### Fixed
40
+ - Matcher canonicalize/matches preserves the `*` wildcard.
41
+ - `KnowledgeBaseManager` concurrency contract tightened.
42
+
43
+ ### Removed
44
+ - **`PermissionLevel`** (SAFE / CAUTION / DANGEROUS), `ConfirmationPlugin`, `_wrap_for_confirmation`, `hitl_tools`, and the `hitl_enabled` setting — superseded by the permission engine.
45
+
8
46
  ## [0.5.0] - 2026-04-17
9
47
 
10
48
  ### Added
@@ -47,7 +47,7 @@ agentic-cli/
47
47
  │ │ ├── persistence/ # Checkpointers, stores
48
48
  │ │ └── tools/ # LangChain-compatible wrappers
49
49
  │ ├── tools/
50
- │ │ ├── registry.py # ToolRegistry, @register_tool, ToolCategory, PermissionLevel
50
+ │ │ ├── registry.py # ToolRegistry, @register_tool, ToolCategory
51
51
  │ │ ├── executor.py # SafePythonExecutor
52
52
  │ │ ├── knowledge_tools.py # kb_search, kb_ingest, kb_list, kb_read
53
53
  │ │ ├── arxiv_tools.py # search_arxiv, fetch_arxiv_paper, analyze_arxiv_paper
@@ -62,7 +62,7 @@ agentic-cli/
62
62
  │ │ ├── memory_tools.py # save_memory, search_memory + MemoryStore
63
63
  │ │ ├── planning_tools.py # save_plan, get_plan + PlanStore
64
64
  │ │ ├── task_tools.py # save_tasks, get_tasks + TaskStore
65
- │ │ ├── hitl_tools.py # request_approval + ApprovalManager, HITLConfig
65
+ │ │ ├── reflection_tools.py # save_reflection + ToolReflectionStore
66
66
  │ │ ├── shell/ # 8-layer shell security
67
67
  │ │ └── webfetch/ # Fetcher, converter, validator, robots
68
68
  │ ├── knowledge_base/
@@ -114,6 +114,9 @@ Workflow:
114
114
  2. For features: create `feature/<name>` (or `fix/<name>` or `refactor/<name>`) from `develop`, work there, merge back to `develop`
115
115
  3. When ready to release: merge `develop` → `main` and tag the release
116
116
 
117
+ ### What NOT to commit
118
+ - `docs/` is gitignored on purpose (see `.gitignore`). It is a scratchpad for review notes, plans, and internal analysis. **Never `git add docs/…` or suggest committing anything under `docs/`.** If a document belongs in the repo, it lives elsewhere (README, CHANGELOG, top-level `*.md`).
119
+
117
120
  ## Development Principles
118
121
 
119
122
  ### Code Style
@@ -130,7 +133,8 @@ Workflow:
130
133
 
131
134
  ### Key Design Patterns
132
135
  - **Tool error handling**: All tools return `{"success": bool, ...}` dicts. Never raise `ToolError`.
133
- - **Tool registration**: Use `@register_tool(category=..., permission_level=..., description=...)` decorator. Tools are auto-discovered via the global `ToolRegistry`.
136
+ - **Tool registration**: Use `@register_tool(category=..., capabilities=..., description=...)` decorator. `capabilities=` is required — pass `EXEMPT` for tools that need no permission check or a list of `Capability(name, target_arg=...)` tuples the engine matches against rules. Tools are auto-discovered via the global `ToolRegistry`.
137
+ - **Permissions**: `workflow/permissions/` holds a framework-independent engine that evaluates declared capabilities against rules from four sources (builtin, user `~/.{app_name}/settings.json`, project `./.{app_name}/settings.json`, in-memory session). ADK + LangGraph gate tool calls via `workflow/adk/permission_plugin.py::PermissionPlugin` and `workflow/langgraph/permission_wrap.py::wrap_tool_for_permission`. See `docs/superpowers/specs/2026-04-18-permissions-system-design.md`.
134
138
  - **Service registry**: Tools access services and shared state via `get_service(key)` from `workflow.service_registry`. A single ContextVar holds a `dict[str, Any]` set by the workflow manager during processing. Complex services (KBManager, SandboxManager, MemoryStore) are lazily created; simple state (plan string, task list) lives directly in the registry dict.
135
139
  - **Manager detection**: Tools decorated with `@requires("kb_manager")` etc. are scanned by `BaseWorkflowManager._detect_required_managers()` which lazily creates only the needed services.
136
140
  - **Atomic writes**: Use `atomic_write_json`/`atomic_write_text` from `persistence/_utils.py` for file persistence.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-cli
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: A framework for building domain-specific agentic CLI applications
5
5
  Project-URL: Homepage, https://github.com/shoom1/agentic-cli
6
6
  Project-URL: Repository, https://github.com/shoom1/agentic-cli
@@ -32,7 +32,9 @@ Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
32
32
  Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
33
33
  Requires-Dist: pytest>=8.0.0; extra == 'dev'
34
34
  Provides-Extra: kb
35
+ Requires-Dist: bm25s>=0.2.0; extra == 'kb'
35
36
  Requires-Dist: faiss-cpu>=1.7.0; extra == 'kb'
37
+ Requires-Dist: rank-bm25>=0.2.2; extra == 'kb'
36
38
  Requires-Dist: sentence-transformers<3.0.0,>=2.0.0; extra == 'kb'
37
39
  Requires-Dist: torch>=2.2.0; extra == 'kb'
38
40
  Provides-Extra: langgraph
@@ -84,7 +86,7 @@ Agentic CLI provides the core infrastructure for building interactive CLI applic
84
86
  ├─────────────────────────────┬───────────────────────────────────────┤
85
87
  │ GoogleADKWorkflowManager │ LangGraphWorkflowManager │
86
88
  │ (default) │ (optional: langgraph extra) │
87
- │ + ConfirmationPlugin │ + confirmation tool wrapper │
89
+ │ + PermissionPlugin │ + wrap_tool_for_permission │
88
90
  │ + LLMLoggingPlugin │ + native ToolNode │
89
91
  │ + TaskProgressPlugin │ │
90
92
  └─────────────────────────────┴───────────────────────────────────────┘
@@ -191,7 +193,7 @@ manager = GoogleADKWorkflowManager(
191
193
  ```
192
194
 
193
195
  ADK integrations:
194
- - **ConfirmationPlugin** — intercepts `DANGEROUS` tools and prompts the user for approval
196
+ - **PermissionPlugin** — evaluates each tool's declared capabilities against the permission engine and prompts the user when no rule matches
195
197
  - **LLMLoggingPlugin** — structured logging of LLM requests/responses
196
198
  - **TaskProgressPlugin** — streams the task checklist into its own thinking box
197
199
 
@@ -217,7 +219,7 @@ Features:
217
219
  - **Explicit provider support**: Uses `langchain-google-genai` for Gemini (not VertexAI)
218
220
  - **Thinking mode**: Native support for Claude and Gemini thinking/reasoning
219
221
  - **Retry policies**: Automatic retry with exponential backoff
220
- - **Confirmation wrapper**: Wraps `DANGEROUS` tools to request HITL approval
222
+ - **Permission wrapper**: Wraps each tool to gate execution through the permission engine
221
223
  - **Event streaming**: Real-time workflow events via `WorkflowEvent`
222
224
 
223
225
  Requires: `pip install agentic-cli[langgraph]`
@@ -232,7 +234,7 @@ Requires: `pip install agentic-cli[langgraph]`
232
234
  | State persistence | In-memory | Memory, PostgreSQL, or SQLite |
233
235
  | Thinking support | Native (Gemini) | Native (Claude & Gemini) |
234
236
  | Retry handling | Built-in | Built-in with backoff |
235
- | HITL confirmation | ConfirmationPlugin | Tool wrapper |
237
+ | Permission gate | PermissionPlugin | wrap_tool_for_permission |
236
238
  | Context trimming | Native | Native |
237
239
 
238
240
  ### Auto-selection via Settings
@@ -366,11 +368,12 @@ configs = [coordinator, researcher, analyst]
366
368
  Tools are regular Python functions with type hints and docstrings. **All tools return `{"success": bool, ...}` dicts** — never raise exceptions.
367
369
 
368
370
  ```python
369
- from agentic_cli.tools import register_tool, ToolCategory, PermissionLevel
371
+ from agentic_cli.tools import register_tool, ToolCategory
372
+ from agentic_cli.workflow.permissions import Capability, EXEMPT
370
373
 
371
374
  @register_tool(
372
- category=ToolCategory.DATA,
373
- permission_level=PermissionLevel.SAFE,
375
+ category=ToolCategory.NETWORK,
376
+ capabilities=[Capability("http.read")],
374
377
  description="Search the database for matching records.",
375
378
  )
376
379
  def search_database(query: str, limit: int = 10) -> dict:
@@ -387,15 +390,14 @@ def search_database(query: str, limit: int = 10) -> dict:
387
390
  return {"success": True, "results": results, "count": len(results)}
388
391
  ```
389
392
 
390
- Registering via `@register_tool` is optional — you can pass raw callables into `AgentConfig.tools`. Registering gives the tool metadata for the registry, permission-aware HITL wrapping, and tool-summary formatting.
393
+ Registering via `@register_tool` is optional — you can pass raw callables into `AgentConfig.tools`. Registering gives the tool metadata for the registry, capability-aware permission gating, and tool-summary formatting.
391
394
 
392
- ### Permission Levels
395
+ ### Capabilities
393
396
 
394
- | Level | Behavior |
395
- |-------|----------|
396
- | `SAFE` | Runs silently |
397
- | `CAUTION` | Runs; result is surfaced prominently |
398
- | `DANGEROUS` | Intercepted by HITL — user must approve before execution |
397
+ Tool access is gated by the **permission engine** (see the HITL section below). Each registered tool declares what it touches via `capabilities=`:
398
+
399
+ - **`Capability("namespace.action", target_arg="...")`** — e.g. `Capability("filesystem.write", target_arg="path")`, `Capability("http.read", target_arg="url")`, `Capability("shell.exec", target_arg="command")`. The engine resolves `target_arg` against the actual tool-call arguments and matches against rules.
400
+ - **`EXEMPT`** — opts the tool out of the engine entirely. Reserved for backend-internal tools (e.g. ADK `transfer_to_agent`, backend state tools).
399
401
 
400
402
  ### Built-in Tools
401
403
 
@@ -458,7 +460,7 @@ results = search_arxiv("transformer attention", max_results=10, categories=["cs.
458
460
  paper = fetch_arxiv_paper("1706.03762") # "Attention Is All You Need"
459
461
  ```
460
462
 
461
- The `ArxivSearchSource` (in `knowledge_base/sources.py`) is cached via the service registry and reused across tools, including `kb_ingest` for arXiv URLs.
463
+ The `ArxivSearchSource` (in `knowledge_base/sources.py`) is cached via the service registry and reused across tools. To pull a paper into the KB, use `ingest_arxiv_paper(arxiv_id)` — it composes the arxiv source with `kb_manager`.
462
464
 
463
465
  #### File Operations
464
466
 
@@ -476,7 +478,7 @@ list_dir("src/", include_hidden=False)
476
478
  diff_compare(source_a="old.txt", source_b="new.txt")
477
479
  ```
478
480
 
479
- **WRITE tools (caution)**
481
+ **WRITE tools**
480
482
 
481
483
  ```python
482
484
  from agentic_cli.tools import write_file, edit_file
@@ -511,7 +513,8 @@ Renamed tools and a new concept-pages subsystem:
511
513
 
512
514
  ```python
513
515
  from agentic_cli.tools import (
514
- kb_search, kb_ingest, kb_read, kb_list,
516
+ kb_search, kb_ingest_text, kb_ingest_file, kb_ingest_url,
517
+ kb_read, kb_list,
515
518
  kb_write_concept, kb_search_concepts,
516
519
  KB_READER_TOOLS, KB_WRITER_TOOLS,
517
520
  )
@@ -520,12 +523,16 @@ from agentic_cli.tools import (
520
523
  | Tool | Purpose |
521
524
  |------|---------|
522
525
  | `kb_search` | Hybrid BM25 + vector search with RRF fusion, filters by source/date |
523
- | `kb_ingest` | Ingest content or URL (arXiv URLs auto-fetch metadata + PDF) |
526
+ | `kb_ingest_text` | Ingest in-memory text content (no FS or network access) |
527
+ | `kb_ingest_file` | Ingest a local file; declares `filesystem.read(path)` for the permission engine |
528
+ | `kb_ingest_url` | Ingest content from an http(s) URL; routed through the hardened `ContentFetcher` and declares `http.read(url)` |
524
529
  | `kb_read` | Return the per-document markdown sidecar (lazy-generated on first read) |
525
530
  | `kb_list` | List documents, optionally filtered |
526
531
  | `kb_write_concept` | Agent-authored concept/summary page (slugged, merged on overwrite) |
527
532
  | `kb_search_concepts` | Search concept pages (title-weighted, case-insensitive) |
528
533
 
534
+ The single `kb_ingest` tool was split in 0.5.2 so each entry point declares the right capability — text-only stays under `kb.write`, while file and URL ingestion correctly trip `filesystem.read` / `http.read` checks. For arXiv papers, prefer `ingest_arxiv_paper(arxiv_id)`.
535
+
529
536
  Bundle convenience:
530
537
 
531
538
  ```python
@@ -580,15 +587,9 @@ Each tool keeps at most N reflections (FIFO eviction). Reflections can be inject
580
587
 
581
588
  #### HITL (Human-in-the-Loop)
582
589
 
583
- Two mechanisms:
584
-
585
- 1. **Automatic confirmation for `DANGEROUS` tools** — handled by ADK's `ConfirmationPlugin` and LangGraph's tool wrapper. No code changes needed; just mark the tool `DANGEROUS`.
586
- 2. **Explicit `request_approval` tool** — for domain-level checkpoints:
590
+ Tool calls are gated by the **permission engine** (`workflow/permissions/`). Each tool declares a list of capabilities (e.g. `filesystem.write(path=...)`); the engine evaluates them against rules from four sources (builtin defaults, user `~/.{app_name}/settings.json`, project `./.{app_name}/settings.json`, in-memory session). When no rule matches, the user is prompted with `Allow once / Allow for session / Allow always (save to project) / Deny`. Always-grants persist into the project settings file so the next run picks them up automatically.
587
591
 
588
- ```python
589
- from agentic_cli.tools import hitl_tools
590
- # request_approval(message, options) -> {"success": True, "choice": "..."}
591
- ```
592
+ See `docs/superpowers/specs/2026-04-18-permissions-system-design.md` for the full design.
592
593
 
593
594
  ## CLI Commands
594
595
 
@@ -764,22 +765,32 @@ agentic-cli/
764
765
  │ │ ├── adk/
765
766
  │ │ │ ├── manager.py # GoogleADKWorkflowManager
766
767
  │ │ │ ├── event_processor.py
767
- │ │ │ ├── plugins.py # ConfirmationPlugin, LLMLoggingPlugin
768
+ │ │ │ ├── plugins.py # LLMLoggingPlugin
769
+ │ │ │ ├── permission_plugin.py # PermissionPlugin (capability gating)
768
770
  │ │ │ └── task_progress_plugin.py
769
- │ │ └── langgraph/
770
- │ │ ├── manager.py # LangGraphWorkflowManager
771
- │ │ ├── graph_builder.py
772
- │ │ ├── state.py
773
- │ │ └── persistence/ # Checkpointers and stores
771
+ │ │ ├── langgraph/
772
+ │ │ │ ├── manager.py # LangGraphWorkflowManager
773
+ │ │ │ ├── graph_builder.py
774
+ │ │ │ ├── permission_wrap.py # wrap_tool_for_permission
775
+ │ │ │ ├── state.py
776
+ │ │ │ └── persistence/ # Checkpointers and stores
777
+ │ │ └── permissions/ # Framework-independent permission engine
778
+ │ │ ├── capabilities.py # Capability, ResolvedCapability, EXEMPT
779
+ │ │ ├── rules.py # Rule, Effect, RuleSource, CheckResult, AskScope
780
+ │ │ ├── matchers.py # Path/URL/Shell/StringGlob matchers
781
+ │ │ ├── store.py # PermissionContext, BUILTIN_RULES, JSON load/save
782
+ │ │ ├── prompt.py # build_request + parse_response
783
+ │ │ └── engine.py # PermissionEngine
774
784
  │ ├── tools/
775
- │ │ ├── registry.py # ToolRegistry, ToolCategory, PermissionLevel
785
+ │ │ ├── registry.py # ToolRegistry, ToolCategory, register_tool
776
786
  │ │ ├── factories.py # Backend-aware tool factories
777
787
  │ │ ├── executor.py # SafePythonExecutor
778
788
  │ │ ├── arxiv_tools.py # search_arxiv, fetch_arxiv_paper, ingest_arxiv_paper
779
789
  │ │ ├── arxiv_source.py # ArxivSearchSource (service-registered)
780
790
  │ │ ├── execution_tools.py # execute_python
781
791
  │ │ ├── interaction_tools.py # ask_clarification
782
- │ │ ├── knowledge_tools.py # kb_search / kb_ingest / kb_read / kb_list /
792
+ │ │ ├── knowledge_tools.py # kb_search / kb_ingest_text / kb_ingest_file /
793
+ │ │ │ # kb_ingest_url / kb_read / kb_list /
783
794
  │ │ │ # kb_write_concept / kb_search_concepts
784
795
  │ │ ├── file_read.py # read_file, diff_compare
785
796
  │ │ ├── file_write.py # write_file, edit_file (atomic)
@@ -790,7 +801,6 @@ agentic-cli/
790
801
  │ │ ├── pdf_utils.py # PDF text extraction helpers
791
802
  │ │ ├── memory_tools.py # save/search/update/delete + MemoryStore
792
803
  │ │ ├── reflection_tools.py # save_reflection + ToolReflectionStore
793
- │ │ ├── hitl_tools.py # request_approval
794
804
  │ │ ├── _core/ # Shared planning/task logic
795
805
  │ │ │ ├── planning.py
796
806
  │ │ │ └── tasks.py
@@ -38,7 +38,7 @@ Agentic CLI provides the core infrastructure for building interactive CLI applic
38
38
  ├─────────────────────────────┬───────────────────────────────────────┤
39
39
  │ GoogleADKWorkflowManager │ LangGraphWorkflowManager │
40
40
  │ (default) │ (optional: langgraph extra) │
41
- │ + ConfirmationPlugin │ + confirmation tool wrapper │
41
+ │ + PermissionPlugin │ + wrap_tool_for_permission │
42
42
  │ + LLMLoggingPlugin │ + native ToolNode │
43
43
  │ + TaskProgressPlugin │ │
44
44
  └─────────────────────────────┴───────────────────────────────────────┘
@@ -145,7 +145,7 @@ manager = GoogleADKWorkflowManager(
145
145
  ```
146
146
 
147
147
  ADK integrations:
148
- - **ConfirmationPlugin** — intercepts `DANGEROUS` tools and prompts the user for approval
148
+ - **PermissionPlugin** — evaluates each tool's declared capabilities against the permission engine and prompts the user when no rule matches
149
149
  - **LLMLoggingPlugin** — structured logging of LLM requests/responses
150
150
  - **TaskProgressPlugin** — streams the task checklist into its own thinking box
151
151
 
@@ -171,7 +171,7 @@ Features:
171
171
  - **Explicit provider support**: Uses `langchain-google-genai` for Gemini (not VertexAI)
172
172
  - **Thinking mode**: Native support for Claude and Gemini thinking/reasoning
173
173
  - **Retry policies**: Automatic retry with exponential backoff
174
- - **Confirmation wrapper**: Wraps `DANGEROUS` tools to request HITL approval
174
+ - **Permission wrapper**: Wraps each tool to gate execution through the permission engine
175
175
  - **Event streaming**: Real-time workflow events via `WorkflowEvent`
176
176
 
177
177
  Requires: `pip install agentic-cli[langgraph]`
@@ -186,7 +186,7 @@ Requires: `pip install agentic-cli[langgraph]`
186
186
  | State persistence | In-memory | Memory, PostgreSQL, or SQLite |
187
187
  | Thinking support | Native (Gemini) | Native (Claude & Gemini) |
188
188
  | Retry handling | Built-in | Built-in with backoff |
189
- | HITL confirmation | ConfirmationPlugin | Tool wrapper |
189
+ | Permission gate | PermissionPlugin | wrap_tool_for_permission |
190
190
  | Context trimming | Native | Native |
191
191
 
192
192
  ### Auto-selection via Settings
@@ -320,11 +320,12 @@ configs = [coordinator, researcher, analyst]
320
320
  Tools are regular Python functions with type hints and docstrings. **All tools return `{"success": bool, ...}` dicts** — never raise exceptions.
321
321
 
322
322
  ```python
323
- from agentic_cli.tools import register_tool, ToolCategory, PermissionLevel
323
+ from agentic_cli.tools import register_tool, ToolCategory
324
+ from agentic_cli.workflow.permissions import Capability, EXEMPT
324
325
 
325
326
  @register_tool(
326
- category=ToolCategory.DATA,
327
- permission_level=PermissionLevel.SAFE,
327
+ category=ToolCategory.NETWORK,
328
+ capabilities=[Capability("http.read")],
328
329
  description="Search the database for matching records.",
329
330
  )
330
331
  def search_database(query: str, limit: int = 10) -> dict:
@@ -341,15 +342,14 @@ def search_database(query: str, limit: int = 10) -> dict:
341
342
  return {"success": True, "results": results, "count": len(results)}
342
343
  ```
343
344
 
344
- Registering via `@register_tool` is optional — you can pass raw callables into `AgentConfig.tools`. Registering gives the tool metadata for the registry, permission-aware HITL wrapping, and tool-summary formatting.
345
+ Registering via `@register_tool` is optional — you can pass raw callables into `AgentConfig.tools`. Registering gives the tool metadata for the registry, capability-aware permission gating, and tool-summary formatting.
345
346
 
346
- ### Permission Levels
347
+ ### Capabilities
347
348
 
348
- | Level | Behavior |
349
- |-------|----------|
350
- | `SAFE` | Runs silently |
351
- | `CAUTION` | Runs; result is surfaced prominently |
352
- | `DANGEROUS` | Intercepted by HITL — user must approve before execution |
349
+ Tool access is gated by the **permission engine** (see the HITL section below). Each registered tool declares what it touches via `capabilities=`:
350
+
351
+ - **`Capability("namespace.action", target_arg="...")`** — e.g. `Capability("filesystem.write", target_arg="path")`, `Capability("http.read", target_arg="url")`, `Capability("shell.exec", target_arg="command")`. The engine resolves `target_arg` against the actual tool-call arguments and matches against rules.
352
+ - **`EXEMPT`** — opts the tool out of the engine entirely. Reserved for backend-internal tools (e.g. ADK `transfer_to_agent`, backend state tools).
353
353
 
354
354
  ### Built-in Tools
355
355
 
@@ -412,7 +412,7 @@ results = search_arxiv("transformer attention", max_results=10, categories=["cs.
412
412
  paper = fetch_arxiv_paper("1706.03762") # "Attention Is All You Need"
413
413
  ```
414
414
 
415
- The `ArxivSearchSource` (in `knowledge_base/sources.py`) is cached via the service registry and reused across tools, including `kb_ingest` for arXiv URLs.
415
+ The `ArxivSearchSource` (in `knowledge_base/sources.py`) is cached via the service registry and reused across tools. To pull a paper into the KB, use `ingest_arxiv_paper(arxiv_id)` — it composes the arxiv source with `kb_manager`.
416
416
 
417
417
  #### File Operations
418
418
 
@@ -430,7 +430,7 @@ list_dir("src/", include_hidden=False)
430
430
  diff_compare(source_a="old.txt", source_b="new.txt")
431
431
  ```
432
432
 
433
- **WRITE tools (caution)**
433
+ **WRITE tools**
434
434
 
435
435
  ```python
436
436
  from agentic_cli.tools import write_file, edit_file
@@ -465,7 +465,8 @@ Renamed tools and a new concept-pages subsystem:
465
465
 
466
466
  ```python
467
467
  from agentic_cli.tools import (
468
- kb_search, kb_ingest, kb_read, kb_list,
468
+ kb_search, kb_ingest_text, kb_ingest_file, kb_ingest_url,
469
+ kb_read, kb_list,
469
470
  kb_write_concept, kb_search_concepts,
470
471
  KB_READER_TOOLS, KB_WRITER_TOOLS,
471
472
  )
@@ -474,12 +475,16 @@ from agentic_cli.tools import (
474
475
  | Tool | Purpose |
475
476
  |------|---------|
476
477
  | `kb_search` | Hybrid BM25 + vector search with RRF fusion, filters by source/date |
477
- | `kb_ingest` | Ingest content or URL (arXiv URLs auto-fetch metadata + PDF) |
478
+ | `kb_ingest_text` | Ingest in-memory text content (no FS or network access) |
479
+ | `kb_ingest_file` | Ingest a local file; declares `filesystem.read(path)` for the permission engine |
480
+ | `kb_ingest_url` | Ingest content from an http(s) URL; routed through the hardened `ContentFetcher` and declares `http.read(url)` |
478
481
  | `kb_read` | Return the per-document markdown sidecar (lazy-generated on first read) |
479
482
  | `kb_list` | List documents, optionally filtered |
480
483
  | `kb_write_concept` | Agent-authored concept/summary page (slugged, merged on overwrite) |
481
484
  | `kb_search_concepts` | Search concept pages (title-weighted, case-insensitive) |
482
485
 
486
+ The single `kb_ingest` tool was split in 0.5.2 so each entry point declares the right capability — text-only stays under `kb.write`, while file and URL ingestion correctly trip `filesystem.read` / `http.read` checks. For arXiv papers, prefer `ingest_arxiv_paper(arxiv_id)`.
487
+
483
488
  Bundle convenience:
484
489
 
485
490
  ```python
@@ -534,15 +539,9 @@ Each tool keeps at most N reflections (FIFO eviction). Reflections can be inject
534
539
 
535
540
  #### HITL (Human-in-the-Loop)
536
541
 
537
- Two mechanisms:
538
-
539
- 1. **Automatic confirmation for `DANGEROUS` tools** — handled by ADK's `ConfirmationPlugin` and LangGraph's tool wrapper. No code changes needed; just mark the tool `DANGEROUS`.
540
- 2. **Explicit `request_approval` tool** — for domain-level checkpoints:
542
+ Tool calls are gated by the **permission engine** (`workflow/permissions/`). Each tool declares a list of capabilities (e.g. `filesystem.write(path=...)`); the engine evaluates them against rules from four sources (builtin defaults, user `~/.{app_name}/settings.json`, project `./.{app_name}/settings.json`, in-memory session). When no rule matches, the user is prompted with `Allow once / Allow for session / Allow always (save to project) / Deny`. Always-grants persist into the project settings file so the next run picks them up automatically.
541
543
 
542
- ```python
543
- from agentic_cli.tools import hitl_tools
544
- # request_approval(message, options) -> {"success": True, "choice": "..."}
545
- ```
544
+ See `docs/superpowers/specs/2026-04-18-permissions-system-design.md` for the full design.
546
545
 
547
546
  ## CLI Commands
548
547
 
@@ -718,22 +717,32 @@ agentic-cli/
718
717
  │ │ ├── adk/
719
718
  │ │ │ ├── manager.py # GoogleADKWorkflowManager
720
719
  │ │ │ ├── event_processor.py
721
- │ │ │ ├── plugins.py # ConfirmationPlugin, LLMLoggingPlugin
720
+ │ │ │ ├── plugins.py # LLMLoggingPlugin
721
+ │ │ │ ├── permission_plugin.py # PermissionPlugin (capability gating)
722
722
  │ │ │ └── task_progress_plugin.py
723
- │ │ └── langgraph/
724
- │ │ ├── manager.py # LangGraphWorkflowManager
725
- │ │ ├── graph_builder.py
726
- │ │ ├── state.py
727
- │ │ └── persistence/ # Checkpointers and stores
723
+ │ │ ├── langgraph/
724
+ │ │ │ ├── manager.py # LangGraphWorkflowManager
725
+ │ │ │ ├── graph_builder.py
726
+ │ │ │ ├── permission_wrap.py # wrap_tool_for_permission
727
+ │ │ │ ├── state.py
728
+ │ │ │ └── persistence/ # Checkpointers and stores
729
+ │ │ └── permissions/ # Framework-independent permission engine
730
+ │ │ ├── capabilities.py # Capability, ResolvedCapability, EXEMPT
731
+ │ │ ├── rules.py # Rule, Effect, RuleSource, CheckResult, AskScope
732
+ │ │ ├── matchers.py # Path/URL/Shell/StringGlob matchers
733
+ │ │ ├── store.py # PermissionContext, BUILTIN_RULES, JSON load/save
734
+ │ │ ├── prompt.py # build_request + parse_response
735
+ │ │ └── engine.py # PermissionEngine
728
736
  │ ├── tools/
729
- │ │ ├── registry.py # ToolRegistry, ToolCategory, PermissionLevel
737
+ │ │ ├── registry.py # ToolRegistry, ToolCategory, register_tool
730
738
  │ │ ├── factories.py # Backend-aware tool factories
731
739
  │ │ ├── executor.py # SafePythonExecutor
732
740
  │ │ ├── arxiv_tools.py # search_arxiv, fetch_arxiv_paper, ingest_arxiv_paper
733
741
  │ │ ├── arxiv_source.py # ArxivSearchSource (service-registered)
734
742
  │ │ ├── execution_tools.py # execute_python
735
743
  │ │ ├── interaction_tools.py # ask_clarification
736
- │ │ ├── knowledge_tools.py # kb_search / kb_ingest / kb_read / kb_list /
744
+ │ │ ├── knowledge_tools.py # kb_search / kb_ingest_text / kb_ingest_file /
745
+ │ │ │ # kb_ingest_url / kb_read / kb_list /
737
746
  │ │ │ # kb_write_concept / kb_search_concepts
738
747
  │ │ ├── file_read.py # read_file, diff_compare
739
748
  │ │ ├── file_write.py # write_file, edit_file (atomic)
@@ -744,7 +753,6 @@ agentic-cli/
744
753
  │ │ ├── pdf_utils.py # PDF text extraction helpers
745
754
  │ │ ├── memory_tools.py # save/search/update/delete + MemoryStore
746
755
  │ │ ├── reflection_tools.py # save_reflection + ToolReflectionStore
747
- │ │ ├── hitl_tools.py # request_approval
748
756
  │ │ ├── _core/ # Shared planning/task logic
749
757
  │ │ │ ├── planning.py
750
758
  │ │ │ └── tasks.py
@@ -50,7 +50,7 @@ When asked to research papers on a topic:
50
50
  1. Check if a relevant concept page already exists with `kb_search_concepts(topic)`. If one is relevant, read it first — it already synthesizes prior work and lists the source `doc_ids` you can follow.
51
51
  2. Search arXiv for relevant papers. If a search returns `success: false`, report the error instead of retrying with simpler queries.
52
52
  3. Fetch metadata for papers of interest.
53
- 4. Ingest papers into the knowledge base by passing an arXiv PDF URL to `kb_ingest` — this auto-fetches metadata, downloads the PDF, extracts text, embeds chunks, and generates the sidecar in one call. The response includes the LLM-generated `summary` and the `document_id`.
53
+ 4. Ingest papers into the knowledge base. For arXiv papers, call `ingest_arxiv_paper(arxiv_id)` — it handles metadata, PDF download, and rate limiting. For other web sources use `kb_ingest_url(url)`; for a local PDF use `kb_ingest_file(path)`; for raw text you already hold use `kb_ingest_text(content)`. Each returns a `document_id` and the LLM-generated `summary`.
54
54
  5. Read each paper's sidecar with `kb_read(doc_id)` to plan the per-paper analysis.
55
55
  6. Pull full text with `kb_read(doc_id, full=True)` for any sections where the sidecar is insufficient (specific numbers, quotes, methodology details).
56
56
  7. Save detailed per-paper analyses via `write_file`.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agentic-cli"
7
- version = "0.5.0"
7
+ version = "0.5.2"
8
8
  description = "A framework for building domain-specific agentic CLI applications"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -46,6 +46,8 @@ kb = [
46
46
  "torch>=2.2.0",
47
47
  "sentence-transformers>=2.0.0,<3.0.0",
48
48
  "faiss-cpu>=1.7.0",
49
+ "bm25s>=0.2.0",
50
+ "rank-bm25>=0.2.2",
49
51
  ]
50
52
  langgraph = [
51
53
  "langgraph>=0.2.0",
@@ -92,4 +92,4 @@ __all__ = [
92
92
  "CLISettingsMixin",
93
93
  ]
94
94
 
95
- __version__ = "0.5.0"
95
+ __version__ = "0.5.2"
@@ -0,0 +1,137 @@
1
+ """Real BM25 index backends (bm25s, rank_bm25).
2
+
3
+ Both implement the same interface as MockBM25Index so create_bm25_index()
4
+ can return any of them interchangeably. Tokenization is lowercase whitespace
5
+ split to match MockBM25Index; the scoring model is the library's own BM25.
6
+
7
+ Neither underlying library supports true incremental add/remove, so we keep
8
+ tokenized docs + chunk_ids in memory and rebuild the model lazily on search.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ from pathlib import Path
15
+
16
+ from agentic_cli.file_utils import atomic_write_json
17
+
18
+
19
+ def _tokenize(text: str) -> list[str]:
20
+ return text.lower().split()
21
+
22
+
23
+ class _BM25BackendBase:
24
+ _INDEX_FILE: str = ""
25
+
26
+ def __init__(self):
27
+ self._chunk_ids: list[str] = []
28
+ self._tokenized: list[list[str]] = []
29
+ self._model = None
30
+
31
+ @property
32
+ def size(self) -> int:
33
+ return len(self._chunk_ids)
34
+
35
+ def add_documents(self, chunk_ids: list[str], texts: list[str]) -> None:
36
+ for cid, text in zip(chunk_ids, texts):
37
+ self._chunk_ids.append(cid)
38
+ self._tokenized.append(_tokenize(text))
39
+ self._model = None
40
+
41
+ def remove_documents(self, chunk_ids: list[str]) -> None:
42
+ remove_set = set(chunk_ids)
43
+ keep = [
44
+ (cid, toks)
45
+ for cid, toks in zip(self._chunk_ids, self._tokenized)
46
+ if cid not in remove_set
47
+ ]
48
+ if keep:
49
+ self._chunk_ids, self._tokenized = map(list, zip(*keep))
50
+ else:
51
+ self._chunk_ids, self._tokenized = [], []
52
+ self._model = None
53
+
54
+ def rebuild(self, chunk_ids: list[str], texts: list[str]) -> None:
55
+ self._chunk_ids = []
56
+ self._tokenized = []
57
+ self._model = None
58
+ self.add_documents(chunk_ids, texts)
59
+
60
+ def save(self, path: Path) -> None:
61
+ path.mkdir(parents=True, exist_ok=True)
62
+ atomic_write_json(
63
+ path / self._INDEX_FILE,
64
+ {"chunk_ids": self._chunk_ids, "tokenized": self._tokenized},
65
+ )
66
+
67
+ def load(self, path: Path) -> None:
68
+ index_path = path / self._INDEX_FILE
69
+ if not index_path.exists():
70
+ return
71
+ data = json.loads(index_path.read_text())
72
+ self._chunk_ids = data["chunk_ids"]
73
+ self._tokenized = data["tokenized"]
74
+ self._model = None
75
+
76
+
77
+ class RankBM25Index(_BM25BackendBase):
78
+ """BM25 backed by rank_bm25.BM25Plus (pure Python).
79
+
80
+ BM25Plus rather than BM25Okapi because Okapi's IDF can go zero or
81
+ negative when a term appears in most of the corpus; BM25Plus adds a
82
+ delta offset that guarantees positive contributions on real matches.
83
+ """
84
+
85
+ _INDEX_FILE = "bm25_rank.json"
86
+
87
+ def search(self, query: str, top_k: int = 10) -> list[tuple[str, float]]:
88
+ if not self._chunk_ids:
89
+ return []
90
+ query_tokens = _tokenize(query)
91
+ if not query_tokens:
92
+ return []
93
+ query_set = set(query_tokens)
94
+ if self._model is None:
95
+ from rank_bm25 import BM25Plus
96
+
97
+ self._model = BM25Plus(self._tokenized)
98
+ scores = self._model.get_scores(query_tokens)
99
+ scored: list[tuple[str, float]] = []
100
+ for cid, doc_tokens, score in zip(
101
+ self._chunk_ids, self._tokenized, scores
102
+ ):
103
+ if query_set.intersection(doc_tokens):
104
+ scored.append((cid, float(score)))
105
+ scored.sort(key=lambda x: x[1], reverse=True)
106
+ return scored[:top_k]
107
+
108
+
109
+ class BM25sIndex(_BM25BackendBase):
110
+ """BM25 backed by bm25s (NumPy/C-accelerated)."""
111
+
112
+ _INDEX_FILE = "bm25s_sidecar.json"
113
+
114
+ def _build_model(self) -> None:
115
+ import bm25s
116
+
117
+ model = bm25s.BM25()
118
+ model.index(self._tokenized, show_progress=False)
119
+ self._model = model
120
+
121
+ def search(self, query: str, top_k: int = 10) -> list[tuple[str, float]]:
122
+ if not self._chunk_ids:
123
+ return []
124
+ query_tokens = _tokenize(query)
125
+ if not query_tokens:
126
+ return []
127
+ if self._model is None:
128
+ self._build_model()
129
+ k = min(top_k, len(self._chunk_ids))
130
+ docs, scores = self._model.retrieve(
131
+ [query_tokens], k=k, show_progress=False
132
+ )
133
+ results: list[tuple[str, float]] = []
134
+ for idx, score in zip(docs[0], scores[0]):
135
+ if score > 0:
136
+ results.append((self._chunk_ids[int(idx)], float(score)))
137
+ return results