AbstractRuntime 0.4.9__tar.gz → 0.4.10__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 (297) hide show
  1. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/CHANGELOG.md +9 -0
  2. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/PKG-INFO +11 -11
  3. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/README.md +3 -3
  4. abstractruntime-0.4.10/docs/backlog/proposed/2026-05-09_runtime_retention_and_purge_contract.md +82 -0
  5. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/pyproject.toml +8 -8
  6. abstractruntime-0.4.10/release-notes.md +6 -0
  7. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/compiler.py +73 -1
  8. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/visual/executor.py +213 -0
  9. abstractruntime-0.4.10/tests/test_visualflow_media_nodes.py +159 -0
  10. abstractruntime-0.4.9/release-notes.md +0 -9
  11. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/.github/workflows/ci.yml +0 -0
  12. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/.github/workflows/release.yml +0 -0
  13. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/.gitignore +0 -0
  14. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/ACKNOWLEDGMENTS.md +0 -0
  15. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/CONTRIBUTING.md +0 -0
  16. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/LICENSE +0 -0
  17. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/ROADMAP.md +0 -0
  18. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/SECURITY.md +0 -0
  19. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/README.md +0 -0
  20. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/adr/0001_layered_coupling_with_abstractcore.md +0 -0
  21. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/adr/0002_execution_modes_local_remote_hybrid.md +0 -0
  22. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/adr/0003_provenance_tamper_evident_hash_chain.md +0 -0
  23. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/adr/README.md +0 -0
  24. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/api.md +0 -0
  25. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/architecture.md +0 -0
  26. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/README.md +0 -0
  27. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/001_runtime_kernel.md +0 -0
  28. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/002_persistence_and_ledger.md +0 -0
  29. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/003_wait_primitives.md +0 -0
  30. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/004_scheduler_driver.md +0 -0
  31. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/005_abstractcore_integration.md +0 -0
  32. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/006_snapshots_bookmarks.md +0 -0
  33. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/007_provenance_hash_chain.md +0 -0
  34. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/009_artifact_store.md +0 -0
  35. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/010_examples_and_composition.md +0 -0
  36. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/011_subworkflow_support.md +0 -0
  37. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/012_run_store_query_and_scheduler_support.md +0 -0
  38. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/013_effect_retries_and_idempotency.md +0 -0
  39. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/completed/016_runtime_aware_parameters.md +0 -0
  40. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/001_integrations_abstractcore.md +0 -0
  41. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/001_runtime_kernel.md +0 -0
  42. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/002_persistence_and_ledger.md +0 -0
  43. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/002_snapshots_bookmarks.md +0 -0
  44. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/003_provenance_ledger_chain.md +0 -0
  45. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/003_wait_resume_and_scheduler.md +0 -0
  46. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/004_effect_handlers_and_integrations.md +0 -0
  47. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/004_tests.md +0 -0
  48. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/005_docs_updates.md +0 -0
  49. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/005_examples_and_composition.md +0 -0
  50. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/006_ai_fingerprint_and_provenance.md +0 -0
  51. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/DEPRECATED_README.md +0 -0
  52. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/README.md +0 -0
  53. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/deprecated/abstractruntime_docs_final_02a7373b.plan.md +0 -0
  54. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/planned/008_signatures_and_keys.md +0 -0
  55. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/planned/014_remote_tool_worker_executor.md +0 -0
  56. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/planned/015_agent_integration_improvements.md +0 -0
  57. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/planned/017_limit_warnings_and_observability.md +0 -0
  58. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/planned/018_workspace_access_policy_for_media_and_tools.md +0 -0
  59. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/proposed/2026-05-08_runtime_gateway_env_namespace_cleanup.md +0 -0
  60. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/backlog/proposed/2026-05-08_runtime_gateway_install_boundary.md +0 -0
  61. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/evidence.md +0 -0
  62. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/faq.md +0 -0
  63. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/getting-started.md +0 -0
  64. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/integrations/abstractcore.md +0 -0
  65. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/limits.md +0 -0
  66. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/manual_testing.md +0 -0
  67. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/mcp-worker.md +0 -0
  68. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/proposal.md +0 -0
  69. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/provenance.md +0 -0
  70. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/snapshots.md +0 -0
  71. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/tools-comms.md +0 -0
  72. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/docs/workflow-bundles.md +0 -0
  73. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/emails.config.example.yaml +0 -0
  74. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/examples/01_hello_world.py +0 -0
  75. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/examples/02_ask_user.py +0 -0
  76. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/examples/03_wait_until.py +0 -0
  77. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/examples/04_multi_step.py +0 -0
  78. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/examples/05_persistence.py +0 -0
  79. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/examples/06_llm_integration.py +0 -0
  80. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/examples/07_react_agent.py +0 -0
  81. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/examples/README.md +0 -0
  82. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/llms-full.txt +0 -0
  83. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/llms.txt +0 -0
  84. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/mkdocs.yml +0 -0
  85. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/pytest.ini +0 -0
  86. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/__init__.py +0 -0
  87. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/core/__init__.py +0 -0
  88. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/core/config.py +0 -0
  89. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/core/event_keys.py +0 -0
  90. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/core/models.py +0 -0
  91. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/core/policy.py +0 -0
  92. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/core/runtime.py +0 -0
  93. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/core/spec.py +0 -0
  94. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/core/vars.py +0 -0
  95. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/evidence/__init__.py +0 -0
  96. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/evidence/recorder.py +0 -0
  97. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/history_bundle.py +0 -0
  98. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/identity/__init__.py +0 -0
  99. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/identity/fingerprint.py +0 -0
  100. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/__init__.py +0 -0
  101. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/__init__.py +0 -0
  102. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/constants.py +0 -0
  103. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/default_tools.py +0 -0
  104. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/effect_handlers.py +0 -0
  105. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/embeddings_client.py +0 -0
  106. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/factory.py +0 -0
  107. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/llm_client.py +0 -0
  108. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/logging.py +0 -0
  109. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/mcp_worker.py +0 -0
  110. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/observability.py +0 -0
  111. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/output_specs.py +0 -0
  112. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/session_attachments.py +0 -0
  113. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/summarizer.py +0 -0
  114. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/tool_executor.py +0 -0
  115. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractcore/workspace_scoped_tools.py +0 -0
  116. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractmemory/__init__.py +0 -0
  117. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/integrations/abstractmemory/effect_handlers.py +0 -0
  118. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/memory/__init__.py +0 -0
  119. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/memory/active_context.py +0 -0
  120. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/memory/active_memory.py +0 -0
  121. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/memory/compaction.py +0 -0
  122. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/memory/kg_packets.py +0 -0
  123. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/memory/memact_composer.py +0 -0
  124. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/memory/recall_levels.py +0 -0
  125. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/memory/token_budget.py +0 -0
  126. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/rendering/__init__.py +0 -0
  127. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/rendering/agent_trace_report.py +0 -0
  128. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/rendering/json_stringify.py +0 -0
  129. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/scheduler/__init__.py +0 -0
  130. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/scheduler/convenience.py +0 -0
  131. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/scheduler/registry.py +0 -0
  132. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/scheduler/scheduler.py +0 -0
  133. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/storage/__init__.py +0 -0
  134. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/storage/artifacts.py +0 -0
  135. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/storage/base.py +0 -0
  136. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/storage/commands.py +0 -0
  137. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/storage/in_memory.py +0 -0
  138. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/storage/json_files.py +0 -0
  139. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/storage/ledger_chain.py +0 -0
  140. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/storage/observable.py +0 -0
  141. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/storage/offloading.py +0 -0
  142. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/storage/snapshots.py +0 -0
  143. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/storage/sqlite.py +0 -0
  144. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/__init__.py +0 -0
  145. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/adapters/__init__.py +0 -0
  146. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/adapters/agent_adapter.py +0 -0
  147. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/adapters/context_adapter.py +0 -0
  148. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/adapters/control_adapter.py +0 -0
  149. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/adapters/effect_adapter.py +0 -0
  150. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/adapters/event_adapter.py +0 -0
  151. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/adapters/function_adapter.py +0 -0
  152. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/adapters/memact_adapter.py +0 -0
  153. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/adapters/subflow_adapter.py +0 -0
  154. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/adapters/variable_adapter.py +0 -0
  155. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/flow.py +0 -0
  156. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/visual/__init__.py +0 -0
  157. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/visual/agent_ids.py +0 -0
  158. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/visual/builtins.py +0 -0
  159. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/visual/code_executor.py +0 -0
  160. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/visual/models.py +0 -0
  161. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/visualflow_compiler/visual/multi_entry_lowering.py +0 -0
  162. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/workflow_bundle/__init__.py +0 -0
  163. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/workflow_bundle/models.py +0 -0
  164. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/workflow_bundle/packer.py +0 -0
  165. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/workflow_bundle/reader.py +0 -0
  166. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/src/abstractruntime/workflow_bundle/registry.py +0 -0
  167. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/README.md +0 -0
  168. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/conftest.py +0 -0
  169. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_active_context_policy.py +0 -0
  170. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_active_memory.py +0 -0
  171. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_answer_user_effect.py +0 -0
  172. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_artifacts.py +0 -0
  173. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_chat_summarizer_integration.py +0 -0
  174. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_command_store.py +0 -0
  175. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_compaction_helpers.py +0 -0
  176. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_control_adapter_while.py +0 -0
  177. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_default_tools_comms_gating.py +0 -0
  178. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_default_tools_include_skim_files.py +0 -0
  179. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_default_tools_include_skim_folders.py +0 -0
  180. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_default_tools_search_files_executor.py +0 -0
  181. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_durable_toolsets.py +0 -0
  182. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_e2e_tool_calls_idempotency_lmstudio.py +0 -0
  183. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_emit_event_without_workflow_registry.py +0 -0
  184. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_evidence_recorder.py +0 -0
  185. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_factory_timeouts_default_to_abstractcore_config.py +0 -0
  186. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_integration_abstractcore.py +0 -0
  187. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_integrations_abstractcore.py +0 -0
  188. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_json_file_run_store_children_index.py +0 -0
  189. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_jsonl_ledger_recovery.py +0 -0
  190. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_kg_learn_and_recall_contract.py +0 -0
  191. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_ledger_chain.py +0 -0
  192. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_ledger_subscription.py +0 -0
  193. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_call_media_artifact_refs.py +0 -0
  194. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_call_media_artifact_refs_persist_across_restart.py +0 -0
  195. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_call_media_uses_source_path_label.py +0 -0
  196. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_call_requires_prompt.py +0 -0
  197. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_call_response_schema_normalization.py +0 -0
  198. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_call_structured_output_fallback.py +0 -0
  199. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_call_truncation_retry_contract.py +0 -0
  200. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_call_use_context_appends_turn.py +0 -0
  201. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_call_verbatim_payload_capture.py +0 -0
  202. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_client_media_artifacts.py +0 -0
  203. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_client_system_context.py +0 -0
  204. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_llm_client_tool_call_parsing.py +0 -0
  205. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_local_runtime_timeout_kwarg_policy.py +0 -0
  206. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_mcp_remote_tool_executor.py +0 -0
  207. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_mcp_worker_logging.py +0 -0
  208. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_mcp_worker_security.py +0 -0
  209. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_mcp_worker_stdio.py +0 -0
  210. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memact_composer_from_kg_result.py +0 -0
  211. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memory_kg_assert_attributes_defaults.py +0 -0
  212. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memory_kg_packets.py +0 -0
  213. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memory_kg_predicate_aliasing.py +0 -0
  214. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memory_kg_query_packetization_restart.py +0 -0
  215. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memory_kg_query_recall_level_policy.py +0 -0
  216. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memory_kg_semantic_query_ranking.py +0 -0
  217. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memory_note_effect.py +0 -0
  218. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memory_query_effect.py +0 -0
  219. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memory_query_rich_filters.py +0 -0
  220. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memory_scope_and_rehydrate_effect.py +0 -0
  221. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_memory_tag_effect.py +0 -0
  222. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_mlx_generation_serialization.py +0 -0
  223. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_multimodal_abstractcore_integration.py +0 -0
  224. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_offloading.py +0 -0
  225. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_packaging_extras.py +0 -0
  226. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_pause_resume.py +0 -0
  227. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_prompt_cache_modules.py +0 -0
  228. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_queryable_run_store.py +0 -0
  229. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_read_file_fallback_to_session_attachments.py +0 -0
  230. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_real_integration.py +0 -0
  231. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_recall_levels_policy.py +0 -0
  232. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_remote_llm_client.py +0 -0
  233. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_rendering_agent_trace_report.py +0 -0
  234. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_rendering_json_stringify.py +0 -0
  235. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_retry_idempotency.py +0 -0
  236. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_run_history_bundle.py +0 -0
  237. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_runtime_config_max_output_tokens_fallback.py +0 -0
  238. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_runtime_install_boundary.py +0 -0
  239. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_runtime_llm_call_grounding_in_ledger.py +0 -0
  240. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_runtime_node_traces.py +0 -0
  241. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_runtime_start_seeds_tool_support.py +0 -0
  242. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_scheduler.py +0 -0
  243. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_session_attachments_registry_and_open_tool.py +0 -0
  244. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_snapshots.py +0 -0
  245. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_sqlite_ledger_store.py +0 -0
  246. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_start_subworkflow_async_wait.py +0 -0
  247. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_start_subworkflow_inherit_context_merges_messages.py +0 -0
  248. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_start_subworkflow_workspace_inheritance.py +0 -0
  249. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_structured_output_schema_enum.py +0 -0
  250. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_subworkflow.py +0 -0
  251. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_terminal_effect_completion.py +0 -0
  252. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_terminal_resume_appends_ledger_completion.py +0 -0
  253. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_tick_completion_includes_output_in_ledger.py +0 -0
  254. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_tool_approval_executor.py +0 -0
  255. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_tool_approval_resume_executes.py +0 -0
  256. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_tool_calls_idempotency_keys.py +0 -0
  257. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_tool_executor_argument_sanitization.py +0 -0
  258. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_tool_executor_error_output_detection.py +0 -0
  259. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_tool_executor_filename_alias.py +0 -0
  260. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_tool_executor_kwarg_canonicalization.py +0 -0
  261. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_tool_executor_read_file_aliases.py +0 -0
  262. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_tool_executor_timeout.py +0 -0
  263. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_tool_wait_allowlist_safety.py +0 -0
  264. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_trace_context_propagation.py +0 -0
  265. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_vars_query_effect.py +0 -0
  266. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visual_agent_output_context_includes_messages.py +0 -0
  267. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visual_agent_tool_observations_persist_across_restart.py +0 -0
  268. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visual_agent_use_context_inherits_attachments.py +0 -0
  269. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visual_agent_use_context_persists_tool_observations.py +0 -0
  270. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visual_agent_use_context_persists_turn.py +0 -0
  271. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visual_llm_call_schema_ref_resolution.py +0 -0
  272. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visual_memact_compose_node.py +0 -0
  273. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visual_memory_kg_query_outputs_propagate.py +0 -0
  274. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visual_memory_kg_resolve_outputs_propagate.py +0 -0
  275. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visual_multi_entry_loop_overrides.py +0 -0
  276. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_add_message_builtin.py +0 -0
  277. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_add_message_node_appends_to_active_context.py +0 -0
  278. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_call_tool_node.py +0 -0
  279. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_compiler_basic.py +0 -0
  280. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_context_and_builder_nodes.py +0 -0
  281. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_get_element_node.py +0 -0
  282. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_get_random_element_node.py +0 -0
  283. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_has_tools_builtin.py +0 -0
  284. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_llm_call_context_attachments_map_to_media.py +0 -0
  285. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_llm_call_multimodal_output.py +0 -0
  286. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_make_object_node.py +0 -0
  287. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_memory_effect_nodes.py +0 -0
  288. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_memory_source_pins.py +0 -0
  289. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_prompt_only.py +0 -0
  290. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_random_nodes.py +0 -0
  291. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_string_contains_replace.py +0 -0
  292. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_visualflow_tool_parameters_node.py +0 -0
  293. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_wait_event_prompt_metadata.py +0 -0
  294. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_workflow_bundle_registry.py +0 -0
  295. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_workspace_policy_allowlist_mode.py +0 -0
  296. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_workspace_policy_mount_virtual_paths.py +0 -0
  297. {abstractruntime-0.4.9 → abstractruntime-0.4.10}/tests/test_workspace_policy_tool_calls_persist_across_restart.py +0 -0
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.10] - 2026-05-12
11
+
12
+ ### Fixed
13
+ - Generated media VisualFlow nodes now keep media model selection in the output spec and reserve LLM `provider`/`model` routing for explicit `runtime_provider`/`runtime_model` overrides.
14
+ - Legacy `provider`/`model` pins on image, TTS, and STT media nodes remain accepted as media selector fallbacks for existing flows.
15
+
16
+ ### Changed
17
+ - Minimum AbstractCore optional dependency floor is now `abstractcore>=2.13.13` so generated media and audio catalog contracts stay aligned.
18
+
10
19
  ## [0.4.9] - 2026-05-09
