phronomy 0.30.0 → 0.31.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 +76 -0
- data/README.md +7 -2
- data/docs/architecture/agent-tool-child-lifetime.md +141 -0
- data/docs/architecture/dx05-agent-scope.md +4 -2
- data/docs/architecture/r8-unit1.md +68 -68
- data/docs/architecture/r8-unit2.md +34 -34
- data/docs/architecture/structured-output-and-scope.md +73 -13
- data/docs/decisions/029-semantic-completion-and-application-effect-boundary.md +1 -1
- data/docs/decisions/044-agent-default-and-one-shot-composition.md +5 -0
- data/docs/design/durable-semantic-coordination/CHANGELOG_V2_REVISION_2.md +23 -23
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +99 -99
- data/docs/design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md +135 -136
- data/docs/getting-started.md +26 -0
- data/docs/migrations/0.31.md +72 -0
- data/lib/phronomy/agent/async_event_api.rb +17 -8
- data/lib/phronomy/agent/base.rb +27 -2
- data/lib/phronomy/agent/composition/run_once.rb +11 -10
- data/lib/phronomy/agent/context_preparation.rb +1 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +1 -1
- data/lib/phronomy/agent/execution/exact_execution.rb +5 -3
- data/lib/phronomy/agent/execution/execution_coordinator.rb +6 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +1 -6
- data/lib/phronomy/agent/execution/initial_preparation.rb +1 -1
- data/lib/phronomy/agent/execution/invocation_actions.rb +17 -15
- data/lib/phronomy/agent/execution_change.rb +4 -0
- data/lib/phronomy/agent/output_contract.rb +47 -5
- data/lib/phronomy/agent/persistence/codec.rb +16 -2
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +1 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +2 -2
- data/lib/phronomy/agent/runtime_input.rb +7 -0
- data/lib/phronomy/engine/fsm_session.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +16 -0
- data/lib/phronomy/execution/concurrency/result_finalization.rb +106 -0
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +3 -3
- data/lib/phronomy/generation/generator_verifier.rb +6 -3
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +1 -1
- data/lib/phronomy/llm_adapter/backends/structured_response_capture.rb +3 -1
- data/lib/phronomy/tools/agent.rb +58 -22
- data/lib/phronomy/version.rb +1 -1
- data/sig/phronomy/agent.rbs +7 -5
- data/sig/phronomy/execution/async_operation.rbs +1 -0
- data/sig/phronomy/generation/generator_verifier.rbs +1 -1
- data/sig/phronomy/tools/agent.rbs +14 -0
- metadata +6 -2
|
@@ -1,179 +1,178 @@
|
|
|
1
|
-
# V2
|
|
1
|
+
# V2 Recovery Contract Clarifications - Approved Addendum
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Updated: 2026-09-06 / Document revision: V2 revision 2
|
|
4
4
|
|
|
5
|
-
## 0.
|
|
5
|
+
## 0. Purpose and authority
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
ADR-029/030/031
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
This addendum is a normative implementation contract reflecting the user's approval of five additions to V2.
|
|
8
|
+
Apply it together with ADR-029/030/031 and Implementation Design V2.
|
|
9
|
+
It preserves the original V2 responsibility boundaries while clarifying result access, unknown persistence outcomes, wiring compatibility, cancellation, and guarantee wording.
|
|
10
|
+
Where an earlier statement on the same issue differs, these clarifications take precedence. They do not otherwise replace existing ADR contracts.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
The baseline remains `5472116cd99a63ec27875024c955ea82be612d6b`.
|
|
13
|
+
This is specification approval, not evidence of implementation completion or successful tests.
|
|
14
|
+
Public method names, exception names, and mappings to the existing cancellation implementation will be finalized after inspecting the baseline.
|
|
15
|
+
Requirement IDs in this document are for traceability; they are not new Runtime IDs, persistent IDs, or APIs.
|
|
16
16
|
|
|
17
|
-
## 1. RC-01
|
|
17
|
+
## 1. RC-01: Accessing final results and discovering executions
|
|
18
18
|
|
|
19
|
-
###
|
|
19
|
+
### Required contract
|
|
20
20
|
|
|
21
|
-
- Application
|
|
22
|
-
-
|
|
23
|
-
- Handoff
|
|
24
|
-
- admission
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
21
|
+
- Application can obtain the owner identity, execution state, and committed result or error by semantic execution ID through read-only access.
|
|
22
|
+
- This covers AgentExecution and TeamExecution. Handoff/Orchestrator use existing routing anchors, parent executions, and reserved child/Target IDs; they do not create a new execution entity just for result access.
|
|
23
|
+
- Handoff identifies the Target outcome associated with the specified execution. The latest active Agent result from a later turn must not substitute for an earlier invocation's result.
|
|
24
|
+
- Provide a public path to discover execution candidates from a known agent_id/team_id, covering an interruption after admission commits but before Application receives the execution ID. Include terminal as well as active executions.
|
|
25
|
+
- Result access and candidate enumeration must not invoke Providers/Tools, continue Agent execution, redeliver callbacks, or acquire ownership. Distinguish these operations from operations that initiate recovery.
|
|
26
|
+
- Distinguish an unresolved result, absence, and inability to read. Do not translate Persistence failures or result-materialization failures into no execution, in progress, or successful completion without a result.
|
|
27
|
+
- Read basic state and committed results from persisted records. Do not require the graph/Procs needed for continuation merely to access results. Preserve existing codec/schema compatibility requirements.
|
|
28
28
|
|
|
29
|
-
###
|
|
29
|
+
### Scope limitations
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Application
|
|
31
|
+
Candidate discovery does not guarantee automatic unique correspondence to an Application request or exactly-once submission.
|
|
32
|
+
When multiple candidates exist, do not select one merely because it has the latest timestamp; Application must match its own correlation information.
|
|
33
|
+
Application-specific request IDs, outboxes, and deduplication remain Application responsibilities. Do not add generic idempotency infrastructure solely to meet result-access requirements.
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
Preserve existing retention/deletion contracts. Restoring deleted results, indefinite retention, and a new history-management service are not required.
|
|
36
|
+
Inspect existing API/SPI behavior and map list scope, ordering, pagination, and similar concerns to the smallest necessary public path.
|
|
37
|
+
Add only capabilities that existing APIs cannot provide; do not expose internal repositories directly as the public contract.
|
|
38
38
|
|
|
39
|
-
## 2. RC-02
|
|
39
|
+
## 2. RC-02: Distinguishing absence, conflicts, and unknown persistence outcomes
|
|
40
40
|
|
|
41
|
-
###
|
|
41
|
+
### Shared decision rules
|
|
42
42
|
|
|
43
|
-
|
|
|
43
|
+
| Observation | Permitted decision or action |
|
|
44
44
|
|---|---|
|
|
45
|
-
|
|
|
46
|
-
| exact ID
|
|
47
|
-
| exact ID
|
|
48
|
-
|
|
|
49
|
-
| write
|
|
50
|
-
|
|
|
45
|
+
| A successful authoritative read confirms that the reserved ID is absent | Check the parent's latest state, reservation, and cancellation conditions, then attempt admission with the same reserved ID |
|
|
46
|
+
| The exact ID is confirmed nonterminal | Follow the existing Recovery contract for the same execution |
|
|
47
|
+
| The exact ID is confirmed terminal | Use the persisted outcome; do not create a replacement execution |
|
|
48
|
+
| Read timeout / I/O failure / record decode failure | Preserve state and follow existing error/retry contracts; do not treat the failure as absence |
|
|
49
|
+
| Commit outcome is unknown, for example after a lost write acknowledgement | Retain the same operation identity and reserved ID for readback; do not create another ID or assignment before confirmation |
|
|
50
|
+
| A definite revision/admission conflict | Reread the latest authoritative facts and validate consistency with the reservation, owner, and input; do not treat the conflict as success by itself |
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
Readback consistency must satisfy the existing Persistence contract that protects admission/CAS.
|
|
53
|
+
Cache/index discrepancies and read failures must not justify new admission.
|
|
54
|
+
Use existing atomic admission/CAS to prevent races between confirming absence and admission.
|
|
55
|
+
Start external semantic work only after admission success is confirmed.
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
Team admission/enqueue/finalize/assignment/worker outcome/terminal
|
|
59
|
-
Team-owned enqueue/finalize
|
|
57
|
+
Scope: Source Handoff transfer, Target admission/stabilization, parent child reservation/outcome,
|
|
58
|
+
and Team admission/enqueue/finalize/assignment/worker outcome/terminal state.
|
|
59
|
+
Team-owned enqueue/finalize retain the existing policy of fact reconciliation using a stable tool_invocation_id.
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
While an unknown outcome remains unresolved, do not advance to the next semantic operation based on unconfirmed local assumptions.
|
|
62
|
+
Return control through existing Persistence error/retry paths instead of waiting synchronously without a bound.
|
|
63
|
+
Do not add manual resolution that requires Application to invent storage facts.
|
|
64
|
+
Reuse existing Agent/Persistence handling of unknown outcomes; do not create a second transaction engine dedicated to coordination.
|
|
65
65
|
|
|
66
|
-
## 3. RC-03
|
|
66
|
+
## 3. RC-03: Minimum wiring compatibility for recovery
|
|
67
67
|
|
|
68
|
-
###
|
|
68
|
+
### Required checks
|
|
69
69
|
|
|
70
|
-
|
|
|
70
|
+
| Subject | Checks required before continuation |
|
|
71
71
|
|---|---|
|
|
72
|
-
| Agent/Team
|
|
73
|
-
| Orchestrator child |
|
|
74
|
-
| Handoff | original main anchor
|
|
75
|
-
| Team worker/coordinator | Team lineage
|
|
72
|
+
| Agent/Team definition | Compare the persisted definition id/version with the current definition; require equality unless an existing explicit compatibility/migration contract permits otherwise |
|
|
73
|
+
| Orchestrator child | Match the persisted slot, the definition resolved from static subagent registration, reserved agent_id/execution_id, and owner |
|
|
74
|
+
| Handoff | Match the original main anchor, persisted active/Target IDs, required graph connections, and the same Persistence instance/domain |
|
|
75
|
+
| Team worker/coordinator | Match Team lineage, the persisted worker slot and definition, reserved Agent/execution IDs, and the persisted assignment |
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
If a required registration or Target is missing, a definition is incompatible, or the Persistence boundary differs, fail closed without starting semantic work.
|
|
78
|
+
Use existing configuration/rehydration error contracts and make the affected identity and type of missing/inconsistent information clear from the error.
|
|
79
|
+
Do not require execution wiring unnecessary for read-only result access (RC-01).
|
|
80
80
|
|
|
81
|
-
###
|
|
81
|
+
### Committed facts take precedence
|
|
82
82
|
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
83
|
+
- Do not reproject committed HandoffContext using the current HandoffPolicy.
|
|
84
|
+
- Do not replace committed child input/config with current defaults.
|
|
85
|
+
- Do not reselect committed assignments using the current scheduler.
|
|
86
|
+
- Do not recompute committed child/worker/final aggregate outcomes.
|
|
87
|
+
- Uncommitted scheduler/aggregate computations may rerun under the existing V2 replay-safe contract. Application must preserve semantic compatibility with the persisted inputs.
|
|
88
88
|
|
|
89
|
-
Phronomy
|
|
90
|
-
|
|
91
|
-
version
|
|
92
|
-
Proc/Class
|
|
89
|
+
Phronomy can check declared identity/version and structure, not semantic equivalence of Ruby code.
|
|
90
|
+
Application is responsible for not introducing incompatible implementations under an unchanged version.
|
|
91
|
+
Increasing a version alone does not automatically enable recovery of old executions; the corresponding definition or an existing explicit migration contract is required.
|
|
92
|
+
Do not add Proc/Class persistence, source hashes, policy descriptors, or a global generic class registry.
|
|
93
93
|
|
|
94
|
-
## 4. RC-04
|
|
94
|
+
## 4. RC-04: Ending observation and semantic cancellation
|
|
95
95
|
|
|
96
|
-
###
|
|
96
|
+
### Distinguish operations
|
|
97
97
|
|
|
98
|
-
|
|
|
98
|
+
| Operation or state | Contract |
|
|
99
99
|
|---|---|
|
|
100
|
-
| Application
|
|
101
|
-
| Runtime shutdown / process loss |
|
|
102
|
-
|
|
|
103
|
-
| recovery resolution / approval
|
|
100
|
+
| Application stops waiting, a connection is lost, or observation times out | This alone is not semantic cancellation of the durable execution |
|
|
101
|
+
| Runtime shutdown / process loss | This alone does not establish semantic cancellation; preserve execution and recoverability under existing shutdown/draining contracts |
|
|
102
|
+
| Explicit semantic cancellation request | Map it to the target owner/run and reserved executions under the existing Agent cancellation contract |
|
|
103
|
+
| Waiting for recovery resolution / approval | Preserve existing V2 rehydration paths; do not convert waiting into failure or automatic cancellation |
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
APIs that already request semantic cancellation, such as explicit cancellation of an existing Task, retain that contract.
|
|
106
|
+
Do not treat them as equivalent to merely ending observation. Record concrete public API mappings when inspecting the baseline.
|
|
107
107
|
|
|
108
|
-
###
|
|
108
|
+
### Applying cancellation to child executions
|
|
109
109
|
|
|
110
|
-
|
|
|
110
|
+
| Child state | Behavior after semantic cancellation of the parent run is accepted |
|
|
111
111
|
|---|---|
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
|
|
|
112
|
+
| Reserved, not started | Stop new dispatch owned by the current parent run; retain reservation information and do not start a child just to cancel it |
|
|
113
|
+
| Running, or racing with admission | Check the exact reserved ID's state and apply existing Agent cancellation; discarding a local Task alone does not complete cancellation handling |
|
|
114
|
+
| Terminal | Preserve the durable outcome; do not rewrite historical success/failure as cancellation or rerun it for compensation |
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
restart
|
|
121
|
-
|
|
116
|
+
Handle races between parent cancellation and child admission using the latest parent-run state and existing admission contracts.
|
|
117
|
+
Reconcile an already admitted child under the running-child rules; do not create a child with a different ID.
|
|
118
|
+
Map the ordering of parent terminalization and child settlement to existing Agent cancellation/terminal-barrier contracts.
|
|
119
|
+
If a child is incomplete, retain its exact ID and required cancellation/reconciliation state.
|
|
120
|
+
Require rediscovery through existing recovery paths after restart; do not claim that all parent work is complete while forgetting an unhandled child.
|
|
121
|
+
Do not add a new public terminal status or a separate cancellation scheduler solely for this addendum.
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
Handoff
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
Limit cancellation to children owned by the affected parent run; it must not affect other executions of a shared Agent or later Team runs.
|
|
124
|
+
A Handoff Source is handed_off after transfer commits. Cancelling Source later must not be treated as reversing the transfer.
|
|
125
|
+
Map explicit cancellation of the current Handoff turn to the relevant Target execution using persisted routing/reservations.
|
|
126
|
+
Do not return active responsibility to the original main Agent because observation ends or cancellation occurs.
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
Cancellation of external Providers/Tools does not guarantee reversal of side effects. Resolve uncertain outcomes through the existing Agent Recovery contract.
|
|
129
|
+
If existing mechanisms cannot satisfy the required ordering and rediscovery without a second execution engine or similar addition, report a Stop Condition.
|
|
130
130
|
|
|
131
|
-
## 5. RC-05
|
|
131
|
+
## 5. RC-05: Consistent durability guarantee wording
|
|
132
132
|
|
|
133
|
-
>
|
|
134
|
-
>
|
|
133
|
+
> Reuse committed durable outcomes. Recover incomplete executions with the same semantic identity.
|
|
134
|
+
> Resolve uncertain outcomes of external Provider/Tool operations under the existing Agent Recovery contract.
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
callback
|
|
136
|
+
Starting only when execution is confirmed not to have started refers to framework-owned execution admission.
|
|
137
|
+
It does not mean that no request ever reached an external system, nor does it guarantee exactly-once external side effects.
|
|
138
|
+
Recovering an incomplete execution with the same ID also does not mean unconditionally resending every internal Provider/Tool call.
|
|
139
|
+
Loss of a callback or local Task, or an observation timeout, must not justify rerunning semantic work.
|
|
140
140
|
|
|
141
|
-
## 6.
|
|
141
|
+
## 6. Validation conditions and implementation mapping
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
The following are behavioral validation conditions, not a required count of new spec files.
|
|
144
|
+
Where existing specs verify a contract, record that evidence and add only missing cases.
|
|
145
145
|
|
|
146
|
-
| ID |
|
|
146
|
+
| ID | Validation scenario | Required outcome |
|
|
147
147
|
|---|---|---|
|
|
148
|
-
| RC-01-A | terminal commit
|
|
149
|
-
| RC-01-B | admission
|
|
150
|
-
| RC-01-C | graph/listener
|
|
151
|
-
| RC-02-A |
|
|
152
|
-
| RC-02-B | read
|
|
153
|
-
| RC-02-C |
|
|
154
|
-
| RC-03-A | subagent
|
|
155
|
-
| RC-03-B |
|
|
156
|
-
| RC-04-A |
|
|
157
|
-
| RC-04-B |
|
|
158
|
-
| RC-04-C |
|
|
159
|
-
| RC-04-D |
|
|
160
|
-
| RC-04-E | Handoff transfer
|
|
161
|
-
| RC-05-A |
|
|
162
|
-
|
|
163
|
-
baseline
|
|
164
|
-
|
|
165
|
-
focused
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
## 7.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
Proc/Class serialization
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
## 8.
|
|
176
|
-
|
|
177
|
-
2026-09-07
|
|
178
|
-
|
|
179
|
-
本追補の責務境界と必須契約は変更しない。
|
|
148
|
+
| RC-01-A | Process loss after terminal commit and before callback | Read the same result by exact ID without rerunning semantic work or callbacks |
|
|
149
|
+
| RC-01-B | Caller is lost before the admission acknowledgement; execution later becomes terminal | Discover candidates from owner identity; do not arbitrarily choose one when multiple candidates exist |
|
|
150
|
+
| RC-01-C | Access results without a graph/listener, or inject result read/decode failure | Require no unnecessary wiring; do not disguise an access failure as success or absence |
|
|
151
|
+
| RC-02-A | Lose the acknowledgement immediately after each successful semantic commit | Adopt the existing fact through readback; create neither another ID nor duplicate assignments |
|
|
152
|
+
| RC-02-B | Independently inject read failure, authoritative absence, and CAS conflict | Keep the states distinct; execute only after admission success is confirmed |
|
|
153
|
+
| RC-02-C | Readback also fails after an unknown outcome | Preserve state and return control; do not wait synchronously without a bound or create a new execution |
|
|
154
|
+
| RC-03-A | Remove subagent registration, mismatch definition version, or omit Target | Fail closed before continuation; do not substitute another definition or Target |
|
|
155
|
+
| RC-03-B | Recover with compatible current wiring | Do not recompute committed Context/input/assignment/outcome |
|
|
156
|
+
| RC-04-A | End waiting, shut down Runtime, or lose the process | Do not establish implicit semantic cancellation |
|
|
157
|
+
| RC-04-B | Parent cancellation with an unstarted/active/terminal child | Follow state-specific rules and preserve exact IDs and committed outcomes |
|
|
158
|
+
| RC-04-C | Race parent cancellation against child admission/terminal state | Do not lose the child; remain consistent with existing cancellation/terminal barriers |
|
|
159
|
+
| RC-04-D | Lose the process at each commit boundary after a cancellation request | Rediscover and reconcile incomplete children of the same run through recovery paths |
|
|
160
|
+
| RC-04-E | End observation or explicitly cancel after Handoff transfer | Do not roll responsibility back to Source; apply cancellation only to the relevant Target execution |
|
|
161
|
+
| RC-05-A | Independently inject an uncertain external Tool effect and a lost callback | Use existing Recovery for the former and committed-result access for the latter; never rerun blindly |
|
|
162
|
+
|
|
163
|
+
When integrating with the baseline, record the public API/SPI, implementation locations, and existing/additional specs for each RC ID in a mapping table.
|
|
164
|
+
In particular, do not mark result-discovery APIs, compatible-version handling, or cancellation/terminal barriers complete using unverified method names.
|
|
165
|
+
Successful focused, backend, fault, and full-suite runs require actual measurement on the target checkout.
|
|
166
|
+
They have not been run at the time of this document revision.
|
|
167
|
+
|
|
168
|
+
## 7. Responsibilities that remain out of scope
|
|
169
|
+
|
|
170
|
+
Callback outbox/ACK, a terminal-delivery index, synthetic executions for standalone fan-out,
|
|
171
|
+
Proc/Class serialization, a global generic registry, distributed transactions across independent Persistence instances,
|
|
172
|
+
and a second execution engine dedicated to Team remain out of scope.
|
|
173
|
+
Public result access does not replace Application notification delivery or deduplication of external effects.
|
|
174
|
+
|
|
175
|
+
## 8. Implementation mapping
|
|
176
|
+
|
|
177
|
+
See [IMPLEMENTATION_REPORT.md](IMPLEMENTATION_REPORT.md) for the implementation, public API/SPI, and validation scope as of 2026-09-07.
|
|
178
|
+
The responsibility boundaries and required contracts in this addendum remain unchanged.
|
data/docs/getting-started.md
CHANGED
|
@@ -137,6 +137,26 @@ mutable Agent/Execution/Journal state before every LLM or Tool step. A conflicti
|
|
|
137
137
|
external durable write is surfaced as `Phronomy::Persistence::StateConflictError` rather than
|
|
138
138
|
silently merged into the live instance.
|
|
139
139
|
|
|
140
|
+
Read persisted conversation values without interpreting Journal storage:
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
messages = agent.transcript_messages
|
|
144
|
+
messages.each { |message| puts "#{message.role}: #{message.content}" }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`transcript_messages_async` returns a TaskResult with the same frozen array of
|
|
148
|
+
`LLMAdapter::Message` values. For the same durable root, these match
|
|
149
|
+
`result[:messages]`, including Tool calls, Tool results, non-text content and
|
|
150
|
+
opaque metadata. The operation selects root/generation and Journal records in one
|
|
151
|
+
transaction, then restores immutable content off EventLoop. It holds Agent
|
|
152
|
+
ownership until physical completion, including after logical cancellation.
|
|
153
|
+
The synchronous method refuses to wait on EventLoop. Content and integrity
|
|
154
|
+
failures propagate; an unreadable transcript is not an empty conversation.
|
|
155
|
+
|
|
156
|
+
`agent.transcript` remains a JournalRecord view. Knowledge is excluded from the
|
|
157
|
+
conversation. The application still owns authorization, role filtering and
|
|
158
|
+
presentation; reading invokes no LLM or Tool and changes no durable state.
|
|
159
|
+
|
|
140
160
|
The active transcript and Knowledge views can be advanced independently without
|
|
141
161
|
deleting the append-only canonical Journal:
|
|
142
162
|
|
|
@@ -379,6 +399,12 @@ Agent-backed Tools return control to EventLoop while the child lifecycle is
|
|
|
379
399
|
waiting. They do not occupy an OffloadPool worker merely to wait for a child
|
|
380
400
|
Agent result.
|
|
381
401
|
|
|
402
|
+
Each call owns and releases its fresh child Agent after physical work finishes.
|
|
403
|
+
Normal completion includes that release; a failed release preserves the result,
|
|
404
|
+
original error and child in `Tools::Agent::CleanupError`. See
|
|
405
|
+
[child lifetime and cleanup](architecture/agent-tool-child-lifetime.md) for
|
|
406
|
+
cancellation, approval and release-only retry rules.
|
|
407
|
+
|
|
382
408
|
## Filters
|
|
383
409
|
|
|
384
410
|
Filters can transform or reject values at Agent boundaries:
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Migrating to Phronomy 0.31
|
|
2
|
+
|
|
3
|
+
Phronomy 0.31 adds explicit schema delivery and durable transcript reads, and
|
|
4
|
+
fixes ownership and release of one-shot and Agent Tool children. Review these
|
|
5
|
+
compatibility boundaries when upgrading from 0.30.1.
|
|
6
|
+
|
|
7
|
+
## Stored executions
|
|
8
|
+
|
|
9
|
+
New structured executions store output-contract version 2 with `schema_delivery`
|
|
10
|
+
and use AgentExecution envelope 0.3. The schema remains saved and locally
|
|
11
|
+
validated even with `schema_delivery: :none`; omitting native schema delivery is
|
|
12
|
+
an explicit application choice, not an automatic fallback or output repair.
|
|
13
|
+
|
|
14
|
+
Deploy compatible readers before writing these records. Older readers reject
|
|
15
|
+
0.3, including completed executions; do not downgrade a store containing them.
|
|
16
|
+
Version 0.31 still reads envelope 0.2 and existing version-1 native output
|
|
17
|
+
contracts without rewriting them merely on read. Existing free-text records do
|
|
18
|
+
not acquire a schema retroactively. See the
|
|
19
|
+
[structured-output persistence contract](../architecture/structured-output-and-scope.md#failure-and-persistence).
|
|
20
|
+
|
|
21
|
+
## One-shot Agent ownership
|
|
22
|
+
|
|
23
|
+
`Agent.run_once` owns its fresh Agent and ephemeral Store through
|
|
24
|
+
`Agent.open_scope` and releases the live Agent before returning. A reusable
|
|
25
|
+
Agent must instead use `create`/`load` or an explicit ownership scope.
|
|
26
|
+
|
|
27
|
+
When release succeeds, the original invocation value or exception is preserved.
|
|
28
|
+
Unsafe or failed release raises `Agent::ScopeCleanupError`, retaining the result,
|
|
29
|
+
primary error, failed owners and scope. Resolve the release condition before
|
|
30
|
+
retrying `error.scope.release!`; this does not repeat model or Tool work.
|
|
31
|
+
Approval waits, physical work, independent leases, retention and recovery still
|
|
32
|
+
prevent unsafe release. No records are purged and no shared Store or Runtime is
|
|
33
|
+
shut down. See [single-invocation ownership](../architecture/structured-output-and-scope.md#single-invocation-ownership).
|
|
34
|
+
|
|
35
|
+
## Agent-backed Tools
|
|
36
|
+
|
|
37
|
+
Both synchronous `execute` and asynchronous `call_async` now invoke the child
|
|
38
|
+
through `invoke_async`. A subclass that overrides only `invoke` no longer
|
|
39
|
+
changes the synchronous Tool path. Move the intended customization to the
|
|
40
|
+
asynchronous entry point and preserve its TaskResult contract.
|
|
41
|
+
|
|
42
|
+
Each call owns its fresh child and ephemeral Store until invocation and physical
|
|
43
|
+
work have finished and safe release completes. Normal return includes release;
|
|
44
|
+
successful output conversion remains unchanged. Waiting does not consume an
|
|
45
|
+
OffloadPool worker solely to join the child.
|
|
46
|
+
|
|
47
|
+
A release failure raises `Phronomy::Tools::Agent::CleanupError` with code
|
|
48
|
+
`tool.agent_cleanup_failed`. It retains `child_agent`, `agent_id`, `result`,
|
|
49
|
+
`primary_error` and `cleanup_error`; the cause is the primary failure when one
|
|
50
|
+
exists, otherwise the release failure. Resolve the release condition, then retry
|
|
51
|
+
`error.child_agent.unload_async` for release only. These live handles are
|
|
52
|
+
process-local and are not reconstructed from saved error diagnostics.
|
|
53
|
+
|
|
54
|
+
Cleanup failures are also diagnosed after observer cancellation and are not
|
|
55
|
+
hidden by ordinary Tool error suppression. Completed runtime wiring no longer
|
|
56
|
+
retains released children through generated callbacks, temporary tracker context
|
|
57
|
+
or completed participation changes. Session ownership, commit/readback ordering,
|
|
58
|
+
physical leases and recovery guards remain unchanged. See the
|
|
59
|
+
[Agent Tool child lifetime contract](../architecture/agent-tool-child-lifetime.md).
|
|
60
|
+
|
|
61
|
+
## Live-provider limits
|
|
62
|
+
|
|
63
|
+
LM Studio/gpt-oss PEG output parsing remains unresolved. Known-error
|
|
64
|
+
identification and one bounded retry do not establish the root cause or prevent
|
|
65
|
+
recurrence. The existing evidence distinguishes a failed call stopped after two
|
|
66
|
+
POSTs from a separate application invocation that succeeded on its first POST;
|
|
67
|
+
it does not show recovery by retry within one call.
|
|
68
|
+
|
|
69
|
+
The DX1/DX2 normal live path, streaming, Tool use and history restoration remain
|
|
70
|
+
unverified in that environment. Source review, offline tests and CI support this
|
|
71
|
+
release; they do not establish live LLM acceptance. See the
|
|
72
|
+
[LM Studio policy and evidence limits](../architecture/lm-studio-output-failures.md).
|
|
@@ -12,12 +12,12 @@ module Phronomy
|
|
|
12
12
|
# - approval notifications share the Agent listener as :approval_required;
|
|
13
13
|
# - Recovery resolution uses resolve/resolve_async.
|
|
14
14
|
module AsyncEventApi
|
|
15
|
-
def start_reserved_async(reservation:, input:, config: {}, output_schema: nil)
|
|
15
|
+
def start_reserved_async(reservation:, input:, config: {}, output_schema: nil, schema_delivery: nil)
|
|
16
16
|
__with_operation(async: true) do
|
|
17
17
|
unless reservation.is_a?(ReservedExecution) && reservation.agent_id == agent_id
|
|
18
18
|
raise ArgumentError, "reservation must belong to this Agent"
|
|
19
19
|
end
|
|
20
|
-
config = __invocation_config(_prepare_output_config(config, nil, output_schema))
|
|
20
|
+
config = __invocation_config(_prepare_output_config(config, nil, output_schema, schema_delivery))
|
|
21
21
|
ExactExecution.start(agent: self, execution_id: reservation.execution_id, input: input,
|
|
22
22
|
config: config.merge(phronomy_reservation: reservation.correlation))
|
|
23
23
|
end
|
|
@@ -54,12 +54,13 @@ module Phronomy
|
|
|
54
54
|
config: {},
|
|
55
55
|
invocation_context: nil,
|
|
56
56
|
output_schema: nil,
|
|
57
|
+
schema_delivery: nil,
|
|
57
58
|
**removed_options,
|
|
58
59
|
&removed_block
|
|
59
60
|
)
|
|
60
61
|
__with_operation do
|
|
61
62
|
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
62
|
-
config = _prepare_output_config(config, invocation_context, output_schema)
|
|
63
|
+
config = _prepare_output_config(config, invocation_context, output_schema, schema_delivery)
|
|
63
64
|
_check_event_loop_reentrancy(:invoke, :invoke_async)
|
|
64
65
|
_start_agent_operation(
|
|
65
66
|
input,
|
|
@@ -75,12 +76,13 @@ module Phronomy
|
|
|
75
76
|
config: {},
|
|
76
77
|
invocation_context: nil,
|
|
77
78
|
output_schema: nil,
|
|
79
|
+
schema_delivery: nil,
|
|
78
80
|
**removed_options,
|
|
79
81
|
&removed_block
|
|
80
82
|
)
|
|
81
83
|
__with_operation(async: true) do
|
|
82
84
|
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
83
|
-
config = _prepare_output_config(config, invocation_context, output_schema)
|
|
85
|
+
config = _prepare_output_config(config, invocation_context, output_schema, schema_delivery)
|
|
84
86
|
_start_agent_operation(
|
|
85
87
|
input,
|
|
86
88
|
config: config,
|
|
@@ -95,13 +97,14 @@ module Phronomy
|
|
|
95
97
|
config: {},
|
|
96
98
|
invocation_context: nil,
|
|
97
99
|
output_schema: nil,
|
|
100
|
+
schema_delivery: nil,
|
|
98
101
|
**removed_options,
|
|
99
102
|
&removed_block
|
|
100
103
|
)
|
|
101
104
|
__with_operation do
|
|
102
105
|
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
103
106
|
listener = _required_stream_listener!(:stream)
|
|
104
|
-
config = _prepare_output_config(config, invocation_context, output_schema)
|
|
107
|
+
config = _prepare_output_config(config, invocation_context, output_schema, schema_delivery)
|
|
105
108
|
_check_event_loop_reentrancy(:stream, :stream_async)
|
|
106
109
|
_start_agent_operation(
|
|
107
110
|
input,
|
|
@@ -117,13 +120,14 @@ module Phronomy
|
|
|
117
120
|
config: {},
|
|
118
121
|
invocation_context: nil,
|
|
119
122
|
output_schema: nil,
|
|
123
|
+
schema_delivery: nil,
|
|
120
124
|
**removed_options,
|
|
121
125
|
&removed_block
|
|
122
126
|
)
|
|
123
127
|
__with_operation(async: true) do
|
|
124
128
|
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
125
129
|
listener = _required_stream_listener!(:stream_async)
|
|
126
|
-
config = _prepare_output_config(config, invocation_context, output_schema)
|
|
130
|
+
config = _prepare_output_config(config, invocation_context, output_schema, schema_delivery)
|
|
127
131
|
_start_agent_operation(
|
|
128
132
|
input,
|
|
129
133
|
config: config,
|
|
@@ -206,9 +210,14 @@ module Phronomy
|
|
|
206
210
|
|
|
207
211
|
private
|
|
208
212
|
|
|
209
|
-
def _prepare_output_config(config, invocation_context, schema)
|
|
213
|
+
def _prepare_output_config(config, invocation_context, schema, schema_delivery)
|
|
210
214
|
config = _prepare_invocation_config(config, invocation_context)
|
|
211
|
-
schema.nil?
|
|
215
|
+
if schema.nil?
|
|
216
|
+
raise ArgumentError, "schema_delivery requires output_schema" unless schema_delivery.nil?
|
|
217
|
+
return config
|
|
218
|
+
end
|
|
219
|
+
contract = OutputContract.new(schema, schema_delivery: schema_delivery.nil? ? :native : schema_delivery)
|
|
220
|
+
config.merge(phronomy_output_contract: contract)
|
|
212
221
|
end
|
|
213
222
|
|
|
214
223
|
def _start_agent_operation(input, config:, mode:, listener:)
|
data/lib/phronomy/agent/base.rb
CHANGED
|
@@ -400,6 +400,31 @@ module Phronomy
|
|
|
400
400
|
end
|
|
401
401
|
end
|
|
402
402
|
|
|
403
|
+
# Materializes the current durable transcript as immutable Message values.
|
|
404
|
+
# Application authorization and presentation filtering remain caller-owned.
|
|
405
|
+
# @return [Array<LLMAdapter::Message>]
|
|
406
|
+
# @api public
|
|
407
|
+
def transcript_messages
|
|
408
|
+
_check_event_loop_reentrancy(:transcript_messages, :transcript_messages_async)
|
|
409
|
+
transcript_messages_async.wait_result
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Holds Agent ownership until the submitted read physically completes.
|
|
413
|
+
# @return [TaskResult<Array<LLMAdapter::Message>>]
|
|
414
|
+
# @api public
|
|
415
|
+
def transcript_messages_async
|
|
416
|
+
__with_operation(async: true, read_only: true) do
|
|
417
|
+
__execution_environment.submit(on_full: :raise) do
|
|
418
|
+
root, records = persistence.transaction do |tx|
|
|
419
|
+
current = tx.agents.load(agent_id)
|
|
420
|
+
[current, tx.journals.read(agent_id, limit: current.journal_position)]
|
|
421
|
+
end
|
|
422
|
+
RuntimeInput.new(agent: self, persistence: persistence)
|
|
423
|
+
.materialize_transcript(root: root, records: records)
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
403
428
|
def clear_transcript!
|
|
404
429
|
__with_operation do
|
|
405
430
|
mutate_context!(:transcript_cleared) do |root|
|
|
@@ -730,8 +755,8 @@ module Phronomy
|
|
|
730
755
|
def _prepare_invocation_config(config, invocation_context)
|
|
731
756
|
__assert_live_agent!
|
|
732
757
|
_reject_removed_generic_identity_keys!(config)
|
|
733
|
-
if config.keys.any? { |key| %w[output_schema response_schema phronomy_output_contract].include?(key.to_s) }
|
|
734
|
-
raise ArgumentError, "Use the output_schema
|
|
758
|
+
if config.keys.any? { |key| %w[output_schema schema_delivery response_schema phronomy_output_contract].include?(key.to_s) }
|
|
759
|
+
raise ArgumentError, "Use the output_schema and schema_delivery keywords on a new Agent execution"
|
|
735
760
|
end
|
|
736
761
|
invocation_context ?
|
|
737
762
|
config.merge(invocation_context: invocation_context) : config
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# This convenience API
|
|
3
|
+
# This convenience API owns a fresh ephemeral Persistence and a scoped Agent.
|
|
4
4
|
# The application entry loads the method definition; Agent execution does not
|
|
5
5
|
# delegate to or require this higher-level composition implementation.
|
|
6
6
|
module Phronomy
|
|
@@ -18,15 +18,16 @@ module Phronomy
|
|
|
18
18
|
raise ArgumentError, "Provide either on_event: or a block, not both"
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
open_scope do |scope|
|
|
22
|
+
persistence = Phronomy::PersistenceComposition.agent
|
|
23
|
+
agent = scope.create(definition,
|
|
24
|
+
context: context,
|
|
25
|
+
knowledge: knowledge,
|
|
26
|
+
persistence: persistence,
|
|
27
|
+
on_event: on_event,
|
|
28
|
+
&event_block)
|
|
29
|
+
agent.invoke(input, **invoke_options)
|
|
30
|
+
end
|
|
30
31
|
end
|
|
31
32
|
end
|
|
32
33
|
end
|