AbstractRuntime 0.2.0__tar.gz → 0.4.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 (160) hide show
  1. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/CHANGELOG.md +108 -0
  2. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/PKG-INFO +5 -1
  3. abstractruntime-0.4.0/docs/architecture.md +165 -0
  4. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/pyproject.toml +15 -3
  5. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/__init__.py +7 -2
  6. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/core/config.py +14 -1
  7. abstractruntime-0.4.0/src/abstractruntime/core/event_keys.py +62 -0
  8. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/core/models.py +12 -1
  9. abstractruntime-0.4.0/src/abstractruntime/core/runtime.py +3166 -0
  10. abstractruntime-0.4.0/src/abstractruntime/core/vars.py +189 -0
  11. abstractruntime-0.4.0/src/abstractruntime/evidence/__init__.py +10 -0
  12. abstractruntime-0.4.0/src/abstractruntime/evidence/recorder.py +325 -0
  13. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/integrations/abstractcore/__init__.py +3 -0
  14. abstractruntime-0.4.0/src/abstractruntime/integrations/abstractcore/constants.py +19 -0
  15. abstractruntime-0.4.0/src/abstractruntime/integrations/abstractcore/default_tools.py +134 -0
  16. abstractruntime-0.4.0/src/abstractruntime/integrations/abstractcore/effect_handlers.py +368 -0
  17. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/integrations/abstractcore/factory.py +95 -10
  18. abstractruntime-0.4.0/src/abstractruntime/integrations/abstractcore/llm_client.py +801 -0
  19. abstractruntime-0.4.0/src/abstractruntime/integrations/abstractcore/mcp_worker.py +586 -0
  20. abstractruntime-0.4.0/src/abstractruntime/integrations/abstractcore/observability.py +80 -0
  21. abstractruntime-0.4.0/src/abstractruntime/integrations/abstractcore/summarizer.py +154 -0
  22. abstractruntime-0.4.0/src/abstractruntime/integrations/abstractcore/tool_executor.py +625 -0
  23. abstractruntime-0.4.0/src/abstractruntime/memory/__init__.py +21 -0
  24. abstractruntime-0.4.0/src/abstractruntime/memory/active_context.py +746 -0
  25. abstractruntime-0.4.0/src/abstractruntime/memory/active_memory.py +452 -0
  26. abstractruntime-0.4.0/src/abstractruntime/memory/compaction.py +105 -0
  27. abstractruntime-0.4.0/src/abstractruntime/rendering/__init__.py +17 -0
  28. abstractruntime-0.4.0/src/abstractruntime/rendering/agent_trace_report.py +256 -0
  29. abstractruntime-0.4.0/src/abstractruntime/rendering/json_stringify.py +136 -0
  30. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/scheduler/scheduler.py +93 -2
  31. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/storage/__init__.py +3 -1
  32. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/storage/artifacts.py +20 -5
  33. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/storage/json_files.py +15 -2
  34. abstractruntime-0.4.0/src/abstractruntime/storage/observable.py +99 -0
  35. abstractruntime-0.4.0/tests/conftest.py +38 -0
  36. abstractruntime-0.4.0/tests/test_active_context_policy.py +217 -0
  37. abstractruntime-0.4.0/tests/test_active_memory.py +157 -0
  38. abstractruntime-0.4.0/tests/test_answer_user_effect.py +63 -0
  39. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_artifacts.py +32 -0
  40. abstractruntime-0.4.0/tests/test_chat_summarizer_integration.py +146 -0
  41. abstractruntime-0.4.0/tests/test_compaction_helpers.py +42 -0
  42. abstractruntime-0.4.0/tests/test_evidence_recorder.py +136 -0
  43. abstractruntime-0.4.0/tests/test_ledger_subscription.py +87 -0
  44. abstractruntime-0.4.0/tests/test_llm_call_verbatim_payload_capture.py +31 -0
  45. abstractruntime-0.4.0/tests/test_llm_client_tool_call_parsing.py +250 -0
  46. abstractruntime-0.4.0/tests/test_mcp_remote_tool_executor.py +214 -0
  47. abstractruntime-0.4.0/tests/test_mcp_worker_logging.py +92 -0
  48. abstractruntime-0.4.0/tests/test_mcp_worker_security.py +89 -0
  49. abstractruntime-0.4.0/tests/test_mcp_worker_stdio.py +44 -0
  50. abstractruntime-0.4.0/tests/test_memory_note_effect.py +235 -0
  51. abstractruntime-0.4.0/tests/test_memory_query_effect.py +115 -0
  52. abstractruntime-0.4.0/tests/test_memory_query_rich_filters.py +250 -0
  53. abstractruntime-0.4.0/tests/test_memory_scope_and_rehydrate_effect.py +336 -0
  54. abstractruntime-0.4.0/tests/test_memory_tag_effect.py +122 -0
  55. abstractruntime-0.4.0/tests/test_packaging_extras.py +33 -0
  56. abstractruntime-0.4.0/tests/test_pause_resume.py +197 -0
  57. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_remote_llm_client.py +20 -0
  58. abstractruntime-0.4.0/tests/test_rendering_agent_trace_report.py +72 -0
  59. abstractruntime-0.4.0/tests/test_rendering_json_stringify.py +28 -0
  60. abstractruntime-0.4.0/tests/test_runtime_config_max_output_tokens_fallback.py +19 -0
  61. abstractruntime-0.4.0/tests/test_runtime_node_traces.py +65 -0
  62. abstractruntime-0.4.0/tests/test_runtime_start_seeds_tool_support.py +37 -0
  63. abstractruntime-0.4.0/tests/test_start_subworkflow_async_wait.py +61 -0
  64. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_subworkflow.py +20 -0
  65. abstractruntime-0.4.0/tests/test_terminal_effect_completion.py +48 -0
  66. abstractruntime-0.4.0/tests/test_tool_executor_argument_sanitization.py +28 -0
  67. abstractruntime-0.4.0/tests/test_tool_executor_error_output_detection.py +28 -0
  68. abstractruntime-0.4.0/tests/test_tool_executor_filename_alias.py +30 -0
  69. abstractruntime-0.4.0/tests/test_tool_executor_kwarg_canonicalization.py +58 -0
  70. abstractruntime-0.4.0/tests/test_tool_executor_read_file_aliases.py +65 -0
  71. abstractruntime-0.4.0/tests/test_tool_executor_timeout.py +45 -0
  72. abstractruntime-0.4.0/tests/test_tool_wait_allowlist_safety.py +152 -0
  73. abstractruntime-0.4.0/tests/test_vars_query_effect.py +109 -0
  74. abstractruntime-0.4.0/tests/test_wait_event_prompt_metadata.py +61 -0
  75. abstractruntime-0.2.0/src/abstractruntime/core/runtime.py +0 -736
  76. abstractruntime-0.2.0/src/abstractruntime/core/vars.py +0 -94
  77. abstractruntime-0.2.0/src/abstractruntime/integrations/abstractcore/effect_handlers.py +0 -119
  78. abstractruntime-0.2.0/src/abstractruntime/integrations/abstractcore/llm_client.py +0 -397
  79. abstractruntime-0.2.0/src/abstractruntime/integrations/abstractcore/tool_executor.py +0 -168
  80. abstractruntime-0.2.0/tests/conftest.py +0 -20
  81. abstractruntime-0.2.0/tests/test_pause_resume.py +0 -98
  82. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/.gitignore +0 -0
  83. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/LICENSE +0 -0
  84. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/README.md +0 -0
  85. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/ROADMAP.md +0 -0
  86. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/adr/0001_layered_coupling_with_abstractcore.md +0 -0
  87. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/adr/0002_execution_modes_local_remote_hybrid.md +0 -0
  88. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/adr/0003_provenance_tamper_evident_hash_chain.md +0 -0
  89. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/adr/README.md +0 -0
  90. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/README.md +0 -0
  91. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/001_runtime_kernel.md +0 -0
  92. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/002_persistence_and_ledger.md +0 -0
  93. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/003_wait_primitives.md +0 -0
  94. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/004_scheduler_driver.md +0 -0
  95. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/005_abstractcore_integration.md +0 -0
  96. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/006_snapshots_bookmarks.md +0 -0
  97. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/007_provenance_hash_chain.md +0 -0
  98. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/009_artifact_store.md +0 -0
  99. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/010_examples_and_composition.md +0 -0
  100. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/011_subworkflow_support.md +0 -0
  101. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/012_run_store_query_and_scheduler_support.md +0 -0
  102. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/013_effect_retries_and_idempotency.md +0 -0
  103. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/completed/016_runtime_aware_parameters.md +0 -0
  104. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/001_integrations_abstractcore.md +0 -0
  105. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/001_runtime_kernel.md +0 -0
  106. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/002_persistence_and_ledger.md +0 -0
  107. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/002_snapshots_bookmarks.md +0 -0
  108. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/003_provenance_ledger_chain.md +0 -0
  109. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/003_wait_resume_and_scheduler.md +0 -0
  110. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/004_effect_handlers_and_integrations.md +0 -0
  111. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/004_tests.md +0 -0
  112. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/005_docs_updates.md +0 -0
  113. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/005_examples_and_composition.md +0 -0
  114. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/006_ai_fingerprint_and_provenance.md +0 -0
  115. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/DEPRECATED_README.md +0 -0
  116. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/README.md +0 -0
  117. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/deprecated/abstractruntime_docs_final_02a7373b.plan.md +0 -0
  118. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/planned/008_signatures_and_keys.md +0 -0
  119. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/planned/014_remote_tool_worker_executor.md +0 -0
  120. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/planned/015_agent_integration_improvements.md +0 -0
  121. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/backlog/planned/017_limit_warnings_and_observability.md +0 -0
  122. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/integrations/abstractcore.md +0 -0
  123. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/limits.md +0 -0
  124. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/manual_testing.md +0 -0
  125. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/proposal.md +0 -0
  126. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/provenance.md +0 -0
  127. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/docs/snapshots.md +0 -0
  128. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/examples/01_hello_world.py +0 -0
  129. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/examples/02_ask_user.py +0 -0
  130. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/examples/03_wait_until.py +0 -0
  131. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/examples/04_multi_step.py +0 -0
  132. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/examples/05_persistence.py +0 -0
  133. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/examples/06_llm_integration.py +0 -0
  134. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/examples/07_react_agent.py +0 -0
  135. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/examples/README.md +0 -0
  136. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/core/__init__.py +0 -0
  137. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/core/policy.py +0 -0
  138. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/core/spec.py +0 -0
  139. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/identity/__init__.py +0 -0
  140. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/identity/fingerprint.py +0 -0
  141. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/integrations/__init__.py +0 -0
  142. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/integrations/abstractcore/logging.py +0 -0
  143. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/scheduler/__init__.py +0 -0
  144. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/scheduler/convenience.py +0 -0
  145. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/scheduler/registry.py +0 -0
  146. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/storage/base.py +0 -0
  147. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/storage/in_memory.py +0 -0
  148. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/storage/ledger_chain.py +0 -0
  149. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/src/abstractruntime/storage/snapshots.py +0 -0
  150. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/README.md +0 -0
  151. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_durable_toolsets.py +0 -0
  152. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_integration_abstractcore.py +0 -0
  153. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_integrations_abstractcore.py +0 -0
  154. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_ledger_chain.py +0 -0
  155. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_queryable_run_store.py +0 -0
  156. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_real_integration.py +0 -0
  157. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_retry_idempotency.py +0 -0
  158. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_scheduler.py +0 -0
  159. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_snapshots.py +0 -0
  160. {abstractruntime-0.2.0 → abstractruntime-0.4.0}/tests/test_trace_context_propagation.py +0 -0