11
20
 
12
21
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbstractRuntime
3
- Version: 0.4.9
3
+ Version: 0.4.10
4
4
  Summary: AbstractRuntime: a durable graph runner designed to pair with AbstractCore.
5
5
  Project-URL: AbstractCore (website), https://www.abstractcore.ai/
6
6
  Project-URL: AbstractRuntime (GitHub), https://github.com/lpalbou/abstractruntime
@@ -23,23 +23,23 @@ Requires-Python: >=3.10
23
23
  Requires-Dist: abstractmemory>=0.2.6
24
24
  Requires-Dist: abstractsemantics>=0.0.3
25
25
  Provides-Extra: abstractcore
26
- Requires-Dist: abstractcore>=2.13.12; extra == 'abstractcore'
26
+ Requires-Dist: abstractcore>=2.13.13; extra == 'abstractcore'
27
27
  Provides-Extra: all-apple
28
- Requires-Dist: abstractcore[all-apple]>=2.13.12; extra == 'all-apple'
28
+ Requires-Dist: abstractcore[all-apple]>=2.13.13; extra == 'all-apple'
29
29
  Provides-Extra: all-gpu
30
- Requires-Dist: abstractcore[all-gpu]>=2.13.12; extra == 'all-gpu'
30
+ Requires-Dist: abstractcore[all-gpu]>=2.13.13; extra == 'all-gpu'
31
31
  Provides-Extra: apple
