reactifact 0.12.0__tar.gz → 0.14.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 (318) hide show
  1. {reactifact-0.12.0/reactifact.egg-info → reactifact-0.14.0}/PKG-INFO +62 -31
  2. {reactifact-0.12.0 → reactifact-0.14.0}/README.md +59 -30
  3. {reactifact-0.12.0 → reactifact-0.14.0}/pyproject.toml +14 -2
  4. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/__init__.py +11 -3
  5. reactifact-0.14.0/reactifact/a2a/__init__.py +77 -0
  6. reactifact-0.14.0/reactifact/a2a/client.py +282 -0
  7. reactifact-0.14.0/reactifact/a2a/models.py +294 -0
  8. reactifact-0.14.0/reactifact/a2a/remote.py +178 -0
  9. reactifact-0.14.0/reactifact/a2a/server.py +381 -0
  10. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/artifacts.py +37 -12
  11. reactifact-0.14.0/reactifact/authz.py +112 -0
  12. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/branching.py +30 -2
  13. reactifact-0.14.0/reactifact/budget.py +166 -0
  14. reactifact-0.14.0/reactifact/cache.py +167 -0
  15. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/chat.py +108 -7
  16. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/__init__.py +7 -2
  17. reactifact-0.14.0/reactifact/cli/eval.py +126 -0
  18. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/replay.py +10 -0
  19. reactifact-0.14.0/reactifact/cli/scenario.py +192 -0
  20. reactifact-0.14.0/reactifact/cli/skills.py +173 -0
  21. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/commit_log.py +142 -16
  22. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/context.py +138 -2
  23. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/effects.py +27 -0
  24. reactifact-0.14.0/reactifact/eval/__init__.py +165 -0
  25. reactifact-0.14.0/reactifact/eval/dataset.py +144 -0
  26. reactifact-0.14.0/reactifact/eval/evaluators.py +208 -0
  27. reactifact-0.14.0/reactifact/eval/judge.py +223 -0
  28. reactifact-0.14.0/reactifact/eval/online.py +435 -0
  29. reactifact-0.14.0/reactifact/eval/runner.py +322 -0
  30. reactifact-0.12.0/reactifact/eval.py → reactifact-0.14.0/reactifact/eval/scoring.py +88 -5
  31. reactifact-0.14.0/reactifact/events.py +77 -0
  32. reactifact-0.14.0/reactifact/guardrails.py +272 -0
  33. reactifact-0.14.0/reactifact/interrupt.py +54 -0
  34. reactifact-0.14.0/reactifact/logging.py +222 -0
  35. reactifact-0.14.0/reactifact/metrics.py +294 -0
  36. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/operations.py +18 -16
  37. reactifact-0.14.0/reactifact/pricing.py +33 -0
  38. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/anthropic.py +18 -1
  39. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/chat.py +15 -4
  40. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/contracts.py +4 -0
  41. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/fake.py +10 -2
  42. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/gemini.py +9 -0
  43. reactifact-0.14.0/reactifact/quota.py +215 -0
  44. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/__init__.py +27 -0
  45. reactifact-0.14.0/reactifact/recipes/conversation.py +302 -0
  46. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/replay.py +7 -0
  47. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/resources.py +92 -4
  48. reactifact-0.14.0/reactifact/routing.py +94 -0
  49. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/runtime.py +492 -37
  50. reactifact-0.14.0/reactifact/skills/reactifact/SKILL.md +165 -0
  51. reactifact-0.14.0/reactifact/skills/reactifact/references/effects-and-state.md +102 -0
  52. reactifact-0.14.0/reactifact/skills/reactifact/references/trust-and-safety.md +96 -0
  53. reactifact-0.14.0/reactifact/skills/reactifact-agents/SKILL.md +156 -0
  54. reactifact-0.14.0/reactifact/skills/reactifact-agents/references/consume.md +109 -0
  55. reactifact-0.14.0/reactifact/skills/reactifact-agents/references/effects.md +82 -0
  56. reactifact-0.14.0/reactifact/skills/reactifact-eval/SKILL.md +160 -0
  57. reactifact-0.14.0/reactifact/skills/reactifact-eval/references/online.md +72 -0
  58. reactifact-0.14.0/reactifact/skills/reactifact-from-langchain/SKILL.md +102 -0
  59. reactifact-0.14.0/reactifact/skills/reactifact-llm/SKILL.md +159 -0
  60. reactifact-0.14.0/reactifact/skills/reactifact-llm/references/providers.md +77 -0
  61. reactifact-0.14.0/reactifact/skills/reactifact-llm/references/tools.md +83 -0
  62. reactifact-0.14.0/reactifact/skills/reactifact-observability/SKILL.md +154 -0
  63. reactifact-0.14.0/reactifact/skills/reactifact-rag/SKILL.md +150 -0
  64. reactifact-0.14.0/reactifact/skills/reactifact-rag/references/sources.md +103 -0
  65. reactifact-0.14.0/reactifact/skills/reactifact-testing/SKILL.md +114 -0
  66. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/__init__.py +13 -2
  67. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/assertions.py +116 -0
  68. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/fault.py +26 -7
  69. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/golden.py +71 -1
  70. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/lab.py +250 -12
  71. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/mock.py +80 -25
  72. reactifact-0.14.0/reactifact/testing/pytest_plugin.py +69 -0
  73. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tool_use.py +29 -3
  74. reactifact-0.14.0/reactifact/types.py +168 -0
  75. {reactifact-0.12.0 → reactifact-0.14.0/reactifact.egg-info}/PKG-INFO +62 -31
  76. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact.egg-info/SOURCES.txt +70 -1
  77. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact.egg-info/requires.txt +3 -0
  78. reactifact-0.14.0/tests/test_a2a.py +347 -0
  79. reactifact-0.14.0/tests/test_a2a_example.py +16 -0
  80. reactifact-0.14.0/tests/test_a2a_interop.py +94 -0
  81. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_anthropic_provider.py +31 -0
  82. reactifact-0.14.0/tests/test_authz.py +120 -0
  83. reactifact-0.14.0/tests/test_cache.py +171 -0
  84. reactifact-0.14.0/tests/test_chat_example.py +18 -0
  85. reactifact-0.14.0/tests/test_chat_memory.py +142 -0
  86. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_cli.py +2 -0
  87. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_cli_scenario.py +24 -6
  88. reactifact-0.14.0/tests/test_compaction.py +153 -0
  89. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_consumes_produces.py +1 -1
  90. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_devops.py +81 -1
  91. reactifact-0.14.0/tests/test_devops_online.py +114 -0
  92. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_devops_web.py +15 -0
  93. reactifact-0.14.0/tests/test_eval_dataset.py +293 -0
  94. reactifact-0.14.0/tests/test_eval_judge.py +131 -0
  95. reactifact-0.14.0/tests/test_eval_online.py +319 -0
  96. reactifact-0.14.0/tests/test_eval_trajectory.py +140 -0
  97. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_gemini_provider.py +27 -0
  98. reactifact-0.14.0/tests/test_guardrails.py +138 -0
  99. reactifact-0.14.0/tests/test_logging.py +147 -0
  100. reactifact-0.14.0/tests/test_metrics.py +207 -0
  101. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_openai_provider.py +40 -0
  102. reactifact-0.14.0/tests/test_outbox.py +65 -0
  103. reactifact-0.14.0/tests/test_pending_actions.py +256 -0
  104. reactifact-0.14.0/tests/test_public_api.py +285 -0
  105. reactifact-0.14.0/tests/test_quota.py +214 -0
  106. reactifact-0.14.0/tests/test_recipe_conversation.py +154 -0
  107. reactifact-0.14.0/tests/test_routing.py +160 -0
  108. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_runtime_errors.py +86 -0
  109. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_sessions.py +68 -3
  110. reactifact-0.14.0/tests/test_shutdown.py +200 -0
  111. reactifact-0.14.0/tests/test_skills.py +106 -0
  112. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_starter_app.py +5 -0
  113. reactifact-0.14.0/tests/test_testing_golden.py +135 -0
  114. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_testing_lab.py +98 -0
  115. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_testing_mock.py +74 -0
  116. reactifact-0.14.0/tests/test_testing_provenance.py +91 -0
  117. reactifact-0.14.0/tests/test_testing_pytest_plugin.py +48 -0
  118. reactifact-0.14.0/tests/test_token_budget.py +182 -0
  119. reactifact-0.14.0/tests/test_type_registry.py +178 -0
  120. reactifact-0.12.0/reactifact/budget.py +0 -43
  121. reactifact-0.12.0/reactifact/cli/scenario.py +0 -94
  122. reactifact-0.12.0/reactifact/events.py +0 -34
  123. reactifact-0.12.0/reactifact/interrupt.py +0 -22
  124. {reactifact-0.12.0 → reactifact-0.14.0}/LICENSE +0 -0
  125. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/__main__.py +0 -0
  126. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/_extras.py +0 -0
  127. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/_httpx.py +0 -0
  128. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/agent_tool.py +0 -0
  129. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/agents.py +0 -0
  130. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/audit.py +0 -0
  131. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/checkpoints.py +0 -0
  132. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/branch.py +0 -0
  133. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/common.py +0 -0
  134. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/context.py +0 -0
  135. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/graph.py +0 -0
  136. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/cli/trace.py +0 -0
  137. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/commit.py +0 -0
  138. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/consume.py +0 -0
  139. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/context_builder.py +0 -0
  140. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/llm_agent.py +0 -0
  141. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/mcp/__init__.py +0 -0
  142. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/mcp/client.py +0 -0
  143. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/mcp/oauth.py +0 -0
  144. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/mcp/server.py +0 -0
  145. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/native_tool_use.py +0 -0
  146. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/patches.py +0 -0
  147. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/produce.py +0 -0
  148. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/prompts.py +0 -0
  149. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/__init__.py +0 -0
  150. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/_retry.py +0 -0
  151. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/azure.py +0 -0
  152. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/cerebras.py +0 -0
  153. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/deepseek.py +0 -0
  154. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/fireworks.py +0 -0
  155. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/github_models.py +0 -0
  156. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/groq.py +0 -0
  157. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/image.py +0 -0
  158. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/mistral.py +0 -0
  159. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/nvidia.py +0 -0
  160. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/ollama.py +0 -0
  161. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/openai.py +0 -0
  162. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/openrouter.py +0 -0
  163. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/perplexity.py +0 -0
  164. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/qwen.py +0 -0
  165. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/speech.py +0 -0
  166. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/together.py +0 -0
  167. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/video.py +0 -0
  168. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/xai.py +0 -0
  169. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/providers/zai.py +0 -0
  170. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/py.typed +0 -0
  171. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/__init__.py +0 -0
  172. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/_shared.py +0 -0
  173. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/agent.py +0 -0
  174. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/chat.py +0 -0
  175. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/models.py +0 -0
  176. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/rag.py +0 -0
  177. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/quick/tools_agent.py +0 -0
  178. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/cleanup.py +0 -0
  179. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/identity.py +0 -0
  180. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/inputs.py +0 -0
  181. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/memory.py +0 -0
  182. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/plan_execute.py +0 -0
  183. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/reflection.py +0 -0
  184. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/resolve.py +0 -0
  185. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/rollback.py +0 -0
  186. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/search.py +0 -0
  187. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/skills.py +0 -0
  188. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/status.py +0 -0
  189. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/supervisor.py +0 -0
  190. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/text.py +0 -0
  191. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/recipes/tool_loop.py +0 -0
  192. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/redaction.py +0 -0
  193. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/relations.py +0 -0
  194. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/request.py +0 -0
  195. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/scheduler.py +0 -0
  196. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/session.py +0 -0
  197. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/sources.py +0 -0
  198. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/streaming.py +0 -0
  199. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/structured.py +0 -0
  200. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/exceptions.py +0 -0
  201. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/record.py +0 -0
  202. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/testing/registry.py +0 -0
  203. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tools.py +0 -0
  204. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/__init__.py +0 -0
  205. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/_otlp.py +0 -0
  206. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/columns.py +0 -0
  207. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/langfuse.py +0 -0
  208. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/models.py +0 -0
  209. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/otlp.py +0 -0
  210. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/postgres.py +0 -0
  211. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/store.py +0 -0
  212. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/templates/app.css +0 -0
  213. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/templates/sessions.html +0 -0
  214. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/templates/ui.html +0 -0
  215. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/templates/ui_run.html +0 -0
  216. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/tracer.py +0 -0
  217. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/tracing/web.py +0 -0
  218. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/triggers.py +0 -0
  219. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/verify.py +0 -0
  220. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/viz.py +0 -0
  221. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact/web.py +0 -0
  222. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact.egg-info/dependency_links.txt +0 -0
  223. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact.egg-info/entry_points.txt +0 -0
  224. {reactifact-0.12.0 → reactifact-0.14.0}/reactifact.egg-info/top_level.txt +0 -0
  225. {reactifact-0.12.0 → reactifact-0.14.0}/setup.cfg +0 -0
  226. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_absent_consume.py +0 -0
  227. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_adaptive.py +0 -0
  228. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_agent_tool.py +0 -0
  229. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_artifacts.py +0 -0
  230. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_audit.py +0 -0
  231. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_backbone.py +0 -0
  232. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_branching.py +0 -0
  233. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_budget.py +0 -0
  234. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_canonical_ports.py +0 -0
  235. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_chat_web.py +0 -0
  236. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_checkpoint.py +0 -0
  237. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_checkpoints_concurrency.py +0 -0
  238. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_commit_log.py +0 -0
  239. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_concurrency.py +0 -0
  240. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_context_builder.py +0 -0
  241. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_correlated_consume.py +0 -0
  242. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_debounce.py +0 -0
  243. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_determinism.py +0 -0
  244. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_effects.py +0 -0
  245. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_eval.py +0 -0
  246. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_fintech_audit.py +0 -0
  247. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_forklab.py +0 -0
  248. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_forklab_web.py +0 -0
  249. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_friendly_api.py +0 -0
  250. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_golden.py +0 -0
  251. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_hitl.py +0 -0
  252. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_image_provider.py +0 -0
  253. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_incident_commander.py +0 -0
  254. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_invalidation.py +0 -0
  255. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_join_consume.py +0 -0
  256. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_knowledge.py +0 -0
  257. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_knowledge_web.py +0 -0
  258. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_llm_ladder.py +0 -0
  259. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_loop_bound_client.py +0 -0
  260. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_mcp.py +0 -0
  261. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_medic_lab.py +0 -0
  262. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_medic_lab_web.py +0 -0
  263. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_multisource.py +0 -0
  264. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_native_tool_use.py +0 -0
  265. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_patches.py +0 -0
  266. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_ports.py +0 -0
  267. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_produce_styles.py +0 -0
  268. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_produce_trigger.py +0 -0
  269. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_prompts.py +0 -0
  270. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_provider_auth.py +0 -0
  271. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_provider_retry.py +0 -0
  272. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_providers_integration.py +0 -0
  273. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_quick.py +0 -0
  274. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_reacts_to.py +0 -0
  275. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipe_plan_execute.py +0 -0
  276. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipe_reflection.py +0 -0
  277. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipe_supervisor.py +0 -0
  278. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipe_tool_loop.py +0 -0
  279. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipes.py +0 -0
  280. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipes_inputs.py +0 -0
  281. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipes_memory.py +0 -0
  282. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_recipes_skills.py +0 -0
  283. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_redaction.py +0 -0
  284. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_relation_graph.py +0 -0
  285. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_relations.py +0 -0
  286. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_repair.py +0 -0
  287. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_repair_web.py +0 -0
  288. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_replay.py +0 -0
  289. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_repo_agent.py +0 -0
  290. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_request.py +0 -0
  291. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_research.py +0 -0
  292. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_resources.py +0 -0
  293. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_retry.py +0 -0
  294. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_runtime.py +0 -0
  295. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_scheduler_semantics.py +0 -0
  296. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_sources.py +0 -0
  297. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_sources_search.py +0 -0
  298. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_sources_vector.py +0 -0
  299. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_speech_provider.py +0 -0
  300. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_streaming.py +0 -0
  301. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_structured.py +0 -0
  302. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_support_copilot.py +0 -0
  303. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_testing_assertions.py +0 -0
  304. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_testing_registry.py +0 -0
  305. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_tools.py +0 -0
  306. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_trace_columns.py +0 -0
  307. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_trace_tags.py +0 -0
  308. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_tracing.py +0 -0
  309. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_vendor_factories.py +0 -0
  310. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_vendor_multimodal_factories.py +0 -0
  311. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_verify.py +0 -0
  312. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_video_provider.py +0 -0
  313. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_view.py +0 -0
  314. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_viz.py +0 -0
  315. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_web_source.py +0 -0
  316. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_workspace.py +0 -0
  317. {reactifact-0.12.0 → reactifact-0.14.0}/tests/test_workspace_with_sources.py +0 -0
  318. {reactifact-0.12.0 → reactifact-0.14.0}/tests/tests_checkpoints_sqlite.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reactifact