@@ -5,6 +5,114 @@ All notable changes to AbstractRuntime will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - **Durable prompt metadata for EVENT waits**:
12
+ - `WAIT_EVENT` effects may include optional `prompt`, `choices`, and `allow_free_text` fields.
13
+ - The runtime persists these fields onto `WaitState` so hosts (including remote/thin clients) can render a durable ask+wait UX without relying on in-process callbacks.
14
+ - **Rendering utilities** (`abstractruntime.rendering`):
15
+ - `stringify_json(...)` + `JsonStringifyMode` to render JSON/JSON-ish values into strings with `none|beautify|minified` modes.
16
+ - `render_agent_trace_markdown(...)` to render runtime-owned `node_traces` scratchpads into a complete, review-friendly Markdown timeline (tool args + results untruncated).
17
+
18
+ ## [0.4.0] - 2025-01-06
19
+
20
+ ### Added
21
+
22
+ - **Active Memory System** (`abstractruntime.memory.active_memory`): Complete MemAct agent memory module
23
+ - Runtime-owned `ACTIVE_MEMORY_DELTA` effect for structured Active Memory updates (used by agents via `active_memory_delta` tool)
24
+ - JSON-safe durable storage in `run.vars["_runtime"]["active_memory"]`
25
+ - Memory modules: MY PERSONA, RELATIONSHIPS, MEMORY BLUEPRINTS, CURRENT TASKS, CURRENT CONTEXT, CRITICAL INSIGHTS, REFERENCES, HISTORY
26
+ - Active Memory v9 format with natural-language markdown rendering (not YAML) to reduce syntax contamination
27
+ - All components render into system prompt by default (prevents user-role pollution on native-tool providers)
28
+
29
+ - **MCP Worker** (`abstractruntime-mcp-worker`): Standalone stdio-based MCP server for AbstractRuntime tools
30
+ - Exposes AbstractRuntime's default toolsets as MCP tools via stdio transport
31
+ - Human-friendly logging to stderr with ANSI color support
32
+ - Security: allowlist-based command execution safety (`TOOL_WAIT` effect for dangerous commands)
33
+ - New optional dependency: `abstractruntime[mcp-worker]` (includes `abstractcore[tools]`)
34
+ - Entry point: `abstractruntime-mcp-worker` CLI script
35
+
36
+ - **Evidence Capture System** (`abstractruntime.evidence.recorder`): Always-on provenance-first evidence recording
37
+ - Automatically records evidence for external-boundary tools: `web_search`, `fetch_url`, `execute_command`
38
+ - Evidence stored as artifact-backed records indexed as `kind="evidence"` in `RunState.vars["_runtime"]["memory_spans"]`
39
+ - Runtime helpers: `Runtime.list_evidence(run_id)` and `Runtime.load_evidence(evidence_id)`
40
+ - Keeps RunState JSON-safe by storing large payloads in ArtifactStore with refs
41
+
42
+ - **Ledger Subscriptions**: Real-time step append events via `Runtime.subscribe_ledger()`
43
+ - `create_local_runtime`, `create_remote_runtime`, `create_hybrid_runtime` now wrap LedgerStore with `ObservableLedgerStore` by default
44
+ - Hosts can receive real-time notifications when steps are appended to ledger
45
+
46
+ - **Durable Custom Events (Signals)**:
47
+ - `EMIT_EVENT` effect to dispatch events and resume matching `WAIT_EVENT` runs
48
+ - Extended `WAIT_EVENT` to accept `{scope, name}` payloads (runtime computes stable `wait_key`)
49
+ - `Scheduler.emit_event(...)` host API for external event delivery (session-scoped by default)
50
+
51
+ - **Orchestrator-Owned Timeouts** (AbstractCore integration):
52
+ - Default **LLM timeout**: 7200s per `LLM_CALL` (not per-workflow), enforced by `create_*_runtime` factories
53
+ - Default **tool execution timeout**: 7200s per tool call (not per-workflow), enforced by ToolExecutor implementations
54
+
55
+ - **Tool Executor Enhancements** (`MappingToolExecutor`):
56
+ - **Argument canonicalization**: Maps common parameter name variations (e.g., `file_path`/`filepath`/`path`) to canonical names
57
+ - **Filename aliases**: Supports `target_file`, `file_path`, `filepath`, `path` as aliases for file operations
58
+ - **Error output detection**: Detects structured error responses (`{"success": false, ...}`) from tools
59
+ - **Argument sanitization**: Cleans and validates tool call arguments
60
+ - **Timeout support**: Per-tool execution timeouts with configurable limits
61
+
62
+ - **Memory Query Enhancements** (`MEMORY_QUERY` effect):
63
+ - Tag filters with **AND/OR** modes (`tags_mode=all|any`) and **multi-value** keys (`tags.person=["alice","bob"]`)
64
+ - Metadata filters for **authors** (`created_by`) and **locations** (`location`, `tags.location`)
65
+ - Span records now capture `created_by` for `conversation_span`, `active_memory_span`, `memory_note` when `actor_id` available
66
+ - `MEMORY_NOTE` accepts optional `location` field
67
+ - `MEMORY_NOTE` supports `keep_in_context=true` flag to immediately rehydrate stored note into `context.messages`
68
+
69
+ - **Package Dependencies**:
70
+ - New optional dependency: `abstractruntime[abstractcore]` (enables `abstractruntime.integrations.abstractcore.*`)
71
+ - New optional dependency: `abstractruntime[mcp-worker]` (includes `abstractcore[tools]>=2.6.8`)
72
+
73
+ ### Changed
74
+
75
+ - **LLM Client Enhancements**:
76
+ - Tool call parsing refactored for better robustness and error handling
77
+ - Streaming support with timing metrics (TTFT, generation time)
78
+ - Response normalization preserves JSON-safe `raw_response` for debugging
79
+ - Always attaches exact provider request payload under `result.metadata._provider_request` for every `LLM_CALL` step
80
+
81
+ - **Runtime Core** (902 lines changed):
82
+ - Enhanced resume handling for paused/cancelled runs
83
+ - Improved subworkflow execution with async+wait support
84
+ - Better observable ledger integration
85
+
86
+ ### Fixed
87
+
88
+ - **Cancellation is Terminal**: `Runtime.tick()` now treats `RunStatus.CANCELLED` as terminal and will not progress cancelled runs
89
+ - **Control-Plane Safety**: `Runtime.tick()` stops without overwriting externally persisted pause/cancel state (used by AbstractFlow Web)
90
+ - **Atomic Run Checkpoints**: `JsonFileRunStore.save()` writes via temp file + atomic rename to prevent partial/corrupt JSON under concurrent writes
91
+ - **START_SUBWORKFLOW async+wait**: Support for `async=true` + `wait=true` to start child run without blocking parent tick, while keeping parent in durable SUBWORKFLOW wait
92
+ - **ArtifactStore Run-Scoped Addressing**: Artifact IDs namespaced to run when `run_id` provided (prevents cross-run collisions, preserves purge-by-run semantics)
93
+ - **AbstractCore Integration Imports**: `LocalAbstractCoreLLMClient` imports `create_llm` robustly in monorepo namespace-package layouts
94
+ - **Token Limit Metadata**: `_limits.max_output_tokens` falls back to model capabilities when not configured (runtime surfaces explicit per-step output budget)
95
+ - **Token-Cap Normalization Boundary**: Removed local `max_tokens → max_output_tokens` aliasing from AbstractRuntime's AbstractCore client (AbstractCore providers own this mapping)
96
+
97
+ ### Testing
98
+
99
+ - **25 new/modified test files** covering:
100
+ - Active Memory functionality
101
+ - MCP worker (logging, security, stdio communication)
102
+ - Evidence recorder
103
+ - Memory query rich filters
104
+ - Tool executor (canonicalization, filename aliases, timeouts, error detection)
105
+ - LLM client tool call parsing
106
+ - Runtime configuration and subworkflow handling
107
+ - Packaging extras validation
108
+
109
+ ### Statistics
110
+
111
+ - **33 commits** improving memory systems, MCP integration, evidence capture, and tool execution
112
+ - **45 files changed**: 5,788 insertions, 286 deletions
113
+ - **6,074 total lines changed** across the codebase
114
+ - **3 new modules**: `active_memory.py`, `evidence/recorder.py`, `mcp_worker.py`
115
+
8
116
  ## [0.2.0] - 2025-12-17