32
- Requires-Dist: abstractcore[apple]>=2.13.12; extra == 'apple'
32
+ Requires-Dist: abstractcore[apple]>=2.13.13; extra == 'apple'
33
33
  Provides-Extra: docs
34
34
  Requires-Dist: mkdocs-material>=9.0.0; extra == 'docs'
35
35
  Requires-Dist: mkdocs>=1.6.0; extra == 'docs'
36
36
  Requires-Dist: pymdown-extensions>=10.0; extra == 'docs'
37
37
  Provides-Extra: gpu
38
- Requires-Dist: abstractcore[gpu]>=2.13.12; extra == 'gpu'
38
+ Requires-Dist: abstractcore[gpu]>=2.13.13; extra == 'gpu'
39
39
  Provides-Extra: mcp-worker
40
- Requires-Dist: abstractcore[tools]>=2.13.12; extra == 'mcp-worker'
40
+ Requires-Dist: abstractcore[tools]>=2.13.13; extra == 'mcp-worker'
41
41
  Provides-Extra: multimodal
42
- Requires-Dist: abstractcore[audio,media,openai,vision,voice]>=2.13.12; extra == 'multimodal'
42
+ Requires-Dist: abstractcore[audio,media,openai,vision,voice]>=2.13.13; extra == 'multimodal'
43
43
  Provides-Extra: test
