phronomy 0.17.0 → 0.19.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +122 -1134
- data/README.md +70 -1280
- data/benchmark/bench_regression.rb +25 -5
- data/docs/changelog/0.14-and-earlier.md +1137 -0
- data/docs/decisions/008-orchestrator-uses-os-threads.md +46 -48
- data/docs/decisions/009-state-store-abstraction.md +1 -1
- data/docs/decisions/010-cooperative-first-concurrency.md +155 -235
- data/docs/decisions/014-unified-persistence-durable-state.md +268 -0
- data/docs/features.md +93 -0
- data/docs/getting-started.md +387 -0
- data/docs/migrations/0.15.md +35 -0
- data/docs/migrations/0.16.md +43 -0
- data/docs/migrations/0.19.md +148 -0
- data/docs/runtime-and-concurrency.md +349 -0
- data/examples/workflows/generic_task_event_mapping.rb +14 -6
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +2 -2
- data/lib/phronomy/agent/async_event_api.rb +3 -3
- data/lib/phronomy/agent/base.rb +116 -55
- data/lib/phronomy/agent/context/capability/base.rb +13 -3
- data/lib/phronomy/agent/context_assembler.rb +13 -3
- data/lib/phronomy/agent/execution_coordinator.rb +423 -252
- data/lib/phronomy/agent/journal_projection.rb +5 -1
- data/lib/phronomy/agent/shared_state.rb +2 -0
- data/lib/phronomy/agent/tool_executor.rb +29 -71
- data/lib/phronomy/agent/tool_invocation.rb +97 -47
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +55 -161
- data/lib/phronomy/configuration.rb +7 -30
- data/lib/phronomy/diagnostics.rb +12 -41
- data/lib/phronomy/engine/concurrency/async_queue.rb +5 -188
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +6 -7
- data/lib/phronomy/engine/concurrency/cancellation_token.rb +48 -3
- data/lib/phronomy/engine/concurrency/deadline.rb +2 -3
- data/lib/phronomy/engine/concurrency/offload_pool.rb +696 -0
- data/lib/phronomy/engine/concurrency/pool_registry.rb +5 -5
- data/lib/phronomy/engine/event_loop.rb +170 -193
- data/lib/phronomy/engine/fsm_session.rb +6 -4
- data/lib/phronomy/engine/runtime/timer_queue.rb +48 -71
- data/lib/phronomy/engine/runtime/timer_service.rb +13 -21
- data/lib/phronomy/engine/runtime.rb +52 -158
- data/lib/phronomy/engine/task.rb +136 -277
- data/lib/phronomy/llm_adapter/base.rb +14 -14
- data/lib/phronomy/llm_adapter/ruby_llm.rb +3 -4
- data/lib/phronomy/llm_adapter.rb +2 -2
- data/lib/phronomy/metrics.rb +15 -30
- data/lib/phronomy/multi_agent/fan_out_invocation.rb +146 -0
- data/lib/phronomy/multi_agent/fan_out_session_builder.rb +125 -0
- data/lib/phronomy/multi_agent/handoff.rb +1 -0
- data/lib/phronomy/multi_agent/orchestrator.rb +147 -99
- data/lib/phronomy/multi_agent/team_coordinator.rb +2 -0
- data/lib/phronomy/persistence/in_memory.rb +113 -8
- data/lib/phronomy/persistence.rb +12 -3
- data/lib/phronomy/testing/eval/comparison.rb +23 -0
- data/lib/phronomy/testing/eval/dataset.rb +27 -0
- data/lib/phronomy/testing/eval/eval_case.rb +13 -0
- data/lib/phronomy/testing/eval/eval_result.rb +16 -0
- data/lib/phronomy/testing/eval/metrics.rb +43 -0
- data/lib/phronomy/testing/eval/runner.rb +52 -0
- data/lib/phronomy/testing/eval/scorer/base.rb +15 -0
- data/lib/phronomy/testing/eval/scorer/exact_match.rb +25 -0
- data/lib/phronomy/testing/eval/scorer/includes_scorer.rb +25 -0
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +46 -0
- data/lib/phronomy/testing/eval/scorer.rb +10 -0
- data/lib/phronomy/testing/eval.rb +9 -0
- data/lib/phronomy/testing/fake_clock.rb +6 -53
- data/lib/phronomy/testing.rb +2 -6
- data/lib/phronomy/tools/agent.rb +141 -6
- data/lib/phronomy/vector_store/async_backend.rb +21 -17
- data/lib/phronomy/vector_store/base.rb +2 -2
- data/lib/phronomy/vector_store/embeddings/base.rb +6 -5
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow.rb +10 -9
- data/lib/phronomy/workflow_runner.rb +363 -99
- data/lib/phronomy.rb +10 -4
- data/scripts/api_snapshot.rb +4 -4
- metadata +25 -33
- data/lib/phronomy/engine/concurrency/blocking_adapter_pool.rb +0 -561
- data/lib/phronomy/engine/runtime/deterministic_scheduler.rb +0 -439
- data/lib/phronomy/engine/runtime/fake_scheduler.rb +0 -165
- data/lib/phronomy/engine/runtime/runtime_metrics.rb +0 -116
- data/lib/phronomy/engine/runtime/scheduler.rb +0 -98
- data/lib/phronomy/engine/runtime/scheduler_timer_adapter.rb +0 -79
- data/lib/phronomy/engine/runtime/task_registry.rb +0 -95
- data/lib/phronomy/engine/runtime/thread_scheduler.rb +0 -30
- data/lib/phronomy/engine/task/backend.rb +0 -80
- data/lib/phronomy/engine/task/deferred_backend.rb +0 -73
- data/lib/phronomy/engine/task/fiber_backend.rb +0 -157
- data/lib/phronomy/engine/task/immediate_backend.rb +0 -89
- data/lib/phronomy/engine/task/mapped_backend.rb +0 -90
- data/lib/phronomy/engine/task/thread_backend.rb +0 -84
- data/lib/phronomy/engine/task_group.rb +0 -193
- data/lib/phronomy/eval/comparison.rb +0 -47
- data/lib/phronomy/eval/dataset.rb +0 -45
- data/lib/phronomy/eval/eval_case.rb +0 -17
- data/lib/phronomy/eval/eval_result.rb +0 -29
- data/lib/phronomy/eval/metrics.rb +0 -66
- data/lib/phronomy/eval/runner.rb +0 -94
- data/lib/phronomy/eval/scorer/base.rb +0 -22
- data/lib/phronomy/eval/scorer/exact_match.rb +0 -31
- data/lib/phronomy/eval/scorer/includes_scorer.rb +0 -32
- data/lib/phronomy/eval/scorer/llm_judge.rb +0 -72
- data/lib/phronomy/eval/scorer.rb +0 -9
- data/lib/phronomy/eval.rb +0 -7
- data/lib/phronomy/state_store/base.rb +0 -48
- data/lib/phronomy/state_store/in_memory.rb +0 -62
- data/lib/phronomy/testing/fake_scheduler.rb +0 -104
- data/lib/phronomy/testing/scheduler_helpers.rb +0 -68
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# ADR 014: Unified Persistence for Durable State
|
|
2
|
+
|
|
3
|
+
**Status**: Accepted
|
|
4
|
+
**Date**: 2026-08-14
|
|
5
|
+
**Supersedes**: ADR-009
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Phronomy historically had two durable-state abstractions:
|
|
12
|
+
|
|
13
|
+
- `Phronomy::Persistence` for Agent roots, Journals, content and executions;
|
|
14
|
+
- `Phronomy::StateStore` for Workflow snapshots.
|
|
15
|
+
|
|
16
|
+
At the same time, `Persistence` exposed `activations`, although
|
|
17
|
+
`AgentExecutionActivation` contains live process state such as the active
|
|
18
|
+
AgentInvocation, FSMSession, runtime projection, callbacks and uncommitted runtime
|
|
19
|
+
facts. That state is not a durable database record.
|
|
20
|
+
|
|
21
|
+
Keeping two durable backend abstractions makes SQL/database backends implement the
|
|
22
|
+
same responsibility twice. Treating live Activations as Persistence also mixes
|
|
23
|
+
process-local execution ownership with durable storage ownership.
|
|
24
|
+
|
|
25
|
+
A second problem is state freshness. Re-reading mutable Agent records from
|
|
26
|
+
Persistence before every semantic boundary makes the durable copy act as the
|
|
27
|
+
source of truth even while a live Agent instance is executing. That makes
|
|
28
|
+
ownership ambiguous and encourages implicit merge/reload semantics.
|
|
29
|
+
|
|
30
|
+
Workflow runtime identity also needs to be distinct from its durable identity.
|
|
31
|
+
`InvocationContext#session_id` already means application session metadata (for
|
|
32
|
+
example a Rails session), while Workflow `thread_id` identifies durable Workflow
|
|
33
|
+
state. Neither value is the identity of one concrete FSMSession execution.
|
|
34
|
+
|
|
35
|
+
## Decision
|
|
36
|
+
|
|
37
|
+
### Persistence is the only durable backend abstraction
|
|
38
|
+
|
|
39
|
+
`Phronomy::Persistence` exposes the durable repositories:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
contents
|
|
43
|
+
agents
|
|
44
|
+
journals
|
|
45
|
+
executions
|
|
46
|
+
workflow_states
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`Phronomy::StateStore` is removed without a compatibility adapter. Workflow
|
|
50
|
+
persistence is configured with `persistence:` or
|
|
51
|
+
`Phronomy.configuration.persistence` and stored through
|
|
52
|
+
`Persistence#workflow_states`. Agent `new`/`create` also uses the global
|
|
53
|
+
Persistence when no explicit backend is injected, so an application can select
|
|
54
|
+
one durable backend for both domains. Explicit injection takes precedence.
|
|
55
|
+
|
|
56
|
+
The `workflow_states` repository uses optimistic revision metadata:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
record = persistence.workflow_states.load(thread_id)
|
|
60
|
+
# => nil or { snapshot: ..., revision: Integer }
|
|
61
|
+
|
|
62
|
+
persistence.workflow_states.save(
|
|
63
|
+
thread_id,
|
|
64
|
+
expected_revision: record&.fetch(:revision),
|
|
65
|
+
snapshot: snapshot
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
A stale expected revision raises `Phronomy::Persistence::ConflictError`. Upper
|
|
70
|
+
layers do not automatically reload and merge after such a conflict.
|
|
71
|
+
|
|
72
|
+
### Activation is transient Runtime state
|
|
73
|
+
|
|
74
|
+
`AgentExecutionActivation` is not part of the Persistence contract. Live
|
|
75
|
+
Activations are held by a Runtime-local `Agent::ActivationRegistry`.
|
|
76
|
+
|
|
77
|
+
Durable execution rehydration is a separate future capability. When no live
|
|
78
|
+
Activation exists, approval resume raises
|
|
79
|
+
`ExecutionRehydrationRequiredError`; the framework does not silently construct a
|
|
80
|
+
new Agent and pretend that the process-local execution was recovered.
|
|
81
|
+
|
|
82
|
+
### A live Agent instance owns its logical current state
|
|
83
|
+
|
|
84
|
+
After hydration, the live Agent instance and its Activation are authoritative for
|
|
85
|
+
mutable logical state:
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
Agent instance
|
|
89
|
+
├─ current AgentRoot
|
|
90
|
+
├─ hydrated Journal/context view
|
|
91
|
+
└─ active execution entry
|
|
92
|
+
|
|
93
|
+
AgentExecutionActivation
|
|
94
|
+
├─ current AgentExecution
|
|
95
|
+
├─ runtime projection
|
|
96
|
+
├─ Provider results
|
|
97
|
+
├─ Tool/runtime events
|
|
98
|
+
└─ active AgentInvocation / call state
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Persistence is the last committed durable representation and recovery source.
|
|
102
|
+
Normal LLM/Tool cycles do not re-read mutable AgentRoot, AgentExecution or Journal
|
|
103
|
+
records merely to obtain freshness. Successful optimistic commits advance the
|
|
104
|
+
local root/execution/Journals; failed revision checks surface a conflict.
|
|
105
|
+
|
|
106
|
+
Before a next-LLM durable barrier, the backend verifies the live owner's Agent
|
|
107
|
+
revision and Journal position as a durable watermark. This check is a precondition
|
|
108
|
+
only: it does not return replacement state to the live Agent. If another writer
|
|
109
|
+
has advanced either value, the barrier raises `ConflictError` and no Provider
|
|
110
|
+
Call starts from the stale local owner.
|
|
111
|
+
|
|
112
|
+
Content-addressed `contents` are immutable values and may still be dereferenced by
|
|
113
|
+
content reference on demand. That is not a mutable-state refresh.
|
|
114
|
+
|
|
115
|
+
Context Policy therefore sees the current local logical state rather than a
|
|
116
|
+
Persistence readback. Runtime facts captured after a Persistence snapshot remain
|
|
117
|
+
in the Activation and are eligible at the next semantic boundary.
|
|
118
|
+
|
|
119
|
+
### Approval resumes the same owner
|
|
120
|
+
|
|
121
|
+
An approval suspension retains the original Agent instance,
|
|
122
|
+
`AgentExecutionActivation` and `AgentInvocation`. Instance-level
|
|
123
|
+
`agent.approve` / `agent.approve_async` resume that live Activation.
|
|
124
|
+
|
|
125
|
+
When an application has only an `execution_id`, it resolves the current process's
|
|
126
|
+
live owner first:
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
execution_id
|
|
130
|
+
-> Agent::Base.live_for_execution(...)
|
|
131
|
+
-> Runtime ActivationRegistry
|
|
132
|
+
-> live Activation
|
|
133
|
+
-> activation.agent
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A concrete Agent class may call `MyAgent.live_for_execution(execution_id)` to
|
|
137
|
+
also verify that the live owner is an instance of that class. The lookup does not
|
|
138
|
+
load Agent or Execution state from Persistence. If the live Activation no longer
|
|
139
|
+
exists, `ExecutionRehydrationRequiredError` is raised rather than silently
|
|
140
|
+
rehydrating another owner.
|
|
141
|
+
|
|
142
|
+
Approval remains an Agent-instance operation after lookup:
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
agent = MyAgent.live_for_execution(execution_id)
|
|
146
|
+
agent.approve_async(execution_id, approval_request_id: request_id)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Workflow has three distinct identities
|
|
150
|
+
|
|
151
|
+
The following identities must not be conflated:
|
|
152
|
+
|
|
153
|
+
```text
|
|
154
|
+
application session_id
|
|
155
|
+
caller/tracing identity, e.g. Rails session
|
|
156
|
+
|
|
157
|
+
thread_id
|
|
158
|
+
durable Workflow identity / workflow_states key
|
|
159
|
+
|
|
160
|
+
fsm_session_id
|
|
161
|
+
Runtime-only identity of one FSMSession invocation or resume
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Each Workflow invocation/resume receives a fresh `fsm_session_id`. The EventLoop
|
|
165
|
+
registers the session by that ID.
|
|
166
|
+
|
|
167
|
+
For a Workflow with a durable `thread_id`, EventLoop also owns a separate
|
|
168
|
+
admission table:
|
|
169
|
+
|
|
170
|
+
```text
|
|
171
|
+
thread_id -> owner_fsm_session_id
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Admission is acquired before Persistence load and held through FSM execution and
|
|
175
|
+
terminal snapshot save. Release succeeds only when the supplied
|
|
176
|
+
`owner_fsm_session_id` is the current owner. A failed competing invocation can
|
|
177
|
+
therefore never release another invocation's reservation.
|
|
178
|
+
|
|
179
|
+
The admission table belongs to one Runtime and is process-local. It prevents
|
|
180
|
+
competing execution of the same `thread_id` inside that Runtime, but it is not a
|
|
181
|
+
distributed lease and is not shared by multiple Ruby processes, containers, or
|
|
182
|
+
service replicas. Two processes may therefore execute the same durable
|
|
183
|
+
`thread_id` concurrently if an application routes work that way.
|
|
184
|
+
|
|
185
|
+
`workflow_states` optimistic revisions still detect stale/double commits between
|
|
186
|
+
those processes. They do not prevent both executions from starting and cannot
|
|
187
|
+
roll back external side effects already performed before one terminal save loses
|
|
188
|
+
the revision race. CAS is therefore stale/double-commit protection, not
|
|
189
|
+
distributed execution exclusion or duplicate-side-effect prevention.
|
|
190
|
+
|
|
191
|
+
`fsm_session_id` is Runtime metadata. It is not an application Workflow field and
|
|
192
|
+
is not stored in `workflow_states` snapshots.
|
|
193
|
+
|
|
194
|
+
### Persistence I/O stays off EventLoop
|
|
195
|
+
|
|
196
|
+
The Persistence repository contract remains synchronous. Persistence operations
|
|
197
|
+
started by EventLoop-driven Workflow/Agent lifecycle code execute through the
|
|
198
|
+
bounded OffloadPool. Completion is converted back into an EventLoop event where
|
|
199
|
+
lifecycle ordering requires it.
|
|
200
|
+
|
|
201
|
+
No worker waits synchronously for an FSMSession or Task that requires EventLoop
|
|
202
|
+
progress.
|
|
203
|
+
|
|
204
|
+
### Backend synchronization is an implementation detail
|
|
205
|
+
|
|
206
|
+
`Persistence::InMemory` uses one shared `Monitor` for all durable repositories,
|
|
207
|
+
including `workflow_states`. Workflow values retain StateStore-like deep-copy
|
|
208
|
+
semantics and are kept outside the Marshal snapshot used by the existing Agent
|
|
209
|
+
state container, while both remain inside the same transaction domain.
|
|
210
|
+
|
|
211
|
+
A SQL backend should implement the same repository/transaction contract using DB
|
|
212
|
+
transactions, isolation and optimistic revision constraints. Mutex/Monitor and
|
|
213
|
+
Ruby object identity are not part of the public repository contract.
|
|
214
|
+
|
|
215
|
+
## Consequences
|
|
216
|
+
|
|
217
|
+
### Positive
|
|
218
|
+
|
|
219
|
+
- Agent and Workflow durability share one backend configuration and transaction
|
|
220
|
+
abstraction.
|
|
221
|
+
- Process-local Activation state is no longer presented as durable Persistence.
|
|
222
|
+
- Active Agent ownership is explicit; Context Policy freshness no longer depends
|
|
223
|
+
on repeated mutable-state reloads.
|
|
224
|
+
- Approval owner lookup and approval execution are separate: class-level lookup
|
|
225
|
+
resolves the existing live Agent, while approval remains an instance operation.
|
|
226
|
+
- Revision conflicts are visible instead of being hidden by automatic merge.
|
|
227
|
+
- Workflow durable identity is independent of one Runtime FSMSession execution.
|
|
228
|
+
- Owner-aware Workflow admission closes the stale-load window during terminal
|
|
229
|
+
save inside one Runtime.
|
|
230
|
+
- InMemory and future SQL backends can share one contract test suite.
|
|
231
|
+
|
|
232
|
+
### Trade-offs
|
|
233
|
+
|
|
234
|
+
- Removing `StateStore` is a pre-1.0 breaking change.
|
|
235
|
+
- A process restart cannot resume an approval until durable Activation/FSM
|
|
236
|
+
rehydration is implemented separately.
|
|
237
|
+
- Runtime Activation lookup and Workflow admission are process-local. Applications
|
|
238
|
+
requiring cross-process ownership or duplicate-execution prevention need a
|
|
239
|
+
separate routing/lease/fencing design.
|
|
240
|
+
- Optimistic Workflow revisions can reject a stale commit but cannot undo
|
|
241
|
+
duplicate external side effects performed before that conflict is observed.
|
|
242
|
+
- Applications that intentionally allow another process to edit the same live
|
|
243
|
+
Agent or Workflow must handle `ConflictError` and explicitly reload/reconcile.
|
|
244
|
+
- `Persistence` implementations must provide `workflow_states` and optimistic
|
|
245
|
+
revision semantics.
|
|
246
|
+
|
|
247
|
+
## Rejected alternatives
|
|
248
|
+
|
|
249
|
+
### Keep StateStore as a compatibility facade
|
|
250
|
+
|
|
251
|
+
Rejected. It preserves the duplicate durable-backend abstraction and makes future
|
|
252
|
+
backend behavior harder to reason about.
|
|
253
|
+
|
|
254
|
+
### Reload Agent state before every LLM call
|
|
255
|
+
|
|
256
|
+
Rejected. It makes Persistence the live source of truth, creates implicit merge
|
|
257
|
+
semantics and conflicts with Agent-instance ownership.
|
|
258
|
+
|
|
259
|
+
### Use Workflow thread_id as FSMSession id
|
|
260
|
+
|
|
261
|
+
Rejected. Durable identity and one Runtime execution have different lifetimes.
|
|
262
|
+
It also makes owner-aware admission and application `session_id` terminology
|
|
263
|
+
ambiguous.
|
|
264
|
+
|
|
265
|
+
### Release Workflow admission by thread_id only
|
|
266
|
+
|
|
267
|
+
Rejected. Cleanup from an invocation that failed to acquire admission could then
|
|
268
|
+
release the real owner's reservation. Release is owner-aware by construction.
|
data/docs/features.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Features and API stability
|
|
2
|
+
|
|
3
|
+
API means Application Programming Interface in this document.
|
|
4
|
+
|
|
5
|
+
Phronomy is pre-1.0. Minor releases may include breaking changes even to APIs
|
|
6
|
+
labelled Stable; patch releases should remain non-breaking. Consult
|
|
7
|
+
[`CHANGELOG.md`](../CHANGELOG.md) when upgrading.
|
|
8
|
+
|
|
9
|
+
- **Stable** — API is considered complete and suitable for production use.
|
|
10
|
+
- **Beta** — functionality is complete and tested, but signatures or behaviour may evolve in a minor release.
|
|
11
|
+
- **Experimental** — functionality may change without notice and is not recommended as a long-term compatibility boundary.
|
|
12
|
+
|
|
13
|
+
The `main` branch contains unreleased development work. Pin a released gem version
|
|
14
|
+
for production deployments.
|
|
15
|
+
|
|
16
|
+
## Core building blocks
|
|
17
|
+
|
|
18
|
+
| Feature | Stability |
|
|
19
|
+
|---|---|
|
|
20
|
+
| **Workflow** — Stateful, branching workflows with `wait_state` and explicit events | Stable |
|
|
21
|
+
| **Agent** — Stateful ReAct-style agents with stable `agent_id`, persistence-backed execution state, canonical history, guardrails, and conversation context | Stable |
|
|
22
|
+
| **Unified Persistence** — One durable backend abstraction for Agent state and Workflow `workflow_states`; live Agent/Workflow state remains owned by the active instance/session between durable commits | Beta |
|
|
23
|
+
| **Before-Large-Language-Model (LLM) Input Hook** — Three-tier per-call LLM input customization via `before_llm_input` and `LLMInputPatch` | Stable |
|
|
24
|
+
| **Context Management** — Journal + Context Policy + per-LLM-call Manifest with token-budget-aware selection and protocol-safe Tool Call / Tool message dependencies | Stable |
|
|
25
|
+
| **Filters** — Input/output transformation and blocking via `Filter::Base` | Beta |
|
|
26
|
+
| **`PromptInjectionFilter`** — Built-in pattern-based prompt-injection filter | Beta |
|
|
27
|
+
| **Capability redaction/result-size controls** — `redact_params` and `max_result_size` | Beta |
|
|
28
|
+
| **Output Parser** — JSON and Struct-mapped parsers for structured LLM responses | Stable |
|
|
29
|
+
| **Tracing** — Pluggable span-based observability | Stable |
|
|
30
|
+
| **Error Taxonomy** — Provider errors translated to Phronomy transport/authentication/rate-limit/context errors | Beta |
|
|
31
|
+
|
|
32
|
+
## Knowledge and integration
|
|
33
|
+
|
|
34
|
+
| Feature | Stability |
|
|
35
|
+
|---|---|
|
|
36
|
+
| **Knowledge** — Journal-backed persistent Agent context registered with `knowledge:` / `add_knowledge`, selected per LLM call by Context Policy | Beta |
|
|
37
|
+
| **`VectorStore#size`** — Document count for InMemory, RedisSearch, and Pgvector backends | Beta |
|
|
38
|
+
| **`VectorStore::AsyncBackend`** — Pluggable async VectorStore interface with pool-backed defaults and native-async override points | Beta |
|
|
39
|
+
| **Model Context Protocol (MCP) Tool** — `Phronomy::Tools::Mcp` integration through the official `mcp` gem | Beta |
|
|
40
|
+
| **Agent Tool** — `Phronomy::Tools::Agent.from_agent` exposes a child Agent as a Tool without occupying a worker while waiting | Beta |
|
|
41
|
+
| **Vector Search Tool** — `Phronomy::Tools::VectorSearch` wraps VectorStore and Embeddings adapters | Beta |
|
|
42
|
+
|
|
43
|
+
## Execution and reliability
|
|
44
|
+
|
|
45
|
+
| Feature | Stability |
|
|
46
|
+
|---|---|
|
|
47
|
+
| **EventLoop** — Runtime-owned event-driven execution core shared by Agent, ToolInvocation, Workflow, and MultiAgent sessions | Beta |
|
|
48
|
+
| **Workflow durable admission** — Durable `thread_id` ownership is held by a Runtime-only `fsm_session_id` from load through terminal save | Beta |
|
|
49
|
+
| **`invoke` / `invoke_async`** — Blocking and non-blocking Agent/Workflow entry points | Stable |
|
|
50
|
+
| **Agent async events** — `invoke_async(..., on_event:)` and `stream_async(..., on_event:)`; streaming additionally emits `:token` | Beta |
|
|
51
|
+
| **`stream` / `stream_async`** — Event callbacks execute on EventLoop and must return quickly | Beta |
|
|
52
|
+
| **`stream_callback_error_policy`** — Terminal event callback error policy (`:report` / `:fail_task`) | Beta |
|
|
53
|
+
| **`Task#map`** — Application-level Task result transformation and error propagation | Stable |
|
|
54
|
+
| **CancellationToken** — Cooperative cancellation with explicit `cancel!`, lazy monotonic deadlines, and callback registration | Experimental |
|
|
55
|
+
| **Tool `execution_mode`** — `:cooperative` for short EventLoop-safe work; `:offloaded` for synchronous work that must stay off EventLoop | Experimental |
|
|
56
|
+
| **OffloadPool sizing** — `offload_pool_size` / `offload_queue_size`; named pools available for application-owned isolation | Beta |
|
|
57
|
+
| **InvocationContext** — Explicit correlation/cancellation/deadline context for Agent and Workflow invocations | Beta |
|
|
58
|
+
| **Metrics** — OffloadPool active/queue/abandoned metrics plus EventLoop queue/lag metrics | Beta |
|
|
59
|
+
| **Runtime lifecycle** — Runtime-owned EventLoop and terminal `Runtime#shutdown` | Beta |
|
|
60
|
+
|
|
61
|
+
## Agent and workflow patterns
|
|
62
|
+
|
|
63
|
+
| Feature | Stability |
|
|
64
|
+
|---|---|
|
|
65
|
+
| **Workflow asynchronous pattern** — Start async work, return immediately, and continue through `Workflow#signal` | Beta |
|
|
66
|
+
| **Multi-agent** — Agent-as-Tool and hub-and-spoke handoff routing | Beta |
|
|
67
|
+
| **GeneratorVerifier** — Generator-Verifier loop with injectable prompts/parsers | Beta |
|
|
68
|
+
| **`Phronomy::MultiAgent::Orchestrator`** — Parallel subagent dispatch, fan-out, and `subagent` DSL | Beta |
|
|
69
|
+
| **`Phronomy::MultiAgent::TeamCoordinator`** — LLM coordinator with stateful worker Agents | Beta |
|
|
70
|
+
| **SharedState** — Peer-agent shared-state coordination | Experimental |
|
|
71
|
+
| **Human-in-the-loop approval** — Suspension and approval/resume of Tool requests on the same live Agent/Activation owner | Beta |
|
|
72
|
+
| **`tool_approval_policy`** — Application-defined allow/approve/reject policy | Beta |
|
|
73
|
+
|
|
74
|
+
## Public API boundary
|
|
75
|
+
|
|
76
|
+
The feature tables above describe the primary APIs intended for gem consumers.
|
|
77
|
+
Source declarations marked `@api private`, including most EventLoop/FSMSession and
|
|
78
|
+
OffloadPool internals, are implementation details and may change without the same
|
|
79
|
+
compatibility guarantees.
|
|
80
|
+
|
|
81
|
+
`Phronomy::StateStore` is no longer a public backend abstraction. Workflow
|
|
82
|
+
durability is provided through `Phronomy::Persistence#workflow_states`; see the
|
|
83
|
+
0.19 migration guide when upgrading code that used `state_store:`.
|
|
84
|
+
|
|
85
|
+
## Advanced and internal APIs
|
|
86
|
+
|
|
87
|
+
| Feature | Stability |
|
|
88
|
+
|---|---|
|
|
89
|
+
| **`Phronomy::Diagnostics`** — Snapshot of EventLoop lag/queue state and OffloadPool activity | Experimental |
|
|
90
|
+
| **`Phronomy::Testing::FakeClock`** — Test-only deterministic clock helper | Beta |
|
|
91
|
+
|
|
92
|
+
For runtime ownership and the distinction between public lifecycle APIs and
|
|
93
|
+
private execution machinery, see [Runtime and concurrency](runtime-and-concurrency.md).
|