9
117
 
10
118
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbstractRuntime
3
- Version: 0.2.0
3
+ Version: 0.4.0
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: AbstractCore (GitHub), https://github.com/lpalbou/abstractruntime
@@ -18,6 +18,10 @@ Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
20
  Requires-Python: >=3.10
21
+ Provides-Extra: abstractcore
22
+ Requires-Dist: abstractcore>=2.6.8; extra == 'abstractcore'
23
+ Provides-Extra: mcp-worker
24
+ Requires-Dist: abstractcore[tools]>=2.6.8; extra == 'mcp-worker'
21
25
  Description-Content-Type: text/markdown
22
26
 
23
27
  ## AbstractRuntime
@@ -0,0 +1,165 @@
1
+ # AbstractRuntime — Architecture (Current)
2
+
3
+ > Updated: 2026-01-07
4
+ > Scope: this describes **what is implemented today** in this monorepo.
5
+
6
+ AbstractRuntime is the **durable execution kernel** of the AbstractFramework. It runs workflow graphs as a persisted state machine:
7
+ - **tick** a run forward until it completes or blocks
8
+ - persist **checkpoints** (RunState) + an append-only **ledger** (StepRecord)
9
+ - represent blocking as durable **WaitState** (USER / EVENT / UNTIL / SUBWORKFLOW)
10
+ - resume with an external payload (human input, event envelope, job completion)
11
+
12
+ AbstractRuntime is intentionally dependency-light in the core (`abstractruntime/core/*`):
13
+ - higher-level integrations (LLM providers, tool execution, event bus bridging) live in `abstractruntime/integrations/*`
14
+
15
+ ## High-level runtime loop (data flow)
16
+
17
+ ```
18
+ WorkflowSpec (in-memory handlers)
19
+
20
+
21
+ Runtime.tick(run_id)
22
+ │ loads + updates
23
+
24
+ RunStore (checkpoint RunState.vars + waiting)
25
+ │ append
26
+
27
+ LedgerStore (StepRecords: started/completed/waiting/failed)
28
+ │ large payloads
29
+
30
+ ArtifactStore (JSON blobs referenced from vars/ledger)
31
+ ```
32
+
33
+ ## Core Types (Durable, JSON-Safe)
34
+
35
+ Defined in `abstractruntime/src/abstractruntime/core/models.py`:
36
+
37
+ - `WorkflowSpec`
38
+ - `workflow_id`, `entry_node`, `nodes: Dict[node_id, handler]`
39
+ - `RunState`
40
+ - `run_id`, `workflow_id`, `status`, `current_node`
41
+ - `vars` (**JSON-safe** dictionary, persisted by RunStore)
42
+ - `waiting: WaitState | None`
43
+ - `output` / `error`
44
+ - optional provenance: `actor_id`, `session_id`, `parent_run_id`
45
+ - `StepPlan`
46
+ - returned by node handlers: `{effect?, next_node?, complete_output?}`
47
+ - `Effect` + `EffectType`
48
+ - requests for side effects / waits (LLM_CALL, TOOL_CALLS, ASK_USER, WAIT_EVENT, …)
49
+ - `WaitState` + `WaitReason`
50
+ - durable blocking representation
51
+ - `StepRecord`
52
+ - append-only audit log entries (STARTED/COMPLETED/WAITING/FAILED)
53
+
54
+ ## Runtime Semantics (start / tick / resume)
55
+
56
+ Implemented in `abstractruntime/src/abstractruntime/core/runtime.py`:
57
+
58
+ - `Runtime.start(workflow, vars, actor_id?, session_id?, parent_run_id?) -> run_id`
59
+ - creates a `RunState` checkpoint (RUNNING, current_node=entry)
60
+ - `Runtime.tick(workflow, run_id, max_steps=...) -> RunState`
61
+ - executes node handlers in a loop:
62
+ - handler returns `StepPlan`
63
+ - if `StepPlan.effect` exists → dispatch to the effect handler registry
64
+ - if effect outcome blocks → persist `RunState.waiting` and return WAITING
65
+ - if `next_node` exists → advance cursor and continue
66
+ - if `complete_output` exists → set run output and finish
67
+ - `Runtime.resume(workflow, run_id, wait_key, payload, max_steps=...) -> RunState`
68
+ - validates the run is waiting for `wait_key`
69
+ - writes `payload` to the waiting node’s `result_key`
70
+ - clears `waiting`, transitions to RUNNING, continues via `tick`
71
+
72
+ ### Run Control (pause/resume/cancel)
73
+ Run control is runtime-owned (also in `abstractruntime/core/runtime.py`):
74
+ - pause uses a synthetic WAIT_USER (`wait_key = pause:<run_id>`) and a durable flag in `vars["_runtime"]["control"]["paused"]`
75
+ - resume clears the pause wait and continues ticking
76
+ - cancel marks the run CANCELLED (and can cancel subflows via host orchestration)
77
+
78
+ ## Persistence Layer (RunStore / LedgerStore / ArtifactStore)
79
+
80
+ Storage interfaces are defined in `abstractruntime/src/abstractruntime/storage/base.py` and implemented in:
81
+ - `abstractruntime/src/abstractruntime/storage/in_memory.py` (in-memory stores)
82
+ - `abstractruntime/src/abstractruntime/storage/json_files.py` (file-based stores)
83
+ - `JsonFileRunStore`: `run_<run_id>.json`
84
+ - `JsonlLedgerStore`: `ledger_<run_id>.jsonl`
85
+ - `abstractruntime/src/abstractruntime/storage/artifacts.py`
86
+ - `ArtifactStore` + `FileArtifactStore` + helpers (`artifact_ref`, `resolve_artifact`, …)
87
+ - `abstractruntime/src/abstractruntime/storage/observable.py`
88
+ - `ObservableLedgerStore` adds subscriptions for live UI streaming
89
+
90
+ **Key invariant:** `RunState.vars` must remain JSON-safe. Large payloads should be stored in `ArtifactStore` and referenced.
91
+
92
+ ## Effect System (Handlers)
93
+
94
+ The runtime executes side effects by dispatching `EffectType` to handler callables. The core runtime ships with the effect protocol; hosts wire concrete handlers.
95
+
96
+ ### Memory effects (runtime-owned, durable)
97
+ The runtime ships a small, provenance-first memory surface (no embeddings):
98
+ - `EffectType.MEMORY_COMPACT`: archive older `context.messages` into an artifact + insert a summary handle
99
+ - `EffectType.MEMORY_NOTE`: store a durable note with tags + sources
100
+ - supports `payload.scope = run|session|global` (scope is routing: it selects the index-owner run)
101
+ - `EffectType.MEMORY_QUERY`: recall spans/notes by id/tags/time/query
102
+ - supports `payload.scope = run|session|global|all`
103
+ - supports `payload.return = rendered|meta|both` (meta enables deterministic workflows without parsing text)
104
+ - `EffectType.MEMORY_TAG`: apply/merge tags onto existing span index entries
105
+ - `EffectType.MEMORY_REHYDRATE`: rehydrate archived `conversation_span` artifacts into `context.messages` deterministically (deduped)
106
+
107
+ Global scope uses a stable run id (default `global_memory`, override via `ABSTRACTRUNTIME_GLOBAL_MEMORY_RUN_ID`, validated as filesystem-safe).
108
+
109
+ ### AbstractCore Integration (LLM + Tools)
110
+ `abstractruntime/src/abstractruntime/integrations/abstractcore/*` provides:
111
+ - an AbstractCore-backed LLM client (`llm_client.py`)
112
+ - tool execution boundary (`tool_executor.py`)
113
+ - `MappingToolExecutor` (recommended)
114
+ - `AbstractCoreToolExecutor` (legacy adapter path)
115
+ - `PassthroughToolExecutor` (host executes tools externally)
116
+ - effect handler wiring (`effect_handlers.py`)
117
+ - convenience factories (`factory.py`)
118
+ - `create_local_runtime(...)` (local execution)
119
+ - `create_remote_runtime(...)` (passthrough tools / host-mediated)
120
+
121
+ This keeps the kernel independent of AbstractCore while enabling LLM/tool workflows when a host opts in.
122
+
123
+ ## Observability (Ledger + Runtime-Owned Node Traces)
124
+
125
+ ### Ledger (Source of Truth)
126
+ Every node transition produces `StepRecord` entries appended to the `LedgerStore`.
127
+ When using `ObservableLedgerStore`, hosts can subscribe to appends for real-time UI updates.
128
+
129
+ ### Runtime-Owned Node Traces
130
+ In addition to the ledger, the runtime stores bounded, JSON-safe per-node traces under:
131
+ `run.vars["_runtime"]["node_traces"]` (see `abstractruntime/src/abstractruntime/core/runtime.py:_record_node_trace` and helpers in `abstractruntime/src/abstractruntime/core/vars.py`).
132
+
133
+ These traces support host UX needs (scratchpad/trace views) without inventing host-specific persistence formats.
134
+
135
+ ### Optional Event Bus Bridge
136
+ `abstractruntime.integrations.abstractcore.observability` can bridge ledger events to `abstractcore.events.GlobalEventBus` when desired.
137
+
138
+ ## Scheduling (Time-Based Waits)
139
+
140
+ Time-based waits (`WAIT_UNTIL`) are durable. To advance them, a host needs to tick due runs. AbstractRuntime provides:
141
+ - `create_scheduled_runtime(...)` and a `Scheduler` (`abstractruntime/src/abstractruntime/scheduler/*`)
142
+
143
+ Hosts can run a scheduler loop to periodically:
144
+ - query due `WAIT_UNTIL` runs (requires a queryable run store)
145
+ - tick them forward
146
+
147
+ ## Eventing (WAIT_EVENT / EMIT_EVENT)
148
+
149
+ Custom eventing is implemented as:
150
+ - listeners block on `WAIT_EVENT` (`WaitReason.EVENT`)
151
+ - emitters request `EffectType.EMIT_EVENT`, handled by the runtime (`Runtime._handle_emit_event`) which resumes matching `WAIT_EVENT` runs via `Runtime.resume(...)`
152
+ - requires a `QueryableRunStore` to find waiting runs and a `workflow_registry` to load target `WorkflowSpec`s
153
+
154
+ Notes:
155
+ - The emitted event **payload is normalized to a dict** for network-safe stability. If a non-dict is provided, it is wrapped as `{ "value": <payload> }`.
156
+ - `WAIT_EVENT` waits can optionally include UX metadata (`prompt`, `choices`, `allow_free_text`) so hosts can render interactive prompts while remaining fully durable.
157
+
158
+ For **host-driven external signals**, use the scheduler API:
159
+ - `Scheduler.emit_event(...)` (finds waiting runs and resumes them)
160
+
161
+ Event envelopes (scope/session/workflow) are encoded via stable wait keys (see `abstractruntime/src/abstractruntime/core/event_keys.py`).
162
+
163
+ ## Deviations / near-term work
164
+ - **Client-agnostic run history contract**: today, some hosts implement bespoke “ledger → UI events” replay logic. A runtime-owned, versioned `RunHistoryBundle` contract (planned: backlog 311) should become the shared format so any client can render consistent history and achieve stronger reproducibility.
165
+
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "AbstractRuntime"
7
- version = "0.2.0"
7
+ version = "0.4.0"
8
8
  description = "AbstractRuntime: a durable graph runner designed to pair with AbstractCore."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -33,6 +33,20 @@ classifiers = [
33
33
  "Programming Language :: Python :: 3.13",
34
34
  ]