44
44
  Requires-Dist: pytest>=7.0.0; extra == 'test'
45
45
  Description-Content-Type: text/markdown
@@ -50,7 +50,7 @@ Description-Content-Type: text/markdown
50
50
 
51
51
  It is designed for long-running workflows that must survive restarts and explicitly model blocking (human input, timers, external events, subworkflows) without keeping Python stacks alive.
52
52
 
53
- **Version:** 0.4.9 • **Python:** 3.10+
53
+ **Version:** 0.4.10 • **Python:** 3.10+
54
54
 
55
55
  **Status:** pre-1.0 (API may evolve). For production use, pin versions and follow `CHANGELOG.md`.
56
56
 
@@ -84,7 +84,7 @@ AbstractCore integration (LLM + tools):
84
84
  pip install "abstractruntime[abstractcore]"
85
85
  ```
86
86
 
87
- The `abstractcore` extra installs AbstractCore 2.13.12 or newer so the hardened server auth model, provider-key header routing, generated-media contracts, capability catalog, prompt-cache control plane, current tool catalog, public output-selector contract, and async/sync text-generation output-selector parity are available. Use `abstractruntime[multimodal]` when you need common media, vision, voice, and audio dependencies.
87
+ The `abstractcore` extra installs AbstractCore 2.13.13 or newer so the hardened server auth model, provider-key header routing, generated-media contracts, capability catalog, prompt-cache control plane, current tool catalog, public output-selector contract, and async/sync text-generation output-selector parity are available. Use `abstractruntime[multimodal]` when you need common media, vision, voice, and audio dependencies.
88
88
 
89
89
  Hardware profile cascades are available for native Python installs:
90
90
  `abstractruntime[apple]`, `abstractruntime[gpu]`, `abstractruntime[all-apple]`,
@@ -138,7 +138,7 @@ state = rt.resume(
138
138
  assert state.status.value == "completed"
139
139
  ```
140
140
 
141
- ## What’s included (v0.4.9)
141
+ ## What’s included (v0.4.10)
142
142
 
143
143
  Kernel (dependency-light):
144
144
  - workflow graphs: `WorkflowSpec` (`src/abstractruntime/core/spec.py`)
@@ -4,7 +4,7 @@
4
4
 
5
5
  It is designed for long-running workflows that must survive restarts and explicitly model blocking (human input, timers, external events, subworkflows) without keeping Python stacks alive.
6
6
 
7
- **Version:** 0.4.9 • **Python:** 3.10+
7
+ **Version:** 0.4.10 • **Python:** 3.10+
8
8
 
9
9
  **Status:** pre-1.0 (API may evolve). For production use, pin versions and follow `CHANGELOG.md`.
10
10
 
@@ -38,7 +38,7 @@ AbstractCore integration (LLM + tools):
38
38
  pip install "abstractruntime[abstractcore]"
39
39
  ```
40
40
 
41
- The `abstractcore` extra installs AbstractCore 2.13.12 or newer so the hardened server auth model, provider-key header routing, generated-media contracts, capability catalog, prompt-cache control plane, current tool catalog, public output-selector contract, and async/sync text-generation output-selector parity are available. Use `abstractruntime[multimodal]` when you need common media, vision, voice, and audio dependencies.
41
+ The `abstractcore` extra installs AbstractCore 2.13.13 or newer so the hardened server auth model, provider-key header routing, generated-media contracts, capability catalog, prompt-cache control plane, current tool catalog, public output-selector contract, and async/sync text-generation output-selector parity are available. Use `abstractruntime[multimodal]` when you need common media, vision, voice, and audio dependencies.
42
42
 
43
43
  Hardware profile cascades are available for native Python installs:
44
44
  `abstractruntime[apple]`, `abstractruntime[gpu]`, `abstractruntime[all-apple]`,
@@ -92,7 +92,7 @@ state = rt.resume(
92
92
  assert state.status.value == "completed"
93
93
  ```
94
94
 