3
- Version: 0.12.0
3
+ Version: 0.14.0
4
4
  Summary: Reactive, artifact-driven agent runtime: agents transform versioned, typed, provenance-aware artifacts inside an evolving context
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/bzdvdn/reactifact
@@ -36,6 +36,8 @@ Provides-Extra: pg
36
36
  Requires-Dist: psycopg[binary]>=3.2; extra == "pg"
37
37
  Provides-Extra: mcp
38
38
  Requires-Dist: mcp>=2.2; extra == "mcp"
39
+ Provides-Extra: a2a
40
+ Requires-Dist: a2a-sdk>=1.0; extra == "a2a"
39
41
  Provides-Extra: docs
40
42
  Requires-Dist: mkdocs>=1.6; extra == "docs"
41
43
  Requires-Dist: mkdocs-material>=9.5; extra == "docs"
@@ -46,7 +48,7 @@ Dynamic: license-file
46
48
  <img src="docs/img/reactifact-hero.png" alt="reactifact — Agents that react to artifacts, not graphs" width="800">
47
49
  </p>
48
50
 
49
- **Event-driven agents for Python developers — tasks wake on typed artifacts, like Celery tasks wake on messages. No graph to draw.**
51
+ **Auditable agents for Python.** Build **knowledge assistants** over docs, spreadsheets and the web — where the path per question isn't a graph you can draw. Every answer is computed, versioned and reproducible — not a string you have to trust.
50
52
 
