reactifact 0.11.1__tar.gz → 0.11.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. {reactifact-0.11.1/reactifact.egg-info → reactifact-0.11.2}/PKG-INFO +1 -1
  2. {reactifact-0.11.1 → reactifact-0.11.2}/pyproject.toml +1 -1
  3. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/__init__.py +1 -1
  4. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/checkpoints.py +56 -27
  5. {reactifact-0.11.1 → reactifact-0.11.2/reactifact.egg-info}/PKG-INFO +1 -1
  6. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_checkpoint.py +43 -0
  7. {reactifact-0.11.1 → reactifact-0.11.2}/LICENSE +0 -0
  8. {reactifact-0.11.1 → reactifact-0.11.2}/README.md +0 -0
  9. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/__main__.py +0 -0
  10. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/_extras.py +0 -0
  11. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/_httpx.py +0 -0
  12. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/agent_tool.py +0 -0
  13. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/agents.py +0 -0
  14. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/artifacts.py +0 -0
  15. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/audit.py +0 -0
  16. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/branching.py +0 -0
  17. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/budget.py +0 -0
  18. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/chat.py +0 -0
  19. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/cli/__init__.py +0 -0
  20. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/cli/branch.py +0 -0
  21. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/cli/common.py +0 -0
  22. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/cli/context.py +0 -0
  23. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/cli/graph.py +0 -0
  24. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/cli/replay.py +0 -0
  25. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/cli/scenario.py +0 -0
  26. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/cli/trace.py +0 -0
  27. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/commit.py +0 -0
  28. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/commit_log.py +0 -0
  29. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/consume.py +0 -0
  30. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/context.py +0 -0
  31. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/context_builder.py +0 -0
  32. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/effects.py +0 -0
  33. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/eval.py +0 -0
  34. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/events.py +0 -0
  35. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/interrupt.py +0 -0
  36. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/llm_agent.py +0 -0
  37. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/mcp/__init__.py +0 -0
  38. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/mcp/client.py +0 -0
  39. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/mcp/oauth.py +0 -0
  40. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/mcp/server.py +0 -0
  41. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/native_tool_use.py +0 -0
  42. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/operations.py +0 -0
  43. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/patches.py +0 -0
  44. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/produce.py +0 -0
  45. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/prompts.py +0 -0
  46. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/__init__.py +0 -0
  47. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/_retry.py +0 -0
  48. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/anthropic.py +0 -0
  49. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/azure.py +0 -0
  50. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/cerebras.py +0 -0
  51. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/chat.py +0 -0
  52. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/contracts.py +0 -0
  53. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/deepseek.py +0 -0
  54. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/fake.py +0 -0
  55. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/fireworks.py +0 -0
  56. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/gemini.py +0 -0
  57. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/github_models.py +0 -0
  58. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/groq.py +0 -0
  59. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/image.py +0 -0
  60. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/mistral.py +0 -0
  61. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/nvidia.py +0 -0
  62. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/ollama.py +0 -0
  63. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/openai.py +0 -0
  64. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/openrouter.py +0 -0
  65. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/perplexity.py +0 -0
  66. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/qwen.py +0 -0
  67. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/speech.py +0 -0
  68. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/together.py +0 -0
  69. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/video.py +0 -0
  70. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/xai.py +0 -0
  71. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/providers/zai.py +0 -0
  72. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/py.typed +0 -0
  73. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/quick/__init__.py +0 -0
  74. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/quick/_shared.py +0 -0
  75. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/quick/agent.py +0 -0
  76. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/quick/chat.py +0 -0
  77. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/quick/models.py +0 -0
  78. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/quick/rag.py +0 -0
  79. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/quick/tools_agent.py +0 -0
  80. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/__init__.py +0 -0
  81. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/cleanup.py +0 -0
  82. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/identity.py +0 -0
  83. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/inputs.py +0 -0
  84. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/memory.py +0 -0
  85. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/plan_execute.py +0 -0
  86. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/reflection.py +0 -0
  87. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/resolve.py +0 -0
  88. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/rollback.py +0 -0
  89. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/search.py +0 -0
  90. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/skills.py +0 -0
  91. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/status.py +0 -0
  92. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/supervisor.py +0 -0
  93. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/text.py +0 -0
  94. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/recipes/tool_loop.py +0 -0
  95. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/redaction.py +0 -0
  96. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/relations.py +0 -0
  97. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/replay.py +0 -0
  98. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/request.py +0 -0
  99. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/resources.py +0 -0
  100. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/runtime.py +0 -0
  101. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/scheduler.py +0 -0
  102. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/session.py +0 -0
  103. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/sources.py +0 -0
  104. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/streaming.py +0 -0
  105. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/structured.py +0 -0
  106. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/testing/__init__.py +0 -0
  107. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/testing/assertions.py +0 -0
  108. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/testing/exceptions.py +0 -0
  109. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/testing/fault.py +0 -0
  110. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/testing/golden.py +0 -0
  111. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/testing/lab.py +0 -0
  112. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/testing/mock.py +0 -0
  113. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/testing/record.py +0 -0
  114. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/testing/registry.py +0 -0
  115. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tool_use.py +0 -0
  116. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tools.py +0 -0
  117. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/__init__.py +0 -0
  118. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/_otlp.py +0 -0
  119. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/langfuse.py +0 -0
  120. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/models.py +0 -0
  121. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/otlp.py +0 -0
  122. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/postgres.py +0 -0
  123. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/store.py +0 -0
  124. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/templates/app.css +0 -0
  125. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/templates/sessions.html +0 -0
  126. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/templates/ui.html +0 -0
  127. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/templates/ui_run.html +0 -0
  128. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/tracer.py +0 -0
  129. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/tracing/web.py +0 -0
  130. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/triggers.py +0 -0
  131. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/verify.py +0 -0
  132. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/viz.py +0 -0
  133. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact/web.py +0 -0
  134. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact.egg-info/SOURCES.txt +0 -0
  135. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact.egg-info/dependency_links.txt +0 -0
  136. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact.egg-info/entry_points.txt +0 -0
  137. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact.egg-info/requires.txt +0 -0
  138. {reactifact-0.11.1 → reactifact-0.11.2}/reactifact.egg-info/top_level.txt +0 -0
  139. {reactifact-0.11.1 → reactifact-0.11.2}/setup.cfg +0 -0
  140. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_absent_consume.py +0 -0
  141. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_adaptive.py +0 -0
  142. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_agent_tool.py +0 -0
  143. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_anthropic_provider.py +0 -0
  144. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_artifacts.py +0 -0
  145. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_audit.py +0 -0
  146. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_backbone.py +0 -0
  147. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_branching.py +0 -0
  148. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_budget.py +0 -0
  149. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_canonical_ports.py +0 -0
  150. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_chat_web.py +0 -0
  151. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_checkpoints_concurrency.py +0 -0
  152. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_cli.py +0 -0
  153. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_cli_scenario.py +0 -0
  154. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_commit_log.py +0 -0
  155. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_concurrency.py +0 -0
  156. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_consumes_produces.py +0 -0
  157. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_context_builder.py +0 -0
  158. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_correlated_consume.py +0 -0
  159. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_debounce.py +0 -0
  160. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_determinism.py +0 -0
  161. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_devops.py +0 -0
  162. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_devops_web.py +0 -0
  163. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_effects.py +0 -0
  164. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_eval.py +0 -0
  165. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_fintech_audit.py +0 -0
  166. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_forklab.py +0 -0
  167. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_forklab_web.py +0 -0
  168. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_friendly_api.py +0 -0
  169. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_gemini_provider.py +0 -0
  170. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_golden.py +0 -0
  171. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_hitl.py +0 -0
  172. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_image_provider.py +0 -0
  173. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_incident_commander.py +0 -0
  174. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_invalidation.py +0 -0
  175. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_join_consume.py +0 -0
  176. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_knowledge.py +0 -0
  177. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_knowledge_web.py +0 -0
  178. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_llm_ladder.py +0 -0
  179. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_loop_bound_client.py +0 -0
  180. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_mcp.py +0 -0
  181. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_medic_lab.py +0 -0
  182. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_medic_lab_web.py +0 -0
  183. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_multisource.py +0 -0
  184. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_native_tool_use.py +0 -0
  185. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_openai_provider.py +0 -0
  186. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_patches.py +0 -0
  187. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_ports.py +0 -0
  188. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_produce_styles.py +0 -0
  189. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_produce_trigger.py +0 -0
  190. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_prompts.py +0 -0
  191. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_provider_auth.py +0 -0
  192. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_provider_retry.py +0 -0
  193. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_providers_integration.py +0 -0
  194. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_quick.py +0 -0
  195. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_reacts_to.py +0 -0
  196. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_recipe_plan_execute.py +0 -0
  197. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_recipe_reflection.py +0 -0
  198. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_recipe_supervisor.py +0 -0
  199. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_recipe_tool_loop.py +0 -0
  200. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_recipes.py +0 -0
  201. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_recipes_inputs.py +0 -0
  202. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_recipes_memory.py +0 -0
  203. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_recipes_skills.py +0 -0
  204. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_redaction.py +0 -0
  205. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_relation_graph.py +0 -0
  206. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_relations.py +0 -0
  207. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_repair.py +0 -0
  208. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_repair_web.py +0 -0
  209. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_replay.py +0 -0
  210. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_repo_agent.py +0 -0
  211. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_request.py +0 -0
  212. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_research.py +0 -0
  213. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_resources.py +0 -0
  214. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_retry.py +0 -0
  215. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_runtime.py +0 -0
  216. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_runtime_errors.py +0 -0
  217. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_scheduler_semantics.py +0 -0
  218. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_sessions.py +0 -0
  219. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_sources.py +0 -0
  220. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_sources_search.py +0 -0
  221. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_sources_vector.py +0 -0
  222. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_speech_provider.py +0 -0
  223. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_starter_app.py +0 -0
  224. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_streaming.py +0 -0
  225. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_structured.py +0 -0
  226. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_support_copilot.py +0 -0
  227. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_testing_assertions.py +0 -0
  228. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_testing_lab.py +0 -0
  229. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_testing_mock.py +0 -0
  230. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_testing_registry.py +0 -0
  231. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_tools.py +0 -0
  232. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_trace_tags.py +0 -0
  233. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_tracing.py +0 -0
  234. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_vendor_factories.py +0 -0
  235. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_vendor_multimodal_factories.py +0 -0
  236. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_verify.py +0 -0
  237. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_video_provider.py +0 -0
  238. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_view.py +0 -0
  239. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_viz.py +0 -0
  240. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_web_source.py +0 -0
  241. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_workspace.py +0 -0
  242. {reactifact-0.11.1 → reactifact-0.11.2}/tests/test_workspace_with_sources.py +0 -0
  243. {reactifact-0.11.1 → reactifact-0.11.2}/tests/tests_checkpoints_sqlite.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reactifact
