voodoo-framework 2.0.0__tar.gz → 2.2.0__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 (219) hide show
  1. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/PKG-INFO +14 -20
  2. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/README.md +13 -19
  3. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/__init__.py +13 -1
  4. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/__init__.py +2 -0
  5. voodoo_framework-2.2.0/src/voodoo/cli/approvals.py +283 -0
  6. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/primitives/effect.py +9 -0
  7. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/capability.py +54 -4
  8. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/engine.py +87 -6
  9. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/human.py +8 -0
  10. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/security/__init__.py +26 -0
  11. voodoo_framework-2.2.0/src/voodoo/security/redaction.py +175 -0
  12. voodoo_framework-2.2.0/src/voodoo/security/secrets.py +223 -0
  13. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/execution/migrations.py +5 -2
  14. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/execution/postgres.py +27 -3
  15. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/execution/sqlite.py +25 -3
  16. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo_framework.egg-info/PKG-INFO +14 -20
  17. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo_framework.egg-info/SOURCES.txt +5 -0
  18. voodoo_framework-2.2.0/tests/test_capability_security.py +426 -0
  19. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_contract_api.py +11 -0
  20. voodoo_framework-2.2.0/tests/test_durable_hitl.py +346 -0
  21. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/LICENSE +0 -0
  22. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/pyproject.toml +0 -0
  23. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/setup.cfg +0 -0
  24. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/adapters/__init__.py +0 -0
  25. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/adapters/capabilities.py +0 -0
  26. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/adapters/registry.py +0 -0
  27. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/adapters/tailwind.py +0 -0
  28. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/adapters/voodoo_css.py +0 -0
  29. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/agent.py +0 -0
  30. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/agents/__init__.py +0 -0
  31. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/agents/models.py +0 -0
  32. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/agents/registry.py +0 -0
  33. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ai/__init__.py +0 -0
  34. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ai/agent.py +0 -0
  35. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ai/providers/__init__.py +0 -0
  36. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ai/providers/anthropic.py +0 -0
  37. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ai/providers/gemini.py +0 -0
  38. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ai/providers/mock.py +0 -0
  39. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ai/providers/ollama.py +0 -0
  40. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ai/providers/openai.py +0 -0
  41. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ai/tools/__init__.py +0 -0
  42. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ai/tools/registry.py +0 -0
  43. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/api.py +0 -0
  44. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/auth/__init__.py +0 -0
  45. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/auth/cookies.py +0 -0
  46. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/auth/guards.py +0 -0
  47. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/auth/jwt.py +0 -0
  48. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/auth/middleware.py +0 -0
  49. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/auth/passwords.py +0 -0
  50. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/auth/user.py +0 -0
  51. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/__main__.py +0 -0
  52. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/agents.py +0 -0
  53. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/ai.py +0 -0
  54. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/auth.py +0 -0
  55. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/dev.py +0 -0
  56. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/doctor.py +0 -0
  57. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/executions.py +0 -0
  58. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/generate.py +0 -0
  59. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/inspect.py +0 -0
  60. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/new.py +0 -0
  61. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/objects.py +0 -0
  62. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/recover.py +0 -0
  63. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/routes.py +0 -0
  64. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/scaffolding.py +0 -0
  65. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/schedules.py +0 -0
  66. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/tasks.py +0 -0
  67. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/terminal.py +0 -0
  68. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/theme.py +0 -0
  69. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/cli/version.py +0 -0
  70. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/components.py +0 -0
  71. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/config.py +0 -0
  72. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/core/__init__.py +0 -0
  73. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/core/app.py +0 -0
  74. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/core/errors.py +0 -0
  75. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/core/events.py +0 -0
  76. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/core/render.py +0 -0
  77. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/core/routing.py +0 -0
  78. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/core/sitemap.py +0 -0
  79. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/core/state.py +0 -0
  80. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/data/__init__.py +0 -0
  81. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/data/base.py +0 -0
  82. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/data/model.py +0 -0
  83. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/i18n.py +0 -0
  84. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/mcp/__init__.py +0 -0
  85. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/mcp/client.py +0 -0
  86. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/memory/__init__.py +0 -0
  87. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/memory/interfaces.py +0 -0
  88. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/memory/sqlite.py +0 -0
  89. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/mesh/__init__.py +0 -0
  90. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/mesh/client.py +0 -0
  91. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/mesh/event_bus.py +0 -0
  92. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/primitives/__init__.py +0 -0
  93. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/primitives/capability.py +0 -0
  94. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/primitives/compute.py +0 -0
  95. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/primitives/constraint.py +0 -0
  96. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/primitives/intent.py +0 -0
  97. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/primitives/resource.py +0 -0
  98. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/primitives/state.py +0 -0
  99. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/primitives/time.py +0 -0
  100. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/py.typed +0 -0
  101. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/queue.py +0 -0
  102. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/routing/api.py +0 -0
  103. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/routing/pages.py +0 -0
  104. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/__init__.py +0 -0
  105. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/adaptive.py +0 -0
  106. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/constraint.py +0 -0
  107. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/context.py +0 -0
  108. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/errors.py +0 -0
  109. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/execution.py +0 -0
  110. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/graph.py +0 -0
  111. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/persistence.py +0 -0
  112. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/planner.py +0 -0
  113. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/scheduler.py +0 -0
  114. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/task.py +0 -0
  115. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/runtime/workflow.py +0 -0
  116. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/schedule.py +0 -0
  117. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/security/cors.py +0 -0
  118. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/security/csrf.py +0 -0
  119. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/security/headers.py +0 -0
  120. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/security/passwords.py +0 -0
  121. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/security/rate_limit.py +0 -0
  122. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/seo.py +0 -0
  123. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/static/client.js +0 -0
  124. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/status.py +0 -0
  125. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/__init__.py +0 -0
  126. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/cache/__init__.py +0 -0
  127. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/cache/interfaces.py +0 -0
  128. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/cache/memory.py +0 -0
  129. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/cache/redis.py +0 -0
  130. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/database/__init__.py +0 -0
  131. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/database/interfaces.py +0 -0
  132. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/database/postgres.py +0 -0
  133. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/database/sqlite.py +0 -0
  134. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/events/__init__.py +0 -0
  135. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/events/interfaces.py +0 -0
  136. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/events/local.py +0 -0
  137. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/events/postgres.py +0 -0
  138. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/events/sqlite.py +0 -0
  139. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/execution/__init__.py +0 -0
  140. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/manager.py +0 -0
  141. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/objects/__init__.py +0 -0
  142. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/objects/interfaces.py +0 -0
  143. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/objects/local.py +0 -0
  144. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/objects/s3.py +0 -0
  145. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/queue/__init__.py +0 -0
  146. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/queue/interfaces.py +0 -0
  147. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/queue/memory.py +0 -0
  148. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/queue/postgres.py +0 -0
  149. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/queue/redis.py +0 -0
  150. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/queue/sqlite.py +0 -0
  151. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/scheduler/__init__.py +0 -0
  152. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/storage/scheduler/sqlite.py +0 -0
  153. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/telemetry/__init__.py +0 -0
  154. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/telemetry/api.py +0 -0
  155. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/telemetry/middleware.py +0 -0
  156. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/telemetry/store.py +0 -0
  157. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/theme.py +0 -0
  158. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/tools/__init__.py +0 -0
  159. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/tools/registry.py +0 -0
  160. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ui/__init__.py +0 -0
  161. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ui/component.py +0 -0
  162. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ui/events.py +0 -0
  163. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ui/library.py +0 -0
  164. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ui/markdown.py +0 -0
  165. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ui/rendering.py +0 -0
  166. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ui/state.py +0 -0
  167. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ui/styles/__init__.py +0 -0
  168. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ui/styles/presets.py +0 -0
  169. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/ui/styles/theme.py +0 -0
  170. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/workers/__init__.py +0 -0
  171. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo/workers/queue.py +0 -0
  172. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo_framework.egg-info/dependency_links.txt +0 -0
  173. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo_framework.egg-info/entry_points.txt +0 -0
  174. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo_framework.egg-info/requires.txt +0 -0
  175. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/src/voodoo_framework.egg-info/top_level.txt +0 -0
  176. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_agent.py +0 -0
  177. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_agent_registry.py +0 -0
  178. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_agent_runtime.py +0 -0
  179. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_app.py +0 -0
  180. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_auth.py +0 -0
  181. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_checkpoint_resume.py +0 -0
  182. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_chrome.py +0 -0
  183. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_cli.py +0 -0
  184. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_cli_inspect.py +0 -0
  185. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_components.py +0 -0
  186. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_config.py +0 -0
  187. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_data.py +0 -0
  188. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_design_system.py +0 -0
  189. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_events.py +0 -0
  190. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_execution_sqlite_store.py +0 -0
  191. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_http_runtime.py +0 -0
  192. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_human.py +0 -0
  193. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_i18n.py +0 -0
  194. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_integration.py +0 -0
  195. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_mcp.py +0 -0
  196. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_mcp_runtime.py +0 -0
  197. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_memory.py +0 -0
  198. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_mesh.py +0 -0
  199. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_mesh_event_bus.py +0 -0
  200. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_model.py +0 -0
  201. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_object_store.py +0 -0
  202. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_persistence.py +0 -0
  203. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_planner_adaptive.py +0 -0
  204. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_primitives.py +0 -0
  205. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_provider_switching.py +0 -0
  206. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_providers.py +0 -0
  207. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_queue.py +0 -0
  208. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_reactive.py +0 -0
  209. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_runtime.py +0 -0
  210. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_scheduler.py +0 -0
  211. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_security.py +0 -0
  212. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_seo.py +0 -0
  213. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_state.py +0 -0
  214. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_theme.py +0 -0
  215. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_theme_presets.py +0 -0
  216. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_tools.py +0 -0
  217. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_ui.py +0 -0
  218. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_websocket.py +0 -0
  219. {voodoo_framework-2.0.0 → voodoo_framework-2.2.0}/tests/test_workers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voodoo-framework