51
53
  [![CI](https://github.com/bzdvdn/reactifact/actions/workflows/ci.yml/badge.svg)](https://github.com/bzdvdn/reactifact/actions/workflows/ci.yml)
52
54
  [![codecov](https://codecov.io/gh/bzdvdn/reactifact/graph/badge.svg)](https://codecov.io/gh/bzdvdn/reactifact)
@@ -56,29 +58,15 @@ Dynamic: license-file
56
58
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bzdvdn/reactifact)
57
59
  [![Docs](https://img.shields.io/badge/docs-bzdvdn.github.io%2Freactifact-blue)](https://bzdvdn.github.io/reactifact/)
58
60
 
59
- Python developers already know this model from Celery: define a **task**,
60
- declare what triggers it, let the runtime run it. reactifact applies it to agents — a task
61
- reacts to a **typed, versioned artifact** appearing in the context, not to a
62
- queue message you push or a graph edge you draw. The runtime derives what runs
63
- next from state.
61
+ > **Is:** a Python library (3 core deps) · single-process · typed, versioned artifacts with provenance · deterministic replay · runs offline, no API key
62
+ > **Isn't:** a managed platform · a distributed task queue · a pre-built agent/tool marketplace
64
63
 
65
- | Celery | reactifact |
66
- | --- | --- |
67
- | a task | `@produce(Model)` — a unit of work that writes an artifact |
68
- | `delay()` / `apply_async()` | you don't call it: creating the input artifact **is** the trigger |
69
- | routing key / queue | `Consume(Type)` — which artifact type wakes the task |
70
- | chain / group / chord | several `consumes` / `produces`; the runtime derives the order |
71
- | retries, `acks_late` | guards + `Budget`, an honest `None` instead of a wrong result |
72
- | result backend | the `Context` — typed, versioned artifacts |
73
- | worker | `Runtime` |
64
+ A question like *"why did infra costs jump in Q2?"* needs a spreadsheet, a policy doc and a page off the web — and the *next* question needs a different subset. That is not one graph you can draw up front. You declare what artifacts exist and what agents can do with them; the runtime derives the order from state.
74
65
 
75
- Single process today (no broker, no worker pool) — the *model* is Celery-shaped,
76
- not its distributed runtime.
77
-
78
- On top of that model you get something a task queue doesn't: every artifact is
79
- **versioned with provenance**, so a run is reproducible (`context_hash`) and
80
- auditable (`audit.report`) for free. The model reasons; the arithmetic stays
81
- deterministic; every claim carries provenance.
66
+ Every derived artifact then links to what produced it
67
+ (`Answer —supported_by→ Evidence —extracted_from→ Doc`), so *"why did it say
68
+ that?"* is a query, not a guess — and the run is reproducible (`context_hash`)
69
+ and verifiable (`reactifact replay --verify`).
82
70
 
83
71
  ## On top: a provable answer
84
72
 
@@ -130,6 +118,14 @@ print(report_to_markdown(build_report(ctx, answer))) # hash per artifact + edge
130
118
  print(context_hash(ctx)) # reproducible fingerprint
131
119
  ```
132
120
 
121
+ ## Knowledge assistants, live
122
+
123
+ The same shape over many sources, not a fixed path — the
124
+ [`knowledge`](examples/knowledge) CLI answers a harder, multi-source question
125
+ (docs + a CSV) with a real computed number and its sources, no LLM key required:
126
+
127
+ ![CLI demo: asking "how much does gpu cost in total?" — the runtime searches docs and a spreadsheet, computes the sum, verifies it, and answers with sources.](docs/img/knowledge-cli-demo.gif)
128
+
133
129
  ![Left: a hand-wired fetch → verify → answer pipeline. Right: reactifact — search_agent and answer_agent each declare only what they consume and produce, wired together by Context, never each other.](docs/img/wiring.svg)
134
130
 
135
131
  Two agents explore independently on their own forks and merge back automatically
@@ -137,6 +133,31 @@ Two agents explore independently on their own forks and merge back automatically
137
133
 
138
134
  ![forklab demo: two strategies (depth/breadth) investigate on separate forks and merge cleanly; a second run edits the same artifact on both forks and reactifact raises MergeConflict instead of guessing, then re-merges under an explicit policy.](docs/img/forklab-demo.gif)
139
135
 
136
+ <details>
137
+ <summary><strong>If you know Celery…</strong> — the programming model, not its distributed runtime</summary>
138
+
139
+ Python developers already know this model from Celery: define a **task**,
140
+ declare what triggers it, let the runtime run it. reactifact applies it to
141
+ agents — a task reacts to a **typed, versioned artifact** appearing in the
142
+ context, not to a queue message you push or a graph edge you draw. The runtime
143
+ derives what runs next from state.
144
+
145
+ | Celery | reactifact |
146
+ | --- | --- |
147
+ | a task | `@produce(Model)` — a unit of work that writes an artifact |
148
+ | `delay()` / `apply_async()` | you don't call it: creating the input artifact **is** the trigger |
149
+ | routing key / queue | `Consume(Type)` — which artifact type wakes the task |
150
+ | chain / group / chord | several `consumes` / `produces`; the runtime derives the order |
151
+ | result backend | the `Context` — typed, versioned artifacts |
152
+ | worker | `Runtime` |
153
+
154
+ **Not inherited:** a broker, a worker pool, `acks_late`/redelivery, cross-process
155
+ exactly-once. reactifact is a library with an in-process runtime — the *model* is
156
+ Celery-shaped, the deployment is not. (Session-backed runs do survive a restart;
157
+ see [Durability & resume](docs/en/durability.md).)
158
+
159
+ </details>
160
+
140
161
  ```bash
141
162
  pip install reactifact
142
163
  ```
@@ -203,12 +224,6 @@ print(answer.data.text) # "Refunds are available within 14 d
203
224
  print("supported_by:", evidence.data.text) # provenance you can trace, not just a string in a log
204
225
  ```
205
226
 
206
- The same idea, live — the [`knowledge`](examples/knowledge) example's CLI answering a
207
- harder, multi-source question (docs + a CSV) with a real computed number and
208
- its sources, no LLM key required:
209
-
210
- ![CLI demo: asking "how much does gpu cost in total?" — the runtime searches docs and a spreadsheet, computes the sum, verifies it, and answers with sources.](docs/img/knowledge-cli-demo.gif)
211
-
212
227
  ## How it works
213
228
 
214
229
  ```text
@@ -276,13 +291,19 @@ by hand. Run it yourself: `uv run python -m examples.ledger.main`.
276
291
  - **Provenance** — every derived artifact links to what produced it
277
292
  (`Answer —supported_by→ Claim —derived_from→ Evidence —extracted_from→ Doc`).
278
293
  - **HITL** — humans as `effects.ask(...)` → `PendingQuestion`, answered via `effects.resume(...)` like any agent.
294
+ - **Outbox** — outbound side effects as `effects.act(...)` → `PendingAction`, performed once *after* the commit by `Runtime(dispatcher=...)`; safe across replay, retries and merged branches.
279
295
 
280
296
  ## In the box
281
297
 
282
298
  - **Deterministic by design** — calculations over structured data, honest `None`
283
299
  fallbacks instead of hallucinated answers; the model reasons, never "knows".
284
300
  - **Observability** — every run traces agent spans, reads/writes, LLM calls,
285
- tokens: SQLite store + web dashboard, exportable to Langfuse/Postgres (async sinks).
301
+ tokens: SQLite store + web dashboard, exportable to Langfuse/Postgres (async sinks);
302
+ **correlated logs** (`configure_logging()`, silent until called) carry
303
+ `run_id`/`session_id`/`agent` on every line.
304
+ - **Lifecycle** — `Budget.max_seconds` is a hard deadline (a hung LLM call is
305
+ cancelled); `request_stop()`/`ashutdown()` stop at a generation boundary
306
+ (`RunOutcome.STOPPED`); `active_runs()`/`in_flight` give a readiness view.
286
307
  - **MCP, both ways** — call any MCP server's tools as a `Tool`
287
308
  (`mcp_stdio_tools`/`mcp_http_tools`), or expose your own `Tool`s and a live
288
309
  `Context` as an MCP server (`create_mcp_server`) for Claude Desktop, Claude
@@ -313,7 +334,7 @@ uv run python ./examples/devops/web.py # HITL ops assistant + trace dash
313
334
  ```
314
335
 
315
336
  Classic-pattern ports run as one-liners too:
316
- `python -m examples.{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute,adaptive,ledger}.main`.
337
+ `python -m examples.{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute,adaptive,ledger,outbox}.main`.
317
338
 
318
339
  Prefer a notebook? [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/bzdvdn/reactifact/blob/master/examples/quickstart.ipynb)
319
340
  — `examples/quickstart.ipynb` covers the four `quick` cases (`agent`/`rag`/`tools_agent`/`chat_agent`), offline by default.
@@ -325,8 +346,10 @@ Prefer a notebook? [![Open in Colab](https://colab.research.google.com/assets/co
325
346
  - `medic-lab` — hypothesis laboratory: competing hypotheses scored, HITL steering, honest report.
326
347
  - `devops` — HITL tool agents + LLM tool router + trace dashboard.
327
348
  - `repair` — budget-aware replanning (chat/data in Russian by design).
349
+ - `chat` — minimal offline single-model multi-turn conversation (`Conversation` + bounded `ChatMemory`).
328
350
  - `forklab` — deterministic branch & merge: two strategies on their own forks, three-way merge.
329
351
  - `ledger` — offline proof of reactive recompute: edit one fact, only its real `Consume`rs re-run.
352
+ - `outbox` — one intent, one send: `effects.act(...)` + `Runtime(dispatcher=...)`, safe across replays, retries and merged branches (§42).
330
353
  - `llm_ladder` — the workflow from one LLM call to state-changing patches (3 levels).
331
354
  - `adaptive` — hybrid scheduler: rule filters + deterministic rank + LLM tie-break + `rank_limit`.
332
355
  - `{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute}` — canonical ports (see [port-matrix](docs/en/port-matrix.md)).
@@ -355,6 +378,14 @@ uv sync --extra dev --extra web
355
378
  .venv/bin/ruff check
356
379
  ```
357
380
 
381
+ ## Status & contributing
382
+
383
+ Active, `0.14.x`, pre-1.0 — the API is stabilizing and `1.0` will freeze it
384
+ (see the [roadmap](docs/roadmap.md)); releases follow
385
+ [Keep a Changelog](CHANGELOG.md). Questions and design discussion live in
386
+ [GitHub Discussions](https://github.com/bzdvdn/reactifact/discussions); a star,
387
+ or a `good first issue`, helps more than you'd think.
388
+
358
389
  ## License
359
390
 
360
391
  MIT — see [LICENSE](LICENSE).
@@ -2,7 +2,7 @@
2
2
  <img src="docs/img/reactifact-hero.png" alt="reactifact — Agents that react to artifacts, not graphs" width="800">
3
3
  </p>
4
4
 
5
- **Event-driven agents for Python developers — tasks wake on typed artifacts, like Celery tasks wake on messages. No graph to draw.**
5
+ **Auditable agents for Python.** Build **knowledge assistants** over docs, spreadsheets and the web — where the path per question isn't a graph you can draw. Every answer is computed, versioned and reproducible — not a string you have to trust.
6
6
 
7
7
  [![CI](https://github.com/bzdvdn/reactifact/actions/workflows/ci.yml/badge.svg)](https://github.com/bzdvdn/reactifact/actions/workflows/ci.yml)
8
8
  [![codecov](https://codecov.io/gh/bzdvdn/reactifact/graph/badge.svg)](https://codecov.io/gh/bzdvdn/reactifact)
@@ -12,29 +12,15 @@
12
12
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bzdvdn/reactifact)
13
13
  [![Docs](https://img.shields.io/badge/docs-bzdvdn.github.io%2Freactifact-blue)](https://bzdvdn.github.io/reactifact/)
14
14
 
15
- Python developers already know this model from Celery: define a **task**,
16
- declare what triggers it, let the runtime run it. reactifact applies it to agents — a task
17
- reacts to a **typed, versioned artifact** appearing in the context, not to a
18
- queue message you push or a graph edge you draw. The runtime derives what runs
19
- next from state.
15
+ > **Is:** a Python library (3 core deps) · single-process · typed, versioned artifacts with provenance · deterministic replay · runs offline, no API key
16
+ > **Isn't:** a managed platform · a distributed task queue · a pre-built agent/tool marketplace
20
17
 
21
- | Celery | reactifact |
22
- | --- | --- |
23
- | a task | `@produce(Model)` — a unit of work that writes an artifact |
24
- | `delay()` / `apply_async()` | you don't call it: creating the input artifact **is** the trigger |
25
- | routing key / queue | `Consume(Type)` — which artifact type wakes the task |
26
- | chain / group / chord | several `consumes` / `produces`; the runtime derives the order |
27
- | retries, `acks_late` | guards + `Budget`, an honest `None` instead of a wrong result |
28
- | result backend | the `Context` — typed, versioned artifacts |
29
- | worker | `Runtime` |
18
+ A question like *"why did infra costs jump in Q2?"* needs a spreadsheet, a policy doc and a page off the web — and the *next* question needs a different subset. That is not one graph you can draw up front. You declare what artifacts exist and what agents can do with them; the runtime derives the order from state.
30
19
 
31
- Single process today (no broker, no worker pool) — the *model* is Celery-shaped,
32
- not its distributed runtime.
33
-
34
- On top of that model you get something a task queue doesn't: every artifact is
35
- **versioned with provenance**, so a run is reproducible (`context_hash`) and
36
- auditable (`audit.report`) for free. The model reasons; the arithmetic stays
37
- deterministic; every claim carries provenance.
20
+ Every derived artifact then links to what produced it
21
+ (`Answer —supported_by→ Evidence —extracted_from→ Doc`), so *"why did it say
22
+ that?"* is a query, not a guess — and the run is reproducible (`context_hash`)
23
+ and verifiable (`reactifact replay --verify`).
38
24
 
39
25
  ## On top: a provable answer
40
26
 
@@ -86,6 +72,14 @@ print(report_to_markdown(build_report(ctx, answer))) # hash per artifact + edge
86
72
  print(context_hash(ctx)) # reproducible fingerprint
87
73
  ```
88
74
 
75
+ ## Knowledge assistants, live
76
+
77
+ The same shape over many sources, not a fixed path — the
78
+ [`knowledge`](examples/knowledge) CLI answers a harder, multi-source question
79
+ (docs + a CSV) with a real computed number and its sources, no LLM key required:
80
+
81
+ ![CLI demo: asking "how much does gpu cost in total?" — the runtime searches docs and a spreadsheet, computes the sum, verifies it, and answers with sources.](docs/img/knowledge-cli-demo.gif)
82
+
89
83
  ![Left: a hand-wired fetch → verify → answer pipeline. Right: reactifact — search_agent and answer_agent each declare only what they consume and produce, wired together by Context, never each other.](docs/img/wiring.svg)
90
84
 
91
85
  Two agents explore independently on their own forks and merge back automatically
@@ -93,6 +87,31 @@ Two agents explore independently on their own forks and merge back automatically
93
87
 
94
88
  ![forklab demo: two strategies (depth/breadth) investigate on separate forks and merge cleanly; a second run edits the same artifact on both forks and reactifact raises MergeConflict instead of guessing, then re-merges under an explicit policy.](docs/img/forklab-demo.gif)
95
89
 
90
+ <details>
91
+ <summary><strong>If you know Celery…</strong> — the programming model, not its distributed runtime</summary>
92
+
93
+ Python developers already know this model from Celery: define a **task**,
94
+ declare what triggers it, let the runtime run it. reactifact applies it to
95
+ agents — a task reacts to a **typed, versioned artifact** appearing in the
96
+ context, not to a queue message you push or a graph edge you draw. The runtime
97
+ derives what runs next from state.
98
+
99
+ | Celery | reactifact |
100
+ | --- | --- |
101
+ | a task | `@produce(Model)` — a unit of work that writes an artifact |
102
+ | `delay()` / `apply_async()` | you don't call it: creating the input artifact **is** the trigger |
103
+ | routing key / queue | `Consume(Type)` — which artifact type wakes the task |
104
+ | chain / group / chord | several `consumes` / `produces`; the runtime derives the order |
105
+ | result backend | the `Context` — typed, versioned artifacts |
106
+ | worker | `Runtime` |
107
+
108
+ **Not inherited:** a broker, a worker pool, `acks_late`/redelivery, cross-process
109
+ exactly-once. reactifact is a library with an in-process runtime — the *model* is
110
+ Celery-shaped, the deployment is not. (Session-backed runs do survive a restart;
111
+ see [Durability & resume](docs/en/durability.md).)
112
+
113
+ </details>
114
+
96
115
  ```bash
97
116
  pip install reactifact
98
117
  ```
@@ -159,12 +178,6 @@ print(answer.data.text) # "Refunds are available within 14 d
159
178
  print("supported_by:", evidence.data.text) # provenance you can trace, not just a string in a log
160
179
  ```
161
180
 
162
- The same idea, live — the [`knowledge`](examples/knowledge) example's CLI answering a
163
- harder, multi-source question (docs + a CSV) with a real computed number and
164
- its sources, no LLM key required:
165
-
166
- ![CLI demo: asking "how much does gpu cost in total?" — the runtime searches docs and a spreadsheet, computes the sum, verifies it, and answers with sources.](docs/img/knowledge-cli-demo.gif)
167
-
168
181
  ## How it works
169
182
 
170
183
  ```text
@@ -232,13 +245,19 @@ by hand. Run it yourself: `uv run python -m examples.ledger.main`.
232
245
  - **Provenance** — every derived artifact links to what produced it
233
246
  (`Answer —supported_by→ Claim —derived_from→ Evidence —extracted_from→ Doc`).
234
247
  - **HITL** — humans as `effects.ask(...)` → `PendingQuestion`, answered via `effects.resume(...)` like any agent.
248
+ - **Outbox** — outbound side effects as `effects.act(...)` → `PendingAction`, performed once *after* the commit by `Runtime(dispatcher=...)`; safe across replay, retries and merged branches.
235
249
 
236
250
  ## In the box
237
251
 
238
252
  - **Deterministic by design** — calculations over structured data, honest `None`
239
253
  fallbacks instead of hallucinated answers; the model reasons, never "knows".
240
254
  - **Observability** — every run traces agent spans, reads/writes, LLM calls,
241
- tokens: SQLite store + web dashboard, exportable to Langfuse/Postgres (async sinks).
255
+ tokens: SQLite store + web dashboard, exportable to Langfuse/Postgres (async sinks);
256
+ **correlated logs** (`configure_logging()`, silent until called) carry
257
+ `run_id`/`session_id`/`agent` on every line.
258
+ - **Lifecycle** — `Budget.max_seconds` is a hard deadline (a hung LLM call is
259
+ cancelled); `request_stop()`/`ashutdown()` stop at a generation boundary
260
+ (`RunOutcome.STOPPED`); `active_runs()`/`in_flight` give a readiness view.
242
261
  - **MCP, both ways** — call any MCP server's tools as a `Tool`
243
262
  (`mcp_stdio_tools`/`mcp_http_tools`), or expose your own `Tool`s and a live
244
263
  `Context` as an MCP server (`create_mcp_server`) for Claude Desktop, Claude
@@ -269,7 +288,7 @@ uv run python ./examples/devops/web.py # HITL ops assistant + trace dash
269
288
  ```
270
289
 
271
290
  Classic-pattern ports run as one-liners too:
272
- `python -m examples.{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute,adaptive,ledger}.main`.
291
+ `python -m examples.{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute,adaptive,ledger,outbox}.main`.
273
292
 
274
293
  Prefer a notebook? [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/bzdvdn/reactifact/blob/master/examples/quickstart.ipynb)
275
294
  — `examples/quickstart.ipynb` covers the four `quick` cases (`agent`/`rag`/`tools_agent`/`chat_agent`), offline by default.
@@ -281,8 +300,10 @@ Prefer a notebook? [![Open in Colab](https://colab.research.google.com/assets/co
281
300
  - `medic-lab` — hypothesis laboratory: competing hypotheses scored, HITL steering, honest report.
282
301
  - `devops` — HITL tool agents + LLM tool router + trace dashboard.
283
302
  - `repair` — budget-aware replanning (chat/data in Russian by design).
303
+ - `chat` — minimal offline single-model multi-turn conversation (`Conversation` + bounded `ChatMemory`).
284
304
  - `forklab` — deterministic branch & merge: two strategies on their own forks, three-way merge.
285
305
  - `ledger` — offline proof of reactive recompute: edit one fact, only its real `Consume`rs re-run.
306
+ - `outbox` — one intent, one send: `effects.act(...)` + `Runtime(dispatcher=...)`, safe across replays, retries and merged branches (§42).
286
307
  - `llm_ladder` — the workflow from one LLM call to state-changing patches (3 levels).
287
308
  - `adaptive` — hybrid scheduler: rule filters + deterministic rank + LLM tie-break + `rank_limit`.
288
309
  - `{reflection,map_reduce,supervisor,summarize,time_travel,plan_execute}` — canonical ports (see [port-matrix](docs/en/port-matrix.md)).
@@ -311,6 +332,14 @@ uv sync --extra dev --extra web
311
332
  .venv/bin/ruff check
312
333
  ```
313
334
 
335
+ ## Status & contributing
336
+
337
+ Active, `0.14.x`, pre-1.0 — the API is stabilizing and `1.0` will freeze it
338
+ (see the [roadmap](docs/roadmap.md)); releases follow
339
+ [Keep a Changelog](CHANGELOG.md). Questions and design discussion live in
340
+ [GitHub Discussions](https://github.com/bzdvdn/reactifact/discussions); a star,
341
+ or a `good first issue`, helps more than you'd think.
342
+
314
343
  ## License
315
344
 
316
345
  MIT — see [LICENSE](LICENSE).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "reactifact"
7
- version = "0.12.0"
7
+ version = "0.14.0"
8
8
  description = "Reactive, artifact-driven agent runtime: agents transform versioned, typed, provenance-aware artifacts inside an evolving context"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -52,6 +52,9 @@ pg = [
52
52
  mcp = [
53
53
  "mcp>=2.2",
54
54
  ]
55
+ a2a = [
56
+ "a2a-sdk>=1.0",
57
+ ]
55
58
  docs = [
56
59
  "mkdocs>=1.6",
57
60
  "mkdocs-material>=9.5",
@@ -81,6 +84,9 @@ pg = [
81
84
  mcp = [
82
85
  "mcp>=2.2",
83
86
  ]
87
+ a2a = [
88
+ "a2a-sdk>=1.0",
89
+ ]
84
90
  docs = [
85
91
  "mkdocs>=1.6",
86
92
  "mkdocs-material>=9.5",
@@ -99,7 +105,7 @@ reactifact = "reactifact.__main__:main"
99
105
  packages = { find = { where = ["."], include = ["reactifact*"], exclude = ["examples*", "tests*"] } }
100
106
 
101
107
  [tool.setuptools.package-data]
102
- reactifact = ["py.typed"]
108
+ reactifact = ["py.typed", "skills/*/SKILL.md", "skills/*/references/*.md"]
103
109
  "reactifact.tracing" = ["templates/*.html", "templates/*.css"]
104
110
 
105
111
  [tool.pytest.ini_options]
@@ -152,3 +158,9 @@ python_version = "3.11"
152
158
  packages = ["reactifact", "examples"]
153
159
  strict = true
154
160
  warn_unused_ignores = true
161
+
162
+ # pyright defaults its target to the interpreter it finds, which can resolve
163
+ # below the 3.11 floor this package requires — making `StrEnum`/`datetime.UTC`
164
+ # "unknown import symbol" and cascading into false positives on `RunOutcome`.
165
+ [tool.pyright]
166
+ pythonVersion = "3.11"
@@ -24,12 +24,13 @@ of the names you see by default even though they were always cheap to import
24
24
  from .agents import Agent, create_agent
25
25
  from .artifacts import Artifact
26
26
  from .branching import MergeConflict
27
- from .budget import Budget, RunOutcome, RunStats
27
+ from .budget import Budget, BudgetTracker, RunOutcome, RunStats
28
28
  from .consume import Consume, consume
29
29
  from .context import Context, View
30
30
  from .effects import Effects, Handle
31
31
  from .events import Event, EventType
32
- from .interrupt import PendingQuestion
32
+ from .interrupt import PendingAction, PendingQuestion
33
+ from .logging import bind, configure_logging, get_logger
33
34
  from .patches import Create, Delete, Link, Patch, Relation, Unlink, Update
34
35
  from .produce import Produce, ProduceCall, produce
35
36
  from .providers import (
@@ -48,13 +49,15 @@ from .runtime import Runtime
48
49
  from .session import Session, SessionStore
49
50
  from .tools import FunctionTool, Tool, ToolOutput, tool
50
51
  from .triggers import Trigger
52
+ from .types import register as register_type
51
53
 
52
- __version__ = "0.12.0"
54
+ __version__ = "0.14.0"
53
55
 
54
56
  __all__ = [
55
57
  "Agent",
56
58
  "Artifact",
57
59
  "Budget",
60
+ "BudgetTracker",
58
61
  "Consume",
59
62
  "Context",
60
63
  "Create",
@@ -71,6 +74,7 @@ __all__ = [
71
74
  "Message",
72
75
  "MergeConflict",
73
76
  "Patch",
77
+ "PendingAction",
74
78
  "PendingQuestion",
75
79
  "Produce",
76
80
  "ProduceCall",
@@ -93,9 +97,13 @@ __all__ = [
93
97
  "Unlink",
94
98
  "Update",
95
99
  "View",
100
+ "bind",
101
+ "configure_logging",
96
102
  "consume",
97
103
  "create_agent",
98
104
  "current_request",
105
+ "get_logger",
99
106
  "produce",
107
+ "register_type",
100
108
  "tool",
101
109
  ]
@@ -0,0 +1,77 @@
1
+ """A2A (Agent2Agent) integration: call remote agents, or serve reactifact ones.
2
+
3
+ No extra dependency: the client uses `httpx` (core), the server uses FastAPI
4
+ from the existing `web` extra and imports it lazily — so importing this package
5
+ never needs FastAPI.
6
+
7
+ - Client (`reactifact.a2a.client`): `A2AClient` (Agent Card + `message/send`,
8
+ `message/stream`, `tasks/get`, `tasks/cancel`) and `A2AAgentTool`/`a2a_tool`,
9
+ a reactifact `Tool` that delegates to a remote agent.
10
+ - Server (`reactifact.a2a.server`): `create_a2a_router(agents, ...)` — an Agent
11
+ Card plus a JSON-RPC endpoint where a `Task` is a conversation and a HITL
12
+ `PendingQuestion` maps to `input-required`.
13
+
14
+ The JSON-RPC binding implements the A2A `0.3` wire shape (camelCase fields,
15
+ `kind`-discriminated parts).
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from .client import (
21
+ AGENT_CARD_PATH,
22
+ LEGACY_AGENT_CARD_PATH,
23
+ A2AAgentTool,
24
+ A2AClient,
25
+ A2AError,
26
+ a2a_tool,
27
+ )
28
+ from .models import (
29
+ AgentCapabilities,
30
+ AgentCard,
31
+ AgentProvider,
32
+ AgentSkill,
33
+ Artifact,
34
+ ErrorCode,
35
+ JSONRPCError,
36
+ JSONRPCRequest,
37
+ JSONRPCResponse,
38
+ Message,
39
+ MessageSendParams,
40
+ Part,
41
+ Task,
42
+ TaskState,
43
+ TaskStatus,
44
+ UserMessage,
45
+ )
46
+ from .remote import A2ARemoteProduce, remote_agent
47
+ from .server import create_a2a_router, default_create_message, default_reply
48
+
49
+ __all__ = [
50
+ "AGENT_CARD_PATH",
51
+ "LEGACY_AGENT_CARD_PATH",
52
+ "A2AAgentTool",
53
+ "A2AClient",
54
+ "A2AError",
55
+ "A2ARemoteProduce",
56
+ "AgentCapabilities",
57
+ "AgentCard",
58
+ "AgentProvider",
59
+ "AgentSkill",
60
+ "Artifact",
61
+ "ErrorCode",
62
+ "JSONRPCError",
63
+ "JSONRPCRequest",
64
+ "JSONRPCResponse",
65
+ "Message",
66
+ "MessageSendParams",
67
+ "Part",
68
+ "Task",
69
+ "TaskState",
70
+ "TaskStatus",
71
+ "UserMessage",
72
+ "a2a_tool",
73
+ "create_a2a_router",
74
+ "default_create_message",
75
+ "default_reply",
76
+ "remote_agent",
77
+ ]