35
35
 
36
+ [project.optional-dependencies]
37
+ # Enables `abstractruntime.integrations.abstractcore.*` imports.
38
+ abstractcore = [
39
+ "abstractcore>=2.6.8",
40
+ ]
41
+
42
+ # Enables `abstractruntime-mcp-worker` with the default toolsets (includes bs4/lxml).
43
+ mcp-worker = [
44
+ "abstractcore[tools]>=2.6.8",
45
+ ]
46
+
47
+ [project.scripts]
48
+ abstractruntime-mcp-worker = "abstractruntime.integrations.abstractcore.mcp_worker:main"
49
+
36
50
  [project.urls]
37
51
  "AbstractCore (website)" = "https://www.abstractcore.ai/"
38
52
  "AbstractCore (GitHub)" = "https://github.com/lpalbou/abstractruntime"
@@ -41,5 +55,3 @@ classifiers = [
41
55
  packages = ["src/abstractruntime"]
42
56
 
43
57
 
44
-
45
-
@@ -33,6 +33,7 @@ from .storage.base import QueryableRunStore
33
33
  from .storage.in_memory import InMemoryLedgerStore, InMemoryRunStore
34
34
  from .storage.json_files import JsonFileRunStore, JsonlLedgerStore
35
35
  from .storage.ledger_chain import HashChainedLedgerStore, verify_ledger_chain
36
+ from .storage.observable import ObservableLedgerStore, ObservableLedgerStoreProtocol
36
37
  from .storage.snapshots import Snapshot, SnapshotStore, InMemorySnapshotStore, JsonSnapshotStore
37
38
  from .storage.artifacts import (
38
39
  Artifact,
@@ -54,6 +55,7 @@ from .scheduler import (
54
55
  ScheduledRuntime,
55
56
  create_scheduled_runtime,
56
57
  )
58
+ from .memory import ActiveContextPolicy, TimeRange
57
59
 
58
60
  __all__ = [
59
61
  # Core models
@@ -81,6 +83,8 @@ __all__ = [
81
83
  "JsonlLedgerStore",
82
84
  "HashChainedLedgerStore",
83
85
  "verify_ledger_chain",
86
+ "ObservableLedgerStore",
87
+ "ObservableLedgerStoreProtocol",
84
88
  "Snapshot",
85
89
  "SnapshotStore",
86
90
  "InMemorySnapshotStore",
@@ -104,7 +108,8 @@ __all__ = [
104
108
  "RetryPolicy",
105
109
  "NoRetryPolicy",
106
110
  "compute_idempotency_key",
111
+ # Memory
112
+ "ActiveContextPolicy",
113
+ "TimeRange",
107
114
  ]
108
115
 
109
-
110
-
@@ -29,6 +29,8 @@ class RuntimeConfig:
29
29
  max_output_tokens: Maximum tokens for LLM response (None = provider default)
30
30
  warn_tokens_pct: Percentage threshold for token warnings (default: 80)
31
31
  max_history_messages: Maximum conversation history messages (-1 = unlimited)
32
+ provider: Default provider id for this Runtime (best-effort; used for run metadata)
33
+ model: Default model id for this Runtime (best-effort; used for run metadata)
32
34
  model_capabilities: Dict of model capabilities from LLM provider
33
35
 
34
36
  Example:
@@ -50,6 +52,10 @@ class RuntimeConfig:
50
52
  # History management
51
53
  max_history_messages: int = -1 # -1 = unlimited (send all messages)
52
54
 
55
+ # Default routing metadata (optional; depends on how the Runtime was constructed)
56
+ provider: Optional[str] = None
57
+ model: Optional[str] = None
58
+
53
59
  # Model capabilities (populated from LLM client)
54
60
  model_capabilities: Dict[str, Any] = field(default_factory=dict)
55
61
 
@@ -60,6 +66,11 @@ class RuntimeConfig:
60
66
  Dict with canonical limit values for storage in RunState.vars["_limits"].
61
67
  Uses model_capabilities as fallback for max_tokens if not explicitly set.
62
68
  """
69
+ max_output_tokens = self.max_output_tokens
70
+ if max_output_tokens is None:
71
+ # Best-effort: persist the provider/model default so agent logic can reason about
72
+ # output-size constraints (e.g., chunk large tool arguments like file contents).
73
+ max_output_tokens = self.model_capabilities.get("max_output_tokens")
63
74
  return {
64
75
  # Iteration control
65
76
  "max_iterations": self.max_iterations,
@@ -67,7 +78,7 @@ class RuntimeConfig:
67
78
 
68
79
  # Token management
69
80
  "max_tokens": self.max_tokens or self.model_capabilities.get("max_tokens", 32768),
70
- "max_output_tokens": self.max_output_tokens,
81
+ "max_output_tokens": max_output_tokens,
71
82
  "estimated_tokens_used": 0,
72
83
 
73
84
  # History management
@@ -97,5 +108,7 @@ class RuntimeConfig:
97
108
  max_output_tokens=self.max_output_tokens,
98
109
  warn_tokens_pct=self.warn_tokens_pct,
99
110
  max_history_messages=self.max_history_messages,
111
+ provider=self.provider,
112
+ model=self.model,
100
113
  model_capabilities=capabilities,
101
114
  )
@@ -0,0 +1,62 @@
1
+ """abstractruntime.core.event_keys
2
+
3
+ Durable event key conventions.
4
+
5
+ Why this exists:
6
+ - `WAIT_EVENT` needs a stable `wait_key` that external hosts can compute.
7
+ - Visual editors and other hosts (AbstractCode, servers) must agree on the same
8
+ key format without importing UI-specific code.
9
+
10
+ We keep this module dependency-light (stdlib only).
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Optional
16
+
17
+
18
+ def build_event_wait_key(
19
+ *,
20
+ scope: str,
21
+ name: str,
22
+ session_id: Optional[str] = None,
23
+ workflow_id: Optional[str] = None,
24
+ run_id: Optional[str] = None,
25
+ ) -> str:
26
+ """Build a durable wait_key for event-driven workflows.
27
+
28
+ Format:
29
+ evt:{scope}:{scope_id}:{name}
30
+
31
+ Scopes:
32
+ - session: `scope_id` is the workflow instance/session identifier (recommended default)
33
+ - workflow: `scope_id` is the workflow_id
34
+ - run: `scope_id` is the run_id
35
+ - global: `scope_id` is the literal string "global"
36
+ """
37
+ scope_norm = str(scope or "session").strip().lower()
38
+ name_norm = str(name or "").strip()
39
+ if not name_norm:
40
+ raise ValueError("event name is required")
41
+
42
+ scope_id: Optional[str]
43
+ if scope_norm == "session":
44
+ scope_id = str(session_id or "").strip() if session_id is not None else ""
45
+ elif scope_norm == "workflow":
46
+ scope_id = str(workflow_id or "").strip() if workflow_id is not None else ""
47
+ elif scope_norm == "run":
48
+ scope_id = str(run_id or "").strip() if run_id is not None else ""
49
+ elif scope_norm == "global":
50
+ scope_id = "global"
51
+ else:
52
+ raise ValueError(f"unknown event scope: {scope!r}")
53
+
54
+ if not scope_id:
55
+ raise ValueError(f"missing scope id for scope={scope_norm!r}")
56
+
57
+ return f"evt:{scope_norm}:{scope_id}:{name_norm}"
58
+
59
+
60
+
61
+
62
+
@@ -46,10 +46,22 @@ class EffectType(str, Enum):
46
46
  WAIT_EVENT = "wait_event"
47
47
  WAIT_UNTIL = "wait_until"
48
48
  ASK_USER = "ask_user"
49
+ ANSWER_USER = "answer_user"
50
+
51
+ # Eventing
52
+ EMIT_EVENT = "emit_event"
49
53
 
50
54
  # Integrations (implemented via pluggable handlers)
51
55
  LLM_CALL = "llm_call"
52
56
  TOOL_CALLS = "tool_calls"
57
+ MEMORY_QUERY = "memory_query"
58
+ MEMORY_TAG = "memory_tag"
59
+ MEMORY_COMPACT = "memory_compact"
60
+ MEMORY_NOTE = "memory_note"
61
+ MEMORY_REHYDRATE = "memory_rehydrate"
62
+
63
+ # Debug / inspection (schema-only tools -> runtime effects)
64
+ VARS_QUERY = "vars_query"
53
65
 
54
66
  # Composition
55
67
  START_SUBWORKFLOW = "start_subworkflow"
@@ -279,4 +291,3 @@ class LimitWarning:
279
291
  def __post_init__(self) -> None:
280
292
  if self.maximum > 0:
281
293
  self.pct = round(self.current / self.maximum * 100, 1)
282
-