3
- Version: 2.0.0
3
+ Version: 2.2.0
4
4
  Summary: The programmable runtime for adaptive applications and operational systems — web, APIs, agents, workers, human workflows, distributed systems, and physical systems in one runtime.
5
5
  Author-email: Helder Perez <contact@helderperez.com>
6
6
  Maintainer-email: Helder Perez <contact@helderperez.com>
@@ -466,7 +466,7 @@ voodoo auth secret-key
466
466
 
467
467
  ## Project Status & Roadmap
468
468
 
469
- Voodoo is in active development (v1.16.0, Beta). The core runtime, UI system, AI agents, MCP, durable execution, and adaptive runtime are production-ready. PostgreSQL, S3/R2, and Redis adapters are shipped behind optional extras; the AI runtime (model-provider protocol, memory, durable agents, and durable human-in-the-loop) is the current focus.
469
+ Voodoo is in active development (v2.0.0, Beta). The core runtime, UI system, AI agents, MCP, durable execution, adaptive runtime, model-provider protocol, layered memory, and durable agent registry are production-ready. PostgreSQL, S3/R2, and Redis adapters are shipped behind optional extras; durable human-in-the-loop is the current focus.
470
470
 
471
471
  ```mermaid
472
472
  timeline
@@ -477,26 +477,20 @@ timeline
477
477
  Unified Runtime : ExecutionEngine : Planner : Adaptive supervisor
478
478
  Durable Local Runtime : SQLite queue : Executions : Scheduler : Objects : Events
479
479
  Production Providers : PostgreSQL : S3/R2 : Redis
480
- section In Progress (v1.16–v1.19)
480
+ section Shipped (v1.16–v2.0)
481
481
  ModelProvider protocol : Model descriptors : Routing aliases
482
482
  Memory capability : Layered memory : SQLite + FTS5
483
- Durable Agents & HITL : Agent registry : Resumable approvals
484
- section Planned (v2.0+)
485
- Capability Security & Secrets : v2.0
486
- Observability & Protocol : v2.1–v2.2
487
- Local Runtime DX : v2.3
488
- ```
489
-
490
- | Milestone | Version | Status |
491
- |-----------|---------|--------|
492
- | Core framework — runtime, UI, routing, reactive state | v1.0–v1.1 | ✅ Shipped |
493
- | AI & unified runtime — agents, tools, MCP, HITL, ExecutionEngine, planner, adaptive runtime | v1.2 | ✅ Shipped |
494
- | Durable local runtime (zero-infra) — SQLite storage, queue, executions, scheduler, objects, events | v1.3–v1.9 | ✅ Shipped |
495
- | Adapter contracts & runtime configuration | v1.10–v1.11 | ✅ Shipped |
496
- | Production providers (optional) — PostgreSQL, S3/R2, Redis | v1.12–v1.15 | ✅ Shipped |
497
- | AI runtime — model-provider protocol, memory, durable agents, durable HITL | v1.16–v1.19 | 🚧 In Progress |
498
- | Capability security & secrets | v2.0 | 📋 Planned |
499
- | Protocol stability & DX — observability, schemas, local runtime | v2.1–v2.3 | 📋 Planned |
483
+ Durable Agents : Agent registry : v2.0
484
+ section In Progress (v2.1)
485
+ Durable HITL : Resumable approvals
486
+ section Planned (v2.2+)
487
+ Capability Security & Secrets : v2.2
488
+ Observability & Protocol : v2.3–v2.4
489
+ Local Runtime DX : v2.5
490
+ | AI runtime — model-provider protocol, memory, durable agent registry | v1.16–v2.0 | ✅ Shipped |
491
+ | Durable human-in-the-loop — resumable approvals | v2.1 | 🚧 In Progress |
492
+ | Capability security & secrets | v2.2 | 📋 Planned |
493
+ | Protocol stability & DX — observability, schemas, local runtime | v2.3–v2.5 | 📋 Planned |
500
494
 