95
- ## What’s included (v0.4.9)
95
+ ## What’s included (v0.4.10)
96
96
 
97
97
  Kernel (dependency-light):
98
98
  - workflow graphs: `WorkflowSpec` (`src/abstractruntime/core/spec.py`)
@@ -0,0 +1,82 @@
1
+ # Proposed: Runtime Retention And Purge Contract
2
+
3
+ ## Metadata
4
+ - Created: 2026-05-09
5
+ - Status: Proposed
6
+ - Completed: N/A
7
+
8
+ ## Context
9
+
10
+ AbstractRuntime is the durable graph ledger. Durability is still the default, but hosts such as
11
+ AbstractGateway need a principled way to handle temporary/private runs created by authoring tools
12
+ like AbstractFlow.
13
+
14
+ Current Runtime reality:
15
+
16
+ - `RunStore` exposes `save()` and `load()`.
17
+ - `QueryableRunStore` exposes listing and child traversal.
18
+ - `LedgerStore` is append-only with `append()` and `list()`.
19
+ - `ArtifactStore` implementations already have delete helpers such as `delete_by_run()`.
20
+ - There is no generic retention class, purge protocol, or host-neutral cleanup helper for a run tree.
21
+
22
+ ## Why this might matter
23
+
24
+ Gateway can implement cleanup itself, but the concepts are generic:
25
+
26
+ - deleting or archiving a root run and all child/subworkflow runs;
27
+ - deleting ledgers and artifacts for a run tree;
28
+ - preserving append-only production ledgers while allowing explicitly temporary authoring runs to
29
+ expire;
30
+ - hiding private runs from default indexes.
31
+
32
+ If every host invents its own cleanup behavior, run history, artifacts, and child runs can drift.
33
+
34
+ ## Proposed direction
35
+
36
+ Add optional runtime-level retention/purge contracts:
37
+
38
+ - Run metadata convention:
39
+ - `run.vars["_runtime"]["purpose"]`;
40
+ - `run.vars["_runtime"]["visibility"]`;
41
+ - `run.vars["_runtime"]["retention"]`;
42
+ - `expires_at` or `ttl_s` where appropriate.
43
+ - Optional storage protocols:
44
+ - `DeletableRunStore.delete(run_id)`;
45
+ - `DeletableLedgerStore.delete(run_id)` or `delete_by_run(run_id)`;
46
+ - query helpers for expired/private/draft runs.
47
+ - Runtime helper:
48
+ - collect a run tree from a root;
49
+ - purge or archive run checkpoints, ledgers, and artifacts;
50
+ - return a structured cleanup report.
51
+ - Documentation:
52
+ - durability remains default;
53
+ - purge is allowed only for explicitly temporary/private runs unless a host chooses otherwise.
54
+
55
+ ## Evidence to gather before promotion
56
+
57
+ - Determine whether JSON file and SQLite stores can implement deletion safely without weakening
58
+ provenance guarantees for production runs.
59
+ - Decide whether "private" means hidden, encrypted, or merely excluded from default listings in v0.
60
+ - Decide whether purge should be hard-delete or archive-to-artifact for auditability.
61
+ - Check Gateway's needs for draft Flow tests and backlog/UAT workspaces before finalizing fields.
62
+
63
+ ## Validation ideas
64
+
65
+ - Store contract tests for JSON, SQLite, and in-memory implementations.
66
+ - Runtime helper tests:
67
+ - root + child run tree cleanup;
68
+ - artifact cleanup;
69
+ - no accidental cleanup of non-temporary runs unless explicitly forced.
70
+ - Gateway integration tests once promoted.
71
+
72
+ ## Non-goals
73
+
74
+ - Do not make Runtime responsible for Gateway's retention policy defaults.
75
+ - Do not remove append-only ledger semantics for normal production runs.
76
+ - Do not require every store backend to implement deletion immediately; keep protocols optional and
77
+ capability-detected.
78
+
79
+ ## Guidance for future agents
80
+
81
+ Promote this only when Gateway starts implementing first-class draft/private run cleanup. Keep the
82
+ boundary clean: Runtime defines reusable storage mechanics; Gateway decides policy; Flow decides UX.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "AbstractRuntime"
7
- version = "0.4.9"
7
+ version = "0.4.10"
8
8
  description = "AbstractRuntime: a durable graph runner designed to pair with AbstractCore."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -46,34 +46,34 @@ dependencies = [
46
46
  [project.optional-dependencies]
47
47
  # Enables `abstractruntime.integrations.abstractcore.*` imports.
48
48
  abstractcore = [
49
- "abstractcore>=2.13.12",
49
+ "abstractcore>=2.13.13",
50
50
  ]
51
51
 
52
52
  apple = [
53
- "abstractcore[apple]>=2.13.12",
53
+ "abstractcore[apple]>=2.13.13",
54
54
  ]
55
55
 
56
56
  gpu = [
57
- "abstractcore[gpu]>=2.13.12",
57
+ "abstractcore[gpu]>=2.13.13",
58
58
  ]
59
59
 
60
60
  all-apple = [
61
- "abstractcore[all-apple]>=2.13.12",
61
+ "abstractcore[all-apple]>=2.13.13",
62
62
  ]
63
63
 
64
64
  all-gpu = [
65
- "abstractcore[all-gpu]>=2.13.12",
65
+ "abstractcore[all-gpu]>=2.13.13",
66
66
  ]
67
67
 
68
68
  # Enables AbstractCore multimodal generation helpers with common OpenAI-backed
69
69
  # image/voice capability packages.
70
70
  multimodal = [
71
- "abstractcore[media,openai,vision,voice,audio]>=2.13.12",
71
+ "abstractcore[media,openai,vision,voice,audio]>=2.13.13",
72
72
  ]
73
73
 
74
74
  # Enables `abstractruntime-mcp-worker` with the default toolsets (includes bs4/lxml).
75
75
  mcp-worker = [
76
- "abstractcore[tools]>=2.13.12",
76
+ "abstractcore[tools]>=2.13.13",
77
77
  ]
78
78
 
79
79
  # CI/test dependencies. The workflow runs the basic contract suite plus focused
@@ -0,0 +1,6 @@
1
+ ### Fixed
2
+ - Generated media VisualFlow nodes now keep media model selection in the output spec and reserve LLM `provider`/`model` routing for explicit `runtime_provider`/`runtime_model` overrides.
3
+ - Legacy `provider`/`model` pins on image, TTS, and STT media nodes remain accepted as media selector fallbacks for existing flows.
4
+
5
+ ### Changed
6
+ - Minimum AbstractCore optional dependency floor is now `abstractcore>=2.13.13` so generated media and audio catalog contracts stay aligned.
@@ -169,7 +169,7 @@ def _create_effect_node_handler(
169
169
  input_key=input_key,
170
170
  output_key=output_key,
171
171
  )
172
- elif effect_type == "llm_call":
172
+ elif effect_type in {"llm_call", "generate_image", "generate_voice", "transcribe_audio"}:
173
173
  base_handler = create_llm_call_handler(
174
174
  node_id=node_id,
175
175
  next_node=next_node,
@@ -180,6 +180,13 @@ def _create_effect_node_handler(
180
180
  temperature=effect_config.get("temperature", 0.7),
181
181
  seed=effect_config.get("seed", -1),
182
182
  )
183
+ elif effect_type == "listen_voice":
184
+ base_handler = create_wait_event_handler(
185
+ node_id=node_id,
186
+ next_node=next_node,
187
+ input_key=input_key,
188
+ output_key=output_key,
189
+ )
183
190
  elif effect_type == "tool_calls":
184
191
  base_handler = create_tool_calls_handler(
185
192
  node_id=node_id,
@@ -2571,6 +2578,71 @@ def _sync_effect_results_to_node_outputs(run: Any, flow: Flow) -> None:
2571
2578
  if (node_id, "ttft_ms") in referenced_source_pins:
2572
2579
  current["ttft_ms"] = ttft_ms
2573
2580
  mapped_value = response_text
2581
+ elif effect_type in {"generate_image", "generate_voice"}:
2582
+ if isinstance(raw, dict):
2583
+ modality = "image" if effect_type == "generate_image" else "voice"
2584
+ outputs = raw.get("outputs")
2585
+ current["outputs"] = outputs if isinstance(outputs, dict) else {}
2586
+ resources = raw.get("resources")
2587
+ if isinstance(resources, dict):
2588
+ current["resources"] = resources
2589
+ artifact_id, artifact_ref = _first_generated_artifact(raw)
2590
+ if artifact_ref:
2591
+ current["artifact_ref"] = artifact_ref
2592
+ current["image_artifact" if modality == "image" else "audio_artifact"] = artifact_ref
2593
+ ct = artifact_ref.get("content_type")
2594
+ if isinstance(ct, str) and ct.strip():
2595
+ current["content_type"] = ct.strip()
2596
+ if artifact_id:
2597
+ current["artifact_id"] = artifact_id
2598
+ current["success"] = bool(artifact_id or artifact_ref)
2599
+ meta = {
2600
+ "schema": f"abstractflow.{effect_type}.v1.meta",
2601
+ "version": 1,
2602
+ "modality": modality,
2603
+ "provider": raw.get("provider"),
2604
+ "model": raw.get("model"),
2605
+ }
2606
+ current["meta"] = {k: v for k, v in meta.items() if v is not None}
2607
+ current["raw"] = raw
2608
+ mapped_value = artifact_ref or artifact_id
2609
+ elif effect_type == "transcribe_audio":
2610
+ if isinstance(raw, dict):
2611
+ text_value = ""
2612
+ text_obj = raw.get("text")
2613
+ if isinstance(text_obj, dict):
2614
+ content = text_obj.get("content")
2615
+ if content is not None:
2616
+ text_value = str(content)
2617
+ if not text_value:
2618
+ content = raw.get("content")
2619
+ if content is not None:
2620
+ text_value = str(content)
2621
+ current["text"] = text_value
2622
+ artifact_id, artifact_ref = _first_generated_artifact(raw)
2623
+ if artifact_ref:
2624
+ current["artifact_ref"] = artifact_ref
2625
+ current["transcript_artifact"] = artifact_ref
2626
+ if artifact_id:
2627
+ current["artifact_id"] = artifact_id
2628
+ current["success"] = True
2629
+ current["meta"] = {"schema": "abstractflow.transcribe_audio.v1.meta", "version": 1, "modality": "text", "task": "transcription"}
2630
+ current["raw"] = raw
2631
+ mapped_value = text_value
2632
+ elif effect_type == "listen_voice":
2633
+ if isinstance(raw, dict):
2634
+ current.update(raw)
2635
+ audio_ref = raw.get("audio_artifact") or raw.get("artifact") or raw.get("artifact_ref")
2636
+ if isinstance(audio_ref, dict):
2637
+ current["audio_artifact"] = audio_ref
2638
+ current["artifact_ref"] = audio_ref
2639
+ aid = audio_ref.get("$artifact") or audio_ref.get("artifact_id")
2640
+ if isinstance(aid, str) and aid.strip():
2641
+ current["artifact_id"] = aid.strip()
2642
+ mapped_value = current.get("audio_artifact") or raw
2643
+ else:
2644
+ current["event_data"] = raw
2645
+ mapped_value = raw
2574
2646
  elif effect_type == "tool_calls":
2575
2647
  # Effect outcome is produced by AbstractRuntime TOOL_CALLS handler:
2576
2648
  # - executed: {"mode":"executed","results":[{call_id,name,success,output,error}, ...]}
@@ -196,6 +196,10 @@ def visual_to_flow(visual: VisualFlow) -> Flow:
196
196
  "ask_user",
197
197
  "answer_user",
198
198
  "llm_call",
199
+ "generate_image",
200
+ "generate_voice",
201
+ "transcribe_audio",
202
+ "listen_voice",
199
203
  "tool_calls",
200
204
  "call_tool",
201
205
  "wait_until",
@@ -1356,6 +1360,14 @@ def visual_to_flow(visual: VisualFlow) -> Flow:
1356
1360
  return _create_answer_user_handler(data, effect_config)
1357
1361
  if effect_type == "llm_call":
1358
1362
  return _create_llm_call_handler(data, effect_config)
1363
+ if effect_type == "generate_image":
1364
+ return _create_generate_image_handler(data, effect_config)
1365
+ if effect_type == "generate_voice":
1366
+ return _create_generate_voice_handler(data, effect_config)
1367
+ if effect_type == "transcribe_audio":
1368
+ return _create_transcribe_audio_handler(data, effect_config)
1369
+ if effect_type == "listen_voice":
1370
+ return _create_listen_voice_handler(data, effect_config)
1359
1371
  if effect_type == "tool_calls":
1360
1372
  return _create_tool_calls_handler(data, effect_config)
1361
1373
  if effect_type == "call_tool":
@@ -1513,6 +1525,207 @@ def visual_to_flow(visual: VisualFlow) -> Flow:
1513
1525
 
1514
1526
  return handler
1515
1527
 
1528
+ def _nonempty_str(value: Any) -> str:
1529
+ return value.strip() if isinstance(value, str) and value.strip() else ""
1530
+
1531
+ def _coerce_int(value: Any) -> Optional[int]:
1532
+ if value is None or isinstance(value, bool):
1533
+ return None
1534
+ try:
1535
+ return int(float(value))
1536
+ except Exception:
1537
+ return None
1538
+
1539
+ def _coerce_float(value: Any) -> Optional[float]:
1540
+ if value is None or isinstance(value, bool):
1541
+ return None
1542
+ try:
1543
+ out = float(value)
1544
+ except Exception:
1545
+ return None
1546
+ return out if out == out else None
1547
+
1548
+ def _dict_input(input_data: Any) -> Dict[str, Any]:
1549
+ return input_data if isinstance(input_data, dict) else {}
1550
+
1551
+ def _input_or_config(input_data: Any, config: Dict[str, Any], *keys: str, default: Any = None) -> Any:
1552
+ payload = _dict_input(input_data)
1553
+ for key in keys:
1554
+ if key in payload and payload.get(key) not in (None, ""):
1555
+ return payload.get(key)
1556
+ for key in keys:
1557
+ if isinstance(config, dict) and key in config and config.get(key) not in (None, ""):
1558
+ return config.get(key)
1559
+ return default
1560
+
1561
+ def _create_generate_image_handler(data: Dict[str, Any], config: Dict[str, Any]):
1562
+ def handler(input_data: Any):
1563
+ payload = _dict_input(input_data)
1564
+ prompt = str(_input_or_config(payload, config, "prompt", default="") or "")
1565
+ fmt = str(_input_or_config(payload, config, "format", "response_format", default="png") or "png").strip().lower() or "png"
1566
+ output_spec: Dict[str, Any] = {"modality": "image", "task": "image_generation", "format": fmt}
1567
+ for key in ("size", "negative_prompt", "quality", "style"):
1568
+ value = _input_or_config(payload, config, key)
1569
+ if isinstance(value, str) and value.strip():
1570
+ output_spec[key] = value.strip()
1571
+ for key in ("width", "height", "seed", "steps"):
1572
+ value = _coerce_int(_input_or_config(payload, config, key))
1573
+ if value is not None:
1574
+ output_spec[key] = value
1575
+ guidance = _coerce_float(_input_or_config(payload, config, "guidance_scale", "guidanceScale"))
1576
+ if guidance is not None:
1577
+ output_spec["guidance_scale"] = guidance
1578
+ extra = _input_or_config(payload, config, "extra")
1579
+ if isinstance(extra, dict) and extra:
1580
+ output_spec["extra"] = dict(extra)
1581
+ image_provider = _nonempty_str(_input_or_config(payload, config, "image_provider", "imageProvider"))
1582
+ image_model = _nonempty_str(_input_or_config(payload, config, "image_model", "imageModel"))
1583
+ legacy_provider = _nonempty_str(_input_or_config(payload, config, "provider"))
1584
+ legacy_model = _nonempty_str(_input_or_config(payload, config, "model"))
1585
+ runtime_provider = _nonempty_str(
1586
+ _input_or_config(payload, config, "runtime_provider", "runtimeProvider", "llm_provider", "llmProvider")
1587
+ )
1588
+ runtime_model = _nonempty_str(
1589
+ _input_or_config(payload, config, "runtime_model", "runtimeModel", "llm_model", "llmModel")
1590
+ )
1591
+ if not image_provider:
1592
+ image_provider = legacy_provider
1593
+ if not image_model:
1594
+ image_model = legacy_model
1595
+ if image_provider:
1596
+ output_spec["provider"] = image_provider
1597
+ if image_model:
1598
+ output_spec["model"] = image_model
1599
+ pending: Dict[str, Any] = {
1600
+ "type": "llm_call",
1601
+ "prompt": prompt,
1602
+ "system_prompt": "",
1603
+ "tools": [],
1604
+ "params": {},
1605
+ "output": output_spec,
1606
+ }
1607
+ if runtime_provider:
1608
+ pending["provider"] = runtime_provider
1609
+ if runtime_model:
1610
+ pending["model"] = runtime_model
1611
+ return {"image_artifact": None, "artifact_ref": None, "artifact_id": "", "success": None, "_pending_effect": pending}
1612
+
1613
+ return handler
1614
+
1615
+ def _create_generate_voice_handler(data: Dict[str, Any], config: Dict[str, Any]):
1616
+ def handler(input_data: Any):
1617
+ payload = _dict_input(input_data)
1618
+ text = str(_input_or_config(payload, config, "text", "prompt", default="") or "")
1619
+ fmt = str(_input_or_config(payload, config, "format", "response_format", default="wav") or "wav").strip().lower() or "wav"
1620
+ output_spec: Dict[str, Any] = {"modality": "voice", "task": "tts", "format": fmt}
1621
+ for key in ("voice", "profile", "instructions"):
1622
+ value = _input_or_config(payload, config, key)
1623
+ if isinstance(value, str) and value.strip():
1624
+ output_spec[key] = value.strip()
1625
+ tts_provider = _nonempty_str(_input_or_config(payload, config, "tts_provider", "ttsProvider", "provider"))
1626
+ tts_model = _nonempty_str(_input_or_config(payload, config, "tts_model", "ttsModel", "model"))
1627
+ runtime_provider = _nonempty_str(
1628
+ _input_or_config(payload, config, "runtime_provider", "runtimeProvider", "llm_provider", "llmProvider")
1629
+ )
1630
+ runtime_model = _nonempty_str(
1631
+ _input_or_config(payload, config, "runtime_model", "runtimeModel", "llm_model", "llmModel")
1632
+ )
1633
+ if tts_provider:
1634
+ output_spec["provider"] = tts_provider
1635
+ if tts_model:
1636
+ output_spec["model"] = tts_model
1637
+ speed = _coerce_float(_input_or_config(payload, config, "speed"))
1638
+ if speed is not None:
1639
+ output_spec["speed"] = speed
1640
+ extra = _input_or_config(payload, config, "extra")
1641
+ if isinstance(extra, dict) and extra:
1642
+ output_spec["extra"] = dict(extra)
1643
+ pending: Dict[str, Any] = {
1644
+ "type": "llm_call",
1645
+ "prompt": text,
1646
+ "system_prompt": "",
1647
+ "tools": [],
1648
+ "params": {},
1649
+ "output": output_spec,
1650
+ }
1651
+ if runtime_provider:
1652
+ pending["provider"] = runtime_provider
1653
+ if runtime_model:
1654
+ pending["model"] = runtime_model
1655
+ return {"audio_artifact": None, "artifact_ref": None, "artifact_id": "", "success": None, "_pending_effect": pending}
1656
+
1657
+ return handler
1658
+
1659
+ def _create_transcribe_audio_handler(data: Dict[str, Any], config: Dict[str, Any]):
1660
+ def handler(input_data: Any):
1661
+ payload = _dict_input(input_data)
1662
+ audio_ref = _input_or_config(payload, config, "audio_artifact", "artifact", "audio")
1663
+ output_spec: Dict[str, Any] = {"modality": "text", "task": "transcription"}
1664
+ for key in ("language", "prompt", "response_format", "format"):
1665
+ value = _input_or_config(payload, config, key)
1666
+ if isinstance(value, str) and value.strip():
1667
+ output_spec[key] = value.strip()
1668
+ stt_provider = _nonempty_str(_input_or_config(payload, config, "stt_provider", "sttProvider", "provider"))
1669
+ stt_model = _nonempty_str(_input_or_config(payload, config, "stt_model", "sttModel", "model"))
1670
+ runtime_provider = _nonempty_str(
1671
+ _input_or_config(payload, config, "runtime_provider", "runtimeProvider", "llm_provider", "llmProvider")
1672
+ )
1673
+ runtime_model = _nonempty_str(
1674
+ _input_or_config(payload, config, "runtime_model", "runtimeModel", "llm_model", "llmModel")
1675
+ )
1676
+ if stt_provider:
1677
+ output_spec["provider"] = stt_provider
1678
+ if stt_model:
1679
+ output_spec["model"] = stt_model
1680
+ temp = _coerce_float(_input_or_config(payload, config, "temperature"))
1681
+ if temp is not None:
1682
+ output_spec["temperature"] = temp
1683
+ media_item: Any = audio_ref
1684
+ if isinstance(audio_ref, str) and audio_ref.strip() and not audio_ref.startswith(("/", "~")):
1685
+ media_item = {"$artifact": audio_ref.strip(), "type": "audio"}
1686
+ pending: Dict[str, Any] = {
1687
+ "type": "llm_call",
1688
+ "prompt": str(output_spec.get("prompt") or "Transcribe this audio."),
1689
+ "system_prompt": "",
1690
+ "tools": [],
1691
+ "params": {},
1692
+ "media": [media_item] if media_item else [],
1693
+ "output": output_spec,
1694
+ }
1695
+ if runtime_provider:
1696
+ pending["provider"] = runtime_provider
1697
+ if runtime_model:
1698
+ pending["model"] = runtime_model
1699
+ return {"text": "", "transcript_artifact": None, "success": None, "_pending_effect": pending}
1700
+
1701
+ return handler
1702
+
1703
+ def _create_listen_voice_handler(data: Dict[str, Any], config: Dict[str, Any]):
1704
+ def handler(input_data: Any):
1705
+ payload = _dict_input(input_data)
1706
+ prompt = str(_input_or_config(payload, config, "prompt", default="Speak now.") or "Speak now.")
1707
+ wait_key = str(_input_or_config(payload, config, "wait_key", "event_key", default="voice_input") or "voice_input")
1708
+ details: Dict[str, Any] = {"input_mode": "voice", "content_types": ["audio/wav", "audio/mpeg", "audio/webm"]}
1709
+ language = _input_or_config(payload, config, "language")
1710
+ if isinstance(language, str) and language.strip():
1711
+ details["language"] = language.strip()
1712
+ max_duration_s = _coerce_float(_input_or_config(payload, config, "max_duration_s", "maxDurationS"))
1713
+ if max_duration_s is not None:
1714
+ details["max_duration_s"] = max_duration_s
1715
+ return {
1716
+ "audio_artifact": None,
1717
+ "text": "",
1718
+ "_pending_effect": {
1719
+ "type": "wait_event",
1720
+ "wait_key": wait_key,
1721
+ "prompt": prompt,
1722
+ "allow_free_text": True,
1723
+ "details": details,
1724
+ },
1725
+ }
1726
+
1727
+ return handler
1728
+
1516
1729
  def _create_ask_user_handler(data: Dict[str, Any], config: Dict[str, Any]):
1517
1730
  def handler(input_data):
1518
1731
  prompt = input_data.get("prompt", "Please respond:") if isinstance(input_data, dict) else str(input_data)