3
- Version: 0.11.1
3
+ Version: 0.11.2
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "reactifact"
7
- version = "0.11.1"
7
+ version = "0.11.2"
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"
@@ -49,7 +49,7 @@ from .session import Session, SessionStore
49
49
  from .tools import FunctionTool, Tool, ToolOutput, tool
50
50
  from .triggers import Trigger
51
51
 
52
- __version__ = "0.11.1"
52
+ __version__ = "0.11.2"
53
53
 
54
54
  __all__ = [
55
55
  "Agent",
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import contextlib
4
5
  import copy
5
6
  import json
6
7
  import sqlite3
@@ -301,6 +302,12 @@ class PostgreSQLKVBackend(KVBackend):
301
302
  enough for session-checkpoint traffic (small, infrequent writes); reach for
302
303
  `psycopg_pool.AsyncConnectionPool` yourself if you need many concurrent
303
304
  writers sharing one DSN.
305
+
306
+ The connection **self-heals**: a query cancelled mid-flight (a client that
307
+ drops a streaming turn) or any protocol error leaves the shared libpq
308
+ connection busy/aborted, and a single poisoned connection would otherwise
309
+ fail every later session read/write. Each statement reconnects and retries
310
+ once, so one bad query cannot wedge the whole store.
304
311
  """
305
312
 
306
313
  def __init__(self, dsn: str):
@@ -324,42 +331,64 @@ class PostgreSQLKVBackend(KVBackend):
324
331
  self._conn = conn
325
332
  return conn
326
333
 
327
- async def set(self, key: str, data: dict[str, Any]) -> None:
334
+ async def _reset(self) -> None:
335
+ """Drops the cached connection so the next statement reconnects.
336
+
337
+ Called after any error: a connection that raised is never trusted again
338
+ (it may be mid-command or in an aborted transaction).
339
+ """
340
+ conn, self._conn = self._conn, None
341
+ if conn is not None and not conn.closed:
342
+ with contextlib.suppress(Exception):
343
+ await conn.close()
344
+
345
+ async def _run(
346
+ self,
347
+ sql: str,
348
+ params: tuple[Any, ...] = (),
349
+ *,
350
+ fetch: str | None = None,
351
+ ) -> Any:
352
+ """Runs one statement under the lock, reconnecting once on failure.
353
+
354
+ All `kv_entries` statements are idempotent (upsert/delete/select), so a
355
+ retry after a dropped connection is safe.
356
+ """
328
357
  async with self._lock:
329
- conn = await self._connection()
330
- async with conn.cursor() as cur:
331
- await cur.execute(
332
- "INSERT INTO kv_entries (key, data) VALUES (%s, %s) "
333
- "ON CONFLICT (key) DO UPDATE SET data = EXCLUDED.data",
334
- (key, json.dumps(data)),
335
- )
336
- await conn.commit()
358
+ for attempt in (0, 1):
359
+ try:
360
+ conn = await self._connection()
361
+ async with conn.cursor() as cur:
362
+ await cur.execute(sql, params)
363
+ result = await getattr(cur, fetch)() if fetch else None
364
+ await conn.commit()
365
+ return result
366
+ except self._psycopg.Error:
367
+ await self._reset()
368
+ if attempt:
369
+ raise
370
+ raise AssertionError("unreachable") # pragma: no cover
371
+
372
+ async def set(self, key: str, data: dict[str, Any]) -> None:
373
+ await self._run(
374
+ "INSERT INTO kv_entries (key, data) VALUES (%s, %s) "
375
+ "ON CONFLICT (key) DO UPDATE SET data = EXCLUDED.data",
376
+ (key, json.dumps(data)),
377
+ )
337
378
 
338
379
  async def get(self, key: str) -> dict[str, Any] | None:
339
- async with self._lock:
340
- conn = await self._connection()
341
- async with conn.cursor() as cur:
342
- await cur.execute("SELECT data FROM kv_entries WHERE key = %s", (key,))
343
- row = await cur.fetchone()
380
+ row = await self._run(
381
+ "SELECT data FROM kv_entries WHERE key = %s", (key,), fetch="fetchone"
382
+ )
344
383
  return cast(dict[str, Any], json.loads(row[0])) if row is not None else None
345
384
 
346
385
  async def delete(self, key: str) -> None:
347
- async with self._lock:
348
- conn = await self._connection()
349
- async with conn.cursor() as cur:
350
- await cur.execute("DELETE FROM kv_entries WHERE key = %s", (key,))
351
- await conn.commit()
386
+ await self._run("DELETE FROM kv_entries WHERE key = %s", (key,))
352
387
 
353
388
  async def keys(self) -> list[str]:
354
- async with self._lock:
355
- conn = await self._connection()
356
- async with conn.cursor() as cur:
357
- await cur.execute("SELECT key FROM kv_entries")
358
- rows = await cur.fetchall()
389
+ rows = await self._run("SELECT key FROM kv_entries", fetch="fetchall")
359
390
  return [cast(str, r[0]) for r in rows]
360
391
 
361
392
  async def aclose(self) -> None:
362
393
  async with self._lock:
363
- if self._conn is not None and not self._conn.closed:
364
- await self._conn.close()
365
- self._conn = None
394
+ await self._reset()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reactifact
3
- Version: 0.11.1
3
+ Version: 0.11.2
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
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import contextlib
2
3
  import os
3
4
  import tempfile
4
5
 
@@ -109,6 +110,48 @@ async def _test_postgres_kv_roundtrip(dsn: str) -> None:
109
110
  await backend.aclose()
110
111
 
111
112
 
113
+ def test_postgres_kv_self_heals_after_a_cancelled_query():
114
+ """A poisoned shared connection must not wedge the store.
115
+
116
+ A query cancelled mid-flight (a client that drops a streaming turn) leaves
117
+ libpq busy/aborted; before the fix every later session read/write failed
118
+ forever with ``InFailedSqlTransaction`` / "another command is already in
119
+ progress". The backend must reconnect and retry instead.
120
+ """
121
+ dsn = os.environ.get("TEST_PG_DSN")
122
+ if not dsn:
123
+ pytest.skip("TEST_PG_DSN not set — skipping Postgres KV integration")
124
+ import psycopg # noqa: F401 (ensure the extra is installed)
125
+
126
+ asyncio.run(_test_postgres_kv_self_heal(dsn))
127
+
128
+
129
+ async def _test_postgres_kv_self_heal(dsn: str) -> None:
130
+ from reactifact.checkpoints import PostgreSQLKVBackend
131
+
132
+ backend = PostgreSQLKVBackend(dsn)
133
+ try:
134
+ await backend.set("heal", {"v": 1})
135
+ conn = await backend._connection()
136
+
137
+ async def slow() -> None:
138
+ async with conn.cursor() as cur:
139
+ await cur.execute("SELECT pg_sleep(10)")
140
+
141
+ task = asyncio.create_task(slow())
142
+ await asyncio.sleep(0.3)
143
+ task.cancel()
144
+ with contextlib.suppress(asyncio.CancelledError):
145
+ await task
146
+
147
+ assert await backend.get("heal") == {"v": 1}
148
+ assert backend._conn is not conn # reconnected, not the poisoned one
149
+ await backend.delete("heal")
150
+ assert await backend.get("heal") is None
151
+ finally:
152
+ await backend.aclose()
153
+
154
+
112
155
  def test_require_extra_readable_error():
113
156
  """Missing extra dependency → a hint, not a bare ModuleNotFoundError."""
114
157
  from unittest import mock
File without changes
File without changes
File without changes