501
495
  See the [master roadmap](ROADMAP.md) and [sprint plan](SPRINT_PLAN.md) for details.
502
496
 
@@ -403,7 +403,7 @@ voodoo auth secret-key
403
403
 
404
404
  ## Project Status & Roadmap
405
405
 
406
- Voodoo is in active development (v1.16.0, Beta). The core runtime, UI system, AI agents, MCP, durable execution, and adaptive runtime are production-ready. PostgreSQL, S3/R2, and Redis adapters are shipped behind optional extras; the AI runtime (model-provider protocol, memory, durable agents, and durable human-in-the-loop) is the current focus.
406
+ Voodoo is in active development (v2.0.0, Beta). The core runtime, UI system, AI agents, MCP, durable execution, adaptive runtime, model-provider protocol, layered memory, and durable agent registry are production-ready. PostgreSQL, S3/R2, and Redis adapters are shipped behind optional extras; durable human-in-the-loop is the current focus.
407
407
 
408
408
  ```mermaid
409
409
  timeline
@@ -414,26 +414,20 @@ timeline
414
414
  Unified Runtime : ExecutionEngine : Planner : Adaptive supervisor
415
415
  Durable Local Runtime : SQLite queue : Executions : Scheduler : Objects : Events
416
416
  Production Providers : PostgreSQL : S3/R2 : Redis
417
- section In Progress (v1.16–v1.19)
417
+ section Shipped (v1.16–v2.0)
418
418
  ModelProvider protocol : Model descriptors : Routing aliases
419
419
  Memory capability : Layered memory : SQLite + FTS5
420
- Durable Agents & HITL : Agent registry : Resumable approvals
421
- section Planned (v2.0+)
422
- Capability Security & Secrets : v2.0
423
- Observability & Protocol : v2.1–v2.2
424
- Local Runtime DX : v2.3
425
- ```
426
-
427
- | Milestone | Version | Status |
428
- |-----------|---------|--------|
429
- | Core framework — runtime, UI, routing, reactive state | v1.0–v1.1 | ✅ Shipped |
430
- | AI & unified runtime — agents, tools, MCP, HITL, ExecutionEngine, planner, adaptive runtime | v1.2 | ✅ Shipped |
431
- | Durable local runtime (zero-infra) — SQLite storage, queue, executions, scheduler, objects, events | v1.3–v1.9 | ✅ Shipped |
432
- | Adapter contracts & runtime configuration | v1.10–v1.11 | ✅ Shipped |
433
- | Production providers (optional) — PostgreSQL, S3/R2, Redis | v1.12–v1.15 | ✅ Shipped |
434
- | AI runtime — model-provider protocol, memory, durable agents, durable HITL | v1.16–v1.19 | 🚧 In Progress |
435
- | Capability security & secrets | v2.0 | 📋 Planned |
436
- | Protocol stability & DX — observability, schemas, local runtime | v2.1–v2.3 | 📋 Planned |
420
+ Durable Agents : Agent registry : v2.0
421
+ section In Progress (v2.1)
422
+ Durable HITL : Resumable approvals
423
+ section Planned (v2.2+)
424
+ Capability Security & Secrets : v2.2
425
+ Observability & Protocol : v2.3–v2.4
426
+ Local Runtime DX : v2.5
427
+ | AI runtime — model-provider protocol, memory, durable agent registry | v1.16–v2.0 | ✅ Shipped |
428
+ | Durable human-in-the-loop — resumable approvals | v2.1 | 🚧 In Progress |
429
+ | Capability security & secrets | v2.2 | 📋 Planned |
430
+ | Protocol stability & DX — observability, schemas, local runtime | v2.3–v2.5 | 📋 Planned |
437
431
 
438
432
  See the [master roadmap](ROADMAP.md) and [sprint plan](SPRINT_PLAN.md) for details.
439
433
 
@@ -102,7 +102,7 @@ from .ui.styles import (
102
102
  from .ui.styles.theme import Theme, ThemeColors, create_theme
103
103
  from .workers import task
104
104
 
105
- __version__ = "2.0.0"
105
+ __version__ = "2.2.0"
106
106
 
107
107
  __all__ = [
108
108
  # Core runtime
@@ -306,6 +306,18 @@ _DEPRECATED_EXPORTS: dict[str, str] = {
306
306
  "rate_limiter": "voodoo.security",
307
307
  "set_csrf_cookie": "voodoo.security",
308
308
  "validate_password_strength": "voodoo.security",
309
+ # voodoo.security — Sprint 19
310
+ "SecretStore": "voodoo.security",
311
+ "EnvSecretStore": "voodoo.security",
312
+ "LocalSecretStore": "voodoo.security",
313
+ "SecretsError": "voodoo.security",
314
+ "secrets": "voodoo.security",
315
+ "configure_secrets": "voodoo.security",
316
+ "RedactionGuard": "voodoo.security",
317
+ "redact": "voodoo.security",
318
+ "redact_string": "voodoo.security",
319
+ # voodoo.runtime — Sprint 19
320
+ "SENSITIVE_CAPABILITIES": "voodoo.runtime.capability",
309
321
  # voodoo.seo
310
322
  "FAQ": "voodoo.seo",
311
323
  "GEO": "voodoo.seo",
@@ -3,6 +3,7 @@ import typer
3
3
  from voodoo.cli import (
4
4
  agents,
5
5
  ai,
6
+ approvals,
6
7
  dev,
7
8
  doctor,
8
9
  executions,
@@ -38,6 +39,7 @@ app.add_typer(schedules.schedules_app, name="schedules")
38
39
  app.add_typer(executions.executions_app, name="executions")
39
40
  app.add_typer(objects.objects_app, name="objects")
40
41
  app.add_typer(agents.agents_app, name="agents")
42
+ app.add_typer(approvals.approvals_app, name="approvals")
41
43
  app.add_typer(theme.theme_app, name="theme")
42
44
  # Top-level aliases per the Sprint 3 CLI surface: `voodoo execution <id>`
43
45
  # (timeline from the journal) and `voodoo events` (recent journal events).
@@ -0,0 +1,283 @@
1
+ """voodoo approvals — decide pending human approvals from any machine.
2
+
3
+ Sprint 18: human approval is an execution state that survives process
4
+ death. This CLI reads and decides approvals directly from the durable
5
+ execution store (SQLite by default), so a decision can be made on any
6
+ machine — the waiting execution resumes on whichever worker picks it up.
7
+
8
+ voodoo approvals list [--pending] [--json]
9
+ voodoo approvals show <execution-id> [--json]
10
+ voodoo approvals approve <execution-id> [--by NAME] [--note TEXT]
11
+ voodoo approvals deny <execution-id> [--by NAME] [--reason TEXT]
12
+
13
+ With ``--app`` (e.g. ``main:app``) the application module is imported
14
+ first so registered participants (agents, workflows) re-register and the
15
+ approved execution actually re-runs.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import asyncio
21
+ import os
22
+ from pathlib import Path
23
+
24
+ import typer
25
+
26
+ from voodoo.cli import terminal
27
+
28
+ approvals_app = typer.Typer(
29
+ name="approvals",
30
+ help="List, approve, and deny human approvals.",
31
+ no_args_is_help=True,
32
+ rich_markup_mode="rich",
33
+ )
34
+
35
+
36
+ def _resolve_store(store_path: str | None):
37
+ """Return the configured execution store (SQLite by default)."""
38
+ from voodoo.config import config
39
+ from voodoo.runtime.persistence import JSONFileExecutionStore
40
+ from voodoo.storage.execution import SQLiteExecutionStore
41
+
42
+ if store_path is None:
43
+ store_path = os.environ.get("VOODOO_EXECUTION_STORE", config.db_path).replace(
44
+ ":memory:", ".voodoo/state/data.db"
45
+ )
46
+
47
+ if Path(store_path).suffix == ".jsonl":
48
+ return JSONFileExecutionStore(store_path), store_path
49
+ return SQLiteExecutionStore(store_path), store_path
50
+
51
+
52
+ def _load_app(app_str: str | None) -> None:
53
+ """Import the application module so participants re-register."""
54
+ if app_str is None:
55
+ return
56
+ import importlib
57
+ import sys
58
+
59
+ module_name, _, attr = app_str.partition(":")
60
+ attr = attr or "app"
61
+ sys.path.insert(0, os.getcwd())
62
+ mod = importlib.import_module(module_name)
63
+ getattr(mod, attr, None)
64
+
65
+
66
+ def _list_approvals(store, pending_only: bool) -> list[dict]:
67
+ """Read approvals from a store, tolerating stores without the seam."""
68
+ if not hasattr(store, "load_approvals"):
69
+ return []
70
+ return store.load_approvals(pending_only=pending_only)
71
+
72
+
73
+ @approvals_app.command("list")
74
+ def list_approvals(
75
+ pending_only: bool = typer.Option(
76
+ False, "--pending", help="Only show pending approvals"
77
+ ),
78
+ store_path: str = typer.Option(
79
+ None, "--store", help="Path to the execution store (SQLite by default)"
80
+ ),
81
+ app_str: str = typer.Option(
82
+ None, "--app", help="App instance (e.g. main:app) to import first"
83
+ ),
84
+ json_mode: bool = typer.Option(
85
+ False, "--json", help="Machine-readable JSON output"
86
+ ),
87
+ ):
88
+ """List approvals — pending and decided."""
89
+ store, resolved = _resolve_store(store_path)
90
+ _load_app(app_str)
91
+ approvals = _list_approvals(store, pending_only)
92
+
93
+ if json_mode or terminal.is_json_mode():
94
+ terminal.json_output({"store": resolved, "approvals": approvals})
95
+ return
96
+
97
+ terminal.wordmark()
98
+ terminal.blank()
99
+ if not approvals:
100
+ terminal.muted(
101
+ f"no {'pending ' if pending_only else ''}approvals in {resolved}"
102
+ )
103
+ return
104
+ for a in approvals:
105
+ terminal.info(
106
+ f" {str(a.get('id', ''))[:8]} {(a.get('question') or '-')[:60]}"
107
+ )
108
+ terminal.muted(
109
+ f" execution: {str(a.get('execution_id', ''))[:8]}"
110
+ f" status: {a.get('status', '-')}"
111
+ )
112
+ terminal.muted(
113
+ f" requested_by: {a.get('requested_by') or '-'}"
114
+ f" decided_by: {a.get('decided_by') or '-'}"
115
+ )
116
+ terminal.blank()
117
+
118
+
119
+ @approvals_app.command("show")
120
+ def show_approval(
121
+ execution_id: str = typer.Argument(..., help="Execution id of the approval"),
122
+ store_path: str = typer.Option(
123
+ None, "--store", help="Path to the execution store (SQLite by default)"
124
+ ),
125
+ json_mode: bool = typer.Option(
126
+ False, "--json", help="Machine-readable JSON output"
127
+ ),
128
+ ):
129
+ """Show one approval in detail."""
130
+ store, resolved = _resolve_store(store_path)
131
+ approval = (
132
+ store.load_approval(execution_id) if hasattr(store, "load_approval") else None
133
+ )
134
+ if approval is None:
135
+ terminal.error(
136
+ f"approval for execution '{execution_id}' not found in {resolved}"
137
+ )
138
+ raise typer.Exit(1)
139
+
140
+ if json_mode or terminal.is_json_mode():
141
+ terminal.json_output(approval)
142
+ return
143
+
144
+ terminal.wordmark()
145
+ terminal.blank()
146
+ terminal.status_block(
147
+ [
148
+ ("id", str(approval.get("id", ""))),
149
+ ("execution", str(approval.get("execution_id", ""))),
150
+ ("trace", str(approval.get("trace_id", "") or "-")),
151
+ ("status", approval.get("status", "-")),
152
+ ("requested_by", approval.get("requested_by") or "-"),
153
+ ("decided_by", approval.get("decided_by") or "-"),
154
+ ("decided_at", approval.get("decided_at") or "-"),
155
+ ("participant", approval.get("participant") or "-"),
156
+ ]
157
+ )
158
+ question = approval.get("question")
159
+ if question:
160
+ terminal.blank()
161
+ terminal.label_value("question", question)
162
+ reason = approval.get("reason")
163
+ if reason:
164
+ terminal.label_value("reason", reason)
165
+ terminal.blank()
166
+
167
+
168
+ def _decide(
169
+ execution_id: str,
170
+ *,
171
+ decision: str,
172
+ by: str,
173
+ message: str | None,
174
+ store_path: str | None,
175
+ app_str: str | None,
176
+ json_mode: bool,
177
+ ) -> None:
178
+ """Shared approve/deny path: recover waiting executions, then decide."""
179
+ from voodoo.runtime.engine import engine as runtime_engine
180
+
181
+ store, resolved = _resolve_store(store_path)
182
+ runtime_engine.use_store(store)
183
+ # Restore waiting executions + rehydrate persisted approvals so the
184
+ # decision applies to the durable record — not just this process.
185
+ runtime_engine.recover()
186
+ _load_app(app_str)
187
+
188
+ if runtime_engine.approvals.get(execution_id) is None:
189
+ if json_mode or terminal.is_json_mode():
190
+ terminal.json_output(
191
+ {
192
+ "error": "approval not found",
193
+ "execution_id": execution_id,
194
+ "store": resolved,
195
+ }
196
+ )
197
+ else:
198
+ terminal.error(f"approval for execution '{execution_id}' not found")
199
+ raise typer.Exit(1)
200
+
201
+ decide_fn = runtime_engine.approve if decision == "approve" else runtime_engine.deny
202
+ kwargs = {}
203
+ if decision == "approve":
204
+ kwargs["note"] = message
205
+ else:
206
+ kwargs["reason"] = message or "denied"
207
+ executed = asyncio.run(decide_fn(execution_id, by=by, **kwargs))
208
+
209
+ if json_mode or terminal.is_json_mode():
210
+ terminal.json_output(
211
+ {
212
+ "store": resolved,
213
+ "decision": decision,
214
+ "execution_id": execution_id,
215
+ "by": by,
216
+ "resumed": executed.describe() if executed is not None else None,
217
+ }
218
+ )
219
+ return
220
+
221
+ terminal.wordmark()
222
+ terminal.blank()
223
+ icon = "✓" if decision == "approve" else "✗"
224
+ terminal.info(f" {icon} {decision}d execution {execution_id[:8]} by {by}")
225
+ if executed is not None:
226
+ terminal.muted(f" execution → {executed.status.value}")
227
+ else:
228
+ terminal.muted(" execution not found in engine (record updated only)")
229
+ terminal.blank()
230
+
231
+
232
+ @approvals_app.command("approve")
233
+ def approve_cmd(
234
+ execution_id: str = typer.Argument(..., help="Execution id to approve"),
235
+ by: str = typer.Option("human", "--by", help="Who is approving"),
236
+ note: str = typer.Option(None, "--note", help="Optional note"),
237
+ store_path: str = typer.Option(
238
+ None, "--store", help="Path to the execution store (SQLite by default)"
239
+ ),
240
+ app_str: str = typer.Option(
241
+ None, "--app", help="App instance (e.g. main:app) to import first"
242
+ ),
243
+ json_mode: bool = typer.Option(
244
+ False, "--json", help="Machine-readable JSON output"
245
+ ),
246
+ ):
247
+ """Approve a waiting execution — it resumes on a worker."""
248
+ _decide(
249
+ execution_id,
250
+ decision="approve",
251
+ by=by,
252
+ message=note,
253
+ store_path=store_path,
254
+ app_str=app_str,
255
+ json_mode=json_mode,
256
+ )
257
+
258
+
259
+ @approvals_app.command("deny")
260
+ def deny_cmd(
261
+ execution_id: str = typer.Argument(..., help="Execution id to deny"),
262
+ by: str = typer.Option("human", "--by", help="Who is denying"),
263
+ reason: str = typer.Option(None, "--reason", help="Denial reason"),
264
+ store_path: str = typer.Option(
265
+ None, "--store", help="Path to the execution store (SQLite by default)"
266
+ ),
267
+ app_str: str = typer.Option(
268
+ None, "--app", help="App instance (e.g. main:app) to import first"
269
+ ),
270
+ json_mode: bool = typer.Option(
271
+ False, "--json", help="Machine-readable JSON output"
272
+ ),
273
+ ):
274
+ """Deny a waiting execution — it fails with the denial reason."""
275
+ _decide(
276
+ execution_id,
277
+ decision="deny",
278
+ by=by,
279
+ message=reason,
280
+ store_path=store_path,
281
+ app_str=app_str,
282
+ json_mode=json_mode,
283
+ )
@@ -61,6 +61,11 @@ class Effect(BaseModel):
61
61
  error: str | None = None
62
62
  created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
63
63
  executed_at: datetime | None = None
64
+ # Authorization context (Sprint 19, ROADMAP §55)
65
+ actor: str | None = None
66
+ principal: str | None = None
67
+ resource: str | None = None
68
+ scope: str | None = None
64
69
 
65
70
  # -- lifecycle ---------------------------------------------------------
66
71
 
@@ -114,6 +119,10 @@ class Effect(BaseModel):
114
119
  "status": self.status.value,
115
120
  "intent_id": self.intent_id,
116
121
  "capability": self.capability_name,
122
+ "actor": self.actor,
123
+ "principal": self.principal,
124
+ "resource": self.resource,
125
+ "scope": self.scope,
117
126
  "reversible": self.reversible,
118
127
  "idempotent": self.idempotent,
119
128
  "idempotency_key": self.idempotency_key,
@@ -23,7 +23,23 @@ from voodoo.primitives.capability import Capability
23
23
  from voodoo.runtime.context import ExecutionContext
24
24
  from voodoo.runtime.errors import ApprovalRequired, CapabilityDenied
25
25
 
26
- __all__ = ["Resolution", "CapabilityResolver"]
26
+ __all__ = ["Resolution", "CapabilityResolver", "SENSITIVE_CAPABILITIES"]
27
+
28
+
29
+ #: Capabilities that require explicit grants — no ambient authority by
30
+ #: default (Sprint 19, ROADMAP §55). An agent or tool that attempts to
31
+ #: use one of these without an explicit grant gets a :class:`CapabilityDenied`
32
+ #: error, never a silent pass-through.
33
+ SENSITIVE_CAPABILITIES: frozenset[str] = frozenset(
34
+ {
35
+ "filesystem.write",
36
+ "network.request",
37
+ "shell.execute",
38
+ "secrets.read",
39
+ "payment.execute",
40
+ "email.send",
41
+ }
42
+ )
27
43
 
28
44
 
29
45
  class Resolution(StrEnum):
@@ -65,17 +81,50 @@ class CapabilityResolver:
65
81
  Context-granted capabilities take precedence (they are the
66
82
  delegated authority for an in-flight execution). Otherwise the
67
83
  registry is consulted.
84
+
85
+ Sensitive capabilities (Sprint 19) are denied by default — they
86
+ require an explicit grant in the context or registry.
68
87
  """
88
+ if name in SENSITIVE_CAPABILITIES:
89
+ return self._resolve_sensitive(name, scope=scope, context=context)
90
+ return self._resolve_standard(name, scope=scope, context=context)
91
+
92
+ def _resolve_sensitive(
93
+ self,
94
+ name: str,
95
+ *,
96
+ scope: str | None = None,
97
+ context: ExecutionContext | None = None,
98
+ ) -> Resolution:
99
+ """Resolve a sensitive capability — requires explicit grant."""
69
100
  if context is not None and context.has_capability(name, scope=scope):
70
- if name in self.approval_capabilities:
71
- return Resolution.REQUIRES_APPROVAL
72
- return Resolution.ALLOWED
101
+ return self._check_approval(name)
102
+ cap = self.capabilities.get(name)
103
+ if cap is not None and cap.valid:
104
+ if scope is not None and cap.scope is not None and cap.scope != scope:
105
+ return Resolution.DENIED
106
+ return self._check_approval(name)
107
+ return Resolution.DENIED
73
108
 
109
+ def _resolve_standard(
110
+ self,
111
+ name: str,
112
+ *,
113
+ scope: str | None = None,
114
+ context: ExecutionContext | None = None,
115
+ ) -> Resolution:
116
+ """Resolve a standard capability — normal registry rules."""
117
+ if context is not None and context.has_capability(name, scope=scope):
118
+ return self._check_approval(name)
74
119
  cap = self.capabilities.get(name)
75
120
  if cap is None or not cap.valid:
76
121
  return Resolution.DENIED
77
122
  if scope is not None and cap.scope is not None and cap.scope != scope:
78
123
  return Resolution.DENIED
124
+ return self._check_approval(name)
125
+
126
+ def _check_approval(self, name: str) -> Resolution:
127
+ """Check whether a capability requires human approval."""
79
128
  if name in self.approval_capabilities:
80
129
  return Resolution.REQUIRES_APPROVAL
81
130
  return Resolution.ALLOWED
@@ -114,4 +163,5 @@ class CapabilityResolver:
114
163
  return {
115
164
  "capabilities": [c.name for c in self.capabilities.values()],
116
165
  "approval_required": sorted(self.approval_capabilities),
166
+ "sensitive": sorted(SENSITIVE_CAPABILITIES),
117
167
  }