phronomy 0.32.0 → 0.34.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 +139 -0
- data/CONTRIBUTING.md +5 -5
- data/README.md +20 -1
- data/RELEASE_CHECKLIST.md +7 -1
- data/SECURITY.md +1 -1
- data/VERIFY.sh +1 -9
- data/docs/architecture/error-api-review.md +6 -2
- data/docs/architecture/structured-output-and-scope.md +8 -8
- data/docs/architecture/trust-acceptance-followup.md +15 -0
- data/docs/context-policy-budgeting.md +108 -0
- data/docs/decisions/016-semantic-multi-agent-handoff.md +6 -5
- data/docs/examples/shared_state_custom_sharing.rb +145 -0
- data/docs/features.md +4 -2
- data/docs/mcp-client.md +4 -2
- data/docs/migrations/cleanup-audit.md +89 -0
- data/docs/migrations/legacy-format-removal.md +55 -0
- data/docs/migrations/r8-unit6.md +4 -2
- data/docs/migrations/shared-state-multi-agent.md +5 -0
- data/docs/migrations/vector-async-clients.md +6 -1
- data/docs/runtime-and-concurrency.md +36 -0
- data/docs/shared-state-sharing.md +233 -0
- data/lib/phronomy/agent/base.rb +53 -0
- data/lib/phronomy/agent/execution/agent_invocation.rb +0 -4
- data/lib/phronomy/agent/execution/execution_coordinator.rb +11 -0
- data/lib/phronomy/agent/handoff/transfer_projection.rb +0 -9
- data/lib/phronomy/agent/journal/journal_record.rb +2 -2
- data/lib/phronomy/agent/lifecycle/hosted_invocation.rb +2 -5
- data/lib/phronomy/agent/recovery/recovery_rules.rb +0 -12
- data/lib/phronomy/agent/store.rb +3 -8
- data/lib/phronomy/agent/tool_execution/tool_approval_request.rb +3 -2
- data/lib/phronomy/agent/tool_execution/tool_invocation.rb +9 -36
- data/lib/phronomy/context/context_policy.rb +13 -8
- data/lib/phronomy/context/context_policy_input.rb +26 -0
- data/lib/phronomy/context/llm_input_manifest.rb +3 -3
- data/lib/phronomy/engine/runtime/shutdown_result.rb +0 -5
- data/lib/phronomy/execution/execution.rb +0 -11
- data/lib/phronomy/multi_agent/shared_state/binding.rb +44 -0
- data/lib/phronomy/multi_agent/shared_state/member_lifecycle_error.rb +32 -0
- data/lib/phronomy/multi_agent/shared_state.rb +152 -10
- data/lib/phronomy/tool/base.rb +69 -3
- data/lib/phronomy/tool/schema.rb +23 -11
- data/lib/phronomy/version.rb +1 -1
- data/scripts/api_snapshot.rb +6 -3
- data/sig/phronomy/agent.rbs +1 -0
- data/sig/phronomy/context.rbs +3 -0
- data/sig/phronomy/multi_agent.rbs +26 -2
- data/sig/phronomy/tool.rbs +1 -0
- metadata +9 -11
- data/lib/phronomy/execution/scheduler_reentrancy_error.rb +0 -9
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +0 -226
- data/scripts/add_to_h_to_token_doubles.rb +0 -33
- data/scripts/add_to_h_unnamed_doubles.rb +0 -27
- data/scripts/migrate_generator_verifier_result.rb +0 -18
- data/scripts/migrate_spec_agent_definition.rb +0 -108
- data/scripts/migrate_spec_agent_definition_pass2.rb +0 -53
- data/scripts/migrate_spec_inline_pass3.rb +0 -24
- data/sig/phronomy/persistence/migration.rbs +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4c67e91b9abb65c5ab2ddc5b657afa6843b916a5ba84aefcd8f808ae297c3236
|
|
4
|
+
data.tar.gz: b5894936fa47d493da81c650edbf3c330eaeec7101c68ec91acf96bec00d1a79
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ebec13bc657697922f3f487eefe809a35570b36a574e8a3ea95d5124459035ca263ad39b89419e8042621e4971f550cbb7d21ab206d2961f8a7f01bec6b9514e
|
|
7
|
+
data.tar.gz: b1d930edb100e7782522a9d666d555756fcc50e5be46881c58c035f2e021631093132b4bde4cec8134f296f068ddb9cb0fd17548ea356afad32652d484b7da91
|
data/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,145 @@ Release history for 0.14.0 and earlier is archived in
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased]
|
|
14
14
|
|
|
15
|
+
## [0.34.0] - 2026-11-16
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Route ContextPolicy item estimates directly through the protected
|
|
20
|
+
`estimate_tokens` extension operation, removing the redundant private wrapper.
|
|
21
|
+
- Exercise Tool approval redaction through the display/snapshot path used by
|
|
22
|
+
production approval requests instead of internal convenience getters.
|
|
23
|
+
- Replace references to an absent repository language-check script with an
|
|
24
|
+
external maintainer check and recorded evidence. Correct the Storage phase
|
|
25
|
+
status: P4 is implemented; the separate final P5 review remains pending.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- Derive default SharedState members through `Agent::Base.with_tools`, retaining
|
|
30
|
+
Phronomy authoring settings and filters as custom sharing already does. A
|
|
31
|
+
member's iteration limit, temperature and other configuration no longer reset
|
|
32
|
+
when built-in sharing Tools are added. Preserve Tool aliases, the generated
|
|
33
|
+
definition identity and default findings-store behavior.
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
|
|
37
|
+
- Remove Handoff's old selection-unit metadata fallback and Agent Store's raw
|
|
38
|
+
argument fallback. Current conversation groups and exact validated arguments
|
|
39
|
+
remain the authoritative saved facts; current record writers are unchanged.
|
|
40
|
+
- Retire `Persistence::Migration::InitialFormatMigration` and the old
|
|
41
|
+
GeneratorVerifier Result JSON conversion command, including their dedicated
|
|
42
|
+
migration tests and active usage instructions. No replacement converter or
|
|
43
|
+
compatibility entry point is supplied. Current codecs, Result validation and
|
|
44
|
+
the separate Unit4 snapshot migration remain. See the
|
|
45
|
+
[legacy-format cleanup](docs/migrations/legacy-format-removal.md).
|
|
46
|
+
- Remove `Runtime::ShutdownResult#success?`; use the equivalent
|
|
47
|
+
`cleanup_complete?`. Keep `clean?`, which additionally requires a graceful
|
|
48
|
+
shutdown.
|
|
49
|
+
- Remove the obsolete `SchedulerReentrancyError` subclass; use
|
|
50
|
+
`EventLoopReentrancyError`, which is already raised by current execution paths.
|
|
51
|
+
No compatibility aliases are retained. See the
|
|
52
|
+
[cleanup migration](docs/migrations/cleanup-audit.md).
|
|
53
|
+
- Remove unused internal Invocation, RecoveryRules and Execution helpers, along
|
|
54
|
+
with five completed development-only spec migration scripts. Keep the public
|
|
55
|
+
`JournalProjection#llm_call_records` and
|
|
56
|
+
`LLMInputManifest#referenced_content_refs` operations and the Scope hooks still
|
|
57
|
+
used for result composition.
|
|
58
|
+
|
|
59
|
+
### Compatibility and verification
|
|
60
|
+
|
|
61
|
+
- Replace `Runtime::ShutdownResult#success?` with `cleanup_complete?` and rescue
|
|
62
|
+
`EventLoopReentrancyError` instead of the removed `SchedulerReentrancyError`.
|
|
63
|
+
No compatibility aliases are retained. See the
|
|
64
|
+
[cleanup migration](docs/migrations/cleanup-audit.md).
|
|
65
|
+
- Historical pre-S3 and GeneratorVerifier Result conversion support is retired
|
|
66
|
+
without a replacement command. Current record writers and saved representation
|
|
67
|
+
are unchanged; this release does not rewrite existing stores. Handoff no longer
|
|
68
|
+
derives groups from old selection-unit metadata, and authorization requires
|
|
69
|
+
validated argument evidence. Review the
|
|
70
|
+
[legacy-format migration](docs/migrations/legacy-format-removal.md) before
|
|
71
|
+
upgrading applications that still consume those historical records.
|
|
72
|
+
- Previously observed LM Studio/gpt-oss PEG-format failures are not resolved by
|
|
73
|
+
this cleanup. This release does not claim to fix their root cause or prevent
|
|
74
|
+
recurrence; offline persistence/recovery checks do not establish full real-LLM
|
|
75
|
+
acceptance.
|
|
76
|
+
|
|
77
|
+
## [0.33.0] - 2026-11-14
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- Add public `ContextPolicyInput#input_token_limit` and
|
|
82
|
+
`#with_input_token_limit(limit)` for immutable, non-expanding selection limits,
|
|
83
|
+
plus protected `ContextPolicy#estimate_tokens(content)` using the same
|
|
84
|
+
canonical estimator as generated Policy items. Applications can reserve input
|
|
85
|
+
capacity while retaining DefaultPolicy selection without constructing private
|
|
86
|
+
budget objects or calling private token-estimation helpers. These operations
|
|
87
|
+
do not change model capabilities, generation settings or final validation.
|
|
88
|
+
- Add `Agent::Base.with_tools(definitions, id:, version:)` to derive an Agent
|
|
89
|
+
class with a complete replacement Tool map and explicit definition identity.
|
|
90
|
+
The factory preserves Phronomy authoring settings and independently copies
|
|
91
|
+
filter lists; configured callable instances remain shared. Custom SharedState
|
|
92
|
+
uses this public operation after merging member and application Tools. General
|
|
93
|
+
Agent subclass inheritance remains unchanged.
|
|
94
|
+
- Add `MultiAgent::SharedState.sharing(provider, id:, version:)` and the public
|
|
95
|
+
`SharedState::Binding` value. A synchronous provider opens one application-owned
|
|
96
|
+
store for an invocation and supplies Tool definitions for each member/cycle.
|
|
97
|
+
Applications define their own records, search arguments and cleanup without
|
|
98
|
+
overriding SharedState private methods. Custom sharing requires an explicit
|
|
99
|
+
aggregation block and does not inject the default findings Tools or append
|
|
100
|
+
stored records to member prompts.
|
|
101
|
+
- Include the sharing provider's stable ID and semantic version in custom
|
|
102
|
+
generated Agent definition identity. Default SharedState retains its existing
|
|
103
|
+
built-in Tools, prompts and instrumentation revision 1. The extension adds no
|
|
104
|
+
database adapter, ContextPolicy implementation or durable sharing lifecycle.
|
|
105
|
+
- Release terminal custom-sharing members through the public Agent lifecycle
|
|
106
|
+
operation. Normal Agent approval waits keep the invocation and provider open.
|
|
107
|
+
`SharedState::MemberLifecycleError` reports failed release or a custom
|
|
108
|
+
`invoke` override returning a suspended result prematurely, with the Agent and
|
|
109
|
+
available result/error details. It does not automatically answer approvals or
|
|
110
|
+
release a pending execution; application providers retain responsibility for
|
|
111
|
+
resource cleanup and recovery policy.
|
|
112
|
+
|
|
113
|
+
### Changed
|
|
114
|
+
|
|
115
|
+
- Reuse the immutable argument schema prepared by an explicit `parameters`
|
|
116
|
+
declaration. Each Tool instance compiles its own validator on first use.
|
|
117
|
+
|
|
118
|
+
### Fixed
|
|
119
|
+
|
|
120
|
+
- Build each human-facing Tool approval item from the Tool's public
|
|
121
|
+
`approval_display(arguments:, facts:)` result. A Tool without this operation
|
|
122
|
+
contributes empty argument and fact maps to new approval requests. Tool
|
|
123
|
+
execution and policy evaluation still receive their original inputs.
|
|
124
|
+
- Apply the default approval display's sensitive-value check to values inside
|
|
125
|
+
a Tool's redacted hash or array arguments, including copies used as fact keys.
|
|
126
|
+
|
|
127
|
+
### Security
|
|
128
|
+
|
|
129
|
+
- Use patched ActiveRecord 8.0 development dependencies, including ActiveSupport
|
|
130
|
+
8.0.5.2 or later in the 8.0 series. Remove unused ActiveJob and SQLite3
|
|
131
|
+
development dependencies; the examples retain the dependencies they use.
|
|
132
|
+
The published gem's runtime dependencies are unchanged.
|
|
133
|
+
- Provide Bundler Audit in the development bundle and check the complete
|
|
134
|
+
dependency set in pull-request CI and immediately before release publication.
|
|
135
|
+
|
|
136
|
+
### Compatibility and verification
|
|
137
|
+
|
|
138
|
+
- The new SharedState sharing, Agent Tool composition and Context budgeting APIs
|
|
139
|
+
require 0.33.0 or later and remain Experimental. Default sharing and ordinary
|
|
140
|
+
Agent subclass inheritance retain their existing behavior. Custom sharing is
|
|
141
|
+
synchronous and invocation-scoped; it does not add durable sharing recovery.
|
|
142
|
+
- Approval display is selected by each Tool. Custom Tool implementations without
|
|
143
|
+
`approval_display` expose empty display maps; execution and policy evaluation
|
|
144
|
+
still use the original arguments. Review custom display overrides for safe
|
|
145
|
+
human-facing fields when upgrading.
|
|
146
|
+
- Observed LM Studio/gpt-oss PEG-format failures remain unresolved.
|
|
147
|
+
The existing adapter identifies known errors and bounds retries; this release
|
|
148
|
+
does not claim to fix their root cause or establish full real-LLM acceptance.
|
|
149
|
+
The latest full examples verification recorded 102 passed, 1 failed and no
|
|
150
|
+
skips (Example 22: HTTP 400). A separate Qwen Example 22 run completed all three
|
|
151
|
+
cycles, but took 2,220 seconds and does not replace the full verifier result
|
|
152
|
+
or satisfy its 240-second per-example limit.
|
|
153
|
+
|
|
15
154
|
## [0.32.0] - 2026-11-03
|
|
16
155
|
|
|
17
156
|
### Added
|
data/CONTRIBUTING.md
CHANGED
|
@@ -25,11 +25,11 @@ Run the linter:
|
|
|
25
25
|
bundle exec standardrb
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Check that
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
Check that source, comments, documentation and release notes are in English.
|
|
29
|
+
Use the maintainer's external language checker and record its command, checked
|
|
30
|
+
files and result with the review evidence. Check complete final files, including
|
|
31
|
+
new files; a diff-only check can miss retained non-English content. This
|
|
32
|
+
repository does not bundle a language-check script.
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
data/README.md
CHANGED
|
@@ -37,13 +37,31 @@ tracking `main` directly.
|
|
|
37
37
|
|
|
38
38
|
See [Features and Application Programming Interface (API) stability](docs/features.md) for the full feature matrix.
|
|
39
39
|
|
|
40
|
-
The following table describes the application APIs available in v0.
|
|
40
|
+
The following table describes the application APIs available in v0.34.0.
|
|
41
|
+
The sharing, Tool composition and Context budgeting operations remain
|
|
42
|
+
Experimental; their introduction version is shown below.
|
|
41
43
|
Worker hosting and completed parent/child release require v0.32.0 or later.
|
|
42
44
|
Review the [0.31 migration notes](docs/migrations/0.31.md) before upgrading
|
|
43
45
|
persistent applications or custom Agent Tool subclasses.
|
|
44
46
|
|
|
47
|
+
The v0.34.0 cleanup removes obsolete entry points. Use
|
|
48
|
+
`EventLoopReentrancyError` for EventLoop reentrancy failures and
|
|
49
|
+
`Runtime#shutdown`'s `cleanup_complete?` result predicate for resource release;
|
|
50
|
+
`clean?` additionally requires a graceful shutdown. See the
|
|
51
|
+
[cleanup migration](docs/migrations/cleanup-audit.md) before upgrading to v0.34.0.
|
|
52
|
+
The [legacy-format cleanup](docs/migrations/legacy-format-removal.md) separately
|
|
53
|
+
retires old data-conversion entry points and read fallbacks without changing
|
|
54
|
+
the current saved representation.
|
|
55
|
+
|
|
45
56
|
| API | Stability | Contract |
|
|
46
57
|
|---|---|---|
|
|
58
|
+
| `Persistence::Migration::InitialFormatMigration` | Removed (v0.34.0) | The pre-S3 conversion API is retired without a replacement. Current domain codecs and the separate Unit4 snapshot migration remain. [Legacy-format cleanup](docs/migrations/legacy-format-removal.md) |
|
|
59
|
+
| `Context::ContextPolicyInput#input_token_limit / #with_input_token_limit` and protected `ContextPolicy#estimate_tokens` | Experimental (since v0.33.0) | Inspect and constrain selection budgets and estimate candidate content through Context-owned operations. [Policy budgeting](docs/context-policy-budgeting.md) |
|
|
60
|
+
| `Agent::Base.with_tools(definitions, id:, version:)` | Experimental (since v0.33.0) | Derive an Agent class with a complete replacement Tool map and explicit definition identity while preserving Phronomy authoring settings and filters. [Tool composition](docs/shared-state-sharing.md#deriving-an-agent-with-a-complete-tool-set) |
|
|
61
|
+
| `MultiAgent::SharedState.sharing(provider, id:, version:)`, `SharedState::Binding` and `SharedState::MemberLifecycleError` | Experimental (since v0.33.0) | Connect application-owned shared data and Tools to sequential coordination through a synchronous invocation-scoped provider. [Custom sharing contract and example](docs/shared-state-sharing.md) |
|
|
62
|
+
| `Tool::Base#approval_display(arguments:, facts:)` | Beta (since v0.33.0) | The Tool selects the arguments and derived facts a person can see in an approval request. [Approval display](docs/runtime-and-concurrency.md#tool-approval-display) |
|
|
63
|
+
| `Runtime#shutdown` result predicates `cleanup_complete?` and `clean?` | Beta | Check resource release with `cleanup_complete?`; `clean?` additionally requires a graceful shutdown. The obsolete `success?` alias is removed in v0.34.0. [Cleanup migration](docs/migrations/cleanup-audit.md#runtime-cleanup-completion) |
|
|
64
|
+
| `Phronomy::EventLoopReentrancyError` | Beta | Raised when a synchronous operation would block the EventLoop control thread. The obsolete `SchedulerReentrancyError` subclass is removed in v0.34.0. [Cleanup migration](docs/migrations/cleanup-audit.md#eventloop-reentrancy-errors) |
|
|
47
65
|
| `Phronomy::LLMAdapter::LMStudio` | Beta | Explicit known-output-error identification and bounded opt-in retry; [policy and evidence limits](docs/architecture/lm-studio-output-failures.md) |
|
|
48
66
|
| Agent `output_schema:`, `schema_delivery:`, `Agent::OutputError` and `OutputParser::JsonSchemaParser` | Beta | Agent returns validated immutable JSON or an explicit output failure; [contract and migration](docs/architecture/structured-output-and-scope.md) |
|
|
49
67
|
| Agent `transcript_messages` / `transcript_messages_async` | Beta | Read immutable conversation Message values with the same restoration rules as execution results; [reading conversation state](docs/getting-started.md) |
|
|
@@ -200,6 +218,7 @@ contracts, timeout/cancellation semantics, metrics, and callback rules.
|
|
|
200
218
|
- [0.19 unified Persistence migration](docs/migrations/0.19.md).
|
|
201
219
|
- [0.22 semantic Multi-Agent Handoff migration](docs/migrations/0.22.md).
|
|
202
220
|
- [0.31 persistence and Agent lifetime migration](docs/migrations/0.31.md).
|
|
221
|
+
- [0.34 cleanup migration](docs/migrations/cleanup-audit.md) — replacement entry points and retained public operations.
|
|
203
222
|
- [Architecture Decision Records](docs/decisions/) — design rationale and superseding decisions.
|
|
204
223
|
- [CHANGELOG](CHANGELOG.md) — current development and recent release history.
|
|
205
224
|
- [Changelog archive: 0.14.0 and earlier](docs/changelog/0.14-and-earlier.md).
|
data/RELEASE_CHECKLIST.md
CHANGED
|
@@ -24,7 +24,9 @@ Copy it into the GitHub Release draft and check off each item.
|
|
|
24
24
|
|
|
25
25
|
- [ ] `bundle exec rspec --format documentation` — 0 failures
|
|
26
26
|
- [ ] `bundle exec rspec --tag integration` — 0 failures, all expected pending
|
|
27
|
-
- [ ]
|
|
27
|
+
- [ ] External language check passes on the complete release files; source,
|
|
28
|
+
comments, documentation and release notes are in English. Record the checker
|
|
29
|
+
command, file scope and result; no language-check script is bundled here.
|
|
28
30
|
- [ ] `bundle exec standardrb` — 0 offenses
|
|
29
31
|
- [ ] `COVERAGE=1 bundle exec rspec` — coverage above configured threshold (Issue #207)
|
|
30
32
|
- [ ] CI green on all Ruby matrix versions (3.2 / 3.3 / 3.4 / head)
|
|
@@ -38,6 +40,10 @@ Copy it into the GitHub Release draft and check off each item.
|
|
|
38
40
|
- [ ] No new `requires_approval` tools missing the approval gate
|
|
39
41
|
- [ ] No secrets, credentials, or PII in tool descriptions, schema strings, or spec fixtures
|
|
40
42
|
- [ ] Dependency audit passes: `bundle exec bundler-audit check --update`
|
|
43
|
+
Run `bundle install` first; the development Gemfile provides the audit tool.
|
|
44
|
+
Audit the complete resolved bundle, including development and test dependencies,
|
|
45
|
+
without advisory exclusions. A runtime-only audit does not satisfy this gate.
|
|
46
|
+
The dependency-audit CI job and the release workflow enforce the same command.
|
|
41
47
|
|
|
42
48
|
---
|
|
43
49
|
|
data/SECURITY.md
CHANGED
data/VERIFY.sh
CHANGED
|
@@ -30,12 +30,10 @@ required_files=(
|
|
|
30
30
|
lib/phronomy/persistence/durable_record.rb
|
|
31
31
|
lib/phronomy/persistence/durable_codec.rb
|
|
32
32
|
lib/phronomy/persistence/repository_facades.rb
|
|
33
|
-
lib/phronomy/persistence/migration/initial_format_migration.rb
|
|
34
33
|
sig/phronomy/persistence.rbs
|
|
35
34
|
spec/phronomy/persistence/backend_spi_api_spec.rb
|
|
36
35
|
spec/phronomy/persistence/durable_codec_spec.rb
|
|
37
36
|
spec/phronomy/persistence/in_memory_spec.rb
|
|
38
|
-
spec/phronomy/persistence/initial_format_migration_spec.rb
|
|
39
37
|
lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb
|
|
40
38
|
spec/phronomy/workflow_identity_contract_spec.rb
|
|
41
39
|
lib/phronomy/engine/invocation_context.rb
|
|
@@ -128,11 +126,9 @@ syntax_files=(
|
|
|
128
126
|
lib/phronomy/persistence/durable_record.rb
|
|
129
127
|
lib/phronomy/persistence/durable_codec.rb
|
|
130
128
|
lib/phronomy/persistence/repository_facades.rb
|
|
131
|
-
lib/phronomy/persistence/migration/initial_format_migration.rb
|
|
132
129
|
spec/phronomy/persistence/backend_spi_api_spec.rb
|
|
133
130
|
spec/phronomy/persistence/durable_codec_spec.rb
|
|
134
131
|
spec/phronomy/persistence/in_memory_spec.rb
|
|
135
|
-
spec/phronomy/persistence/initial_format_migration_spec.rb
|
|
136
132
|
lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb
|
|
137
133
|
spec/phronomy/workflow_identity_contract_spec.rb
|
|
138
134
|
lib/phronomy/engine/invocation_context.rb
|
|
@@ -327,17 +323,14 @@ ruby -c lib/phronomy/persistence/durable_record.rb >/dev/null
|
|
|
327
323
|
ruby -c lib/phronomy/persistence/durable_codec.rb >/dev/null
|
|
328
324
|
ruby -c lib/phronomy/persistence/repository_facades.rb >/dev/null
|
|
329
325
|
ruby -c lib/phronomy/persistence/in_memory.rb >/dev/null
|
|
330
|
-
ruby -c lib/phronomy/persistence/migration/initial_format_migration.rb >/dev/null
|
|
331
326
|
ruby -c spec/phronomy/persistence/backend_spi_api_spec.rb >/dev/null
|
|
332
327
|
ruby -c spec/phronomy/persistence/durable_codec_spec.rb >/dev/null
|
|
333
328
|
ruby -c spec/phronomy/persistence/in_memory_spec.rb >/dev/null
|
|
334
|
-
ruby -c spec/phronomy/persistence/initial_format_migration_spec.rb >/dev/null
|
|
335
329
|
bundle exec rbs -I sig validate
|
|
336
330
|
bundle exec rspec \
|
|
337
331
|
spec/phronomy/persistence/backend_spi_api_spec.rb \
|
|
338
332
|
spec/phronomy/persistence/durable_codec_spec.rb \
|
|
339
333
|
spec/phronomy/persistence/in_memory_spec.rb \
|
|
340
|
-
spec/phronomy/persistence/initial_format_migration_spec.rb \
|
|
341
334
|
spec/phronomy/persistence/backend_contract_spec.rb \
|
|
342
335
|
spec/phronomy/agent/agent_execution_codec_spec.rb \
|
|
343
336
|
spec/phronomy/agent/llm_input_manifest_spec.rb \
|
|
@@ -530,8 +523,7 @@ legacy_agent_invocation_identity="$(
|
|
|
530
523
|
legacy_agent_invocation_identity="$(
|
|
531
524
|
printf '%s\n' "$legacy_agent_invocation_identity" |
|
|
532
525
|
grep -v 'lib/phronomy/agent/base.rb' |
|
|
533
|
-
grep -v 'lib/phronomy/agent/agent_execution.rb'
|
|
534
|
-
grep -v 'lib/phronomy/persistence/migration/initial_format_migration.rb' || true
|
|
526
|
+
grep -v 'lib/phronomy/agent/agent_execution.rb' || true
|
|
535
527
|
)"
|
|
536
528
|
if [[ -n "$legacy_agent_invocation_identity" ]]; then
|
|
537
529
|
echo "FAIL: active source/RBS reintroduced agent_invocation_id domain identity:" >&2
|
|
@@ -175,7 +175,12 @@ unchanged and acyclic; Ruby+RBS architecture validation passes.
|
|
|
175
175
|
|
|
176
176
|
## Static exception inventory
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
The original inventory used Ripper to inspect 339 Ruby files and listed 63
|
|
179
|
+
definitions, including base, private and optional-integration types. The table
|
|
180
|
+
below removes the superseded `SchedulerReentrancyError` entry as part of the
|
|
181
|
+
[cleanup migration](../migrations/cleanup-audit.md). These historical scan totals
|
|
182
|
+
are not a current count of source files or public APIs. Ruby declarations
|
|
183
|
+
establish ownership; RBS declarations are checked at their owning boundaries.
|
|
179
184
|
|
|
180
185
|
| Class | Parent | Declaration |
|
|
181
186
|
|---|---|---|
|
|
@@ -208,7 +213,6 @@ Ripper inspected 339 Ruby files. The following 63 definitions include base, priv
|
|
|
208
213
|
| `Phronomy::RecursionLimitError` | `Phronomy::Error` | `lib/phronomy/execution/recursion_limit_error.rb` |
|
|
209
214
|
| `Phronomy::RuntimeShutdownError` | `Phronomy::Error` | `lib/phronomy/execution/runtime_shutdown_error.rb` |
|
|
210
215
|
| `Phronomy::RuntimeShutdownReentrancyError` | `Phronomy::RuntimeShutdownError` | `lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb` |
|
|
211
|
-
| `Phronomy::SchedulerReentrancyError` | `Phronomy::EventLoopReentrancyError` | `lib/phronomy/execution/scheduler_reentrancy_error.rb` |
|
|
212
216
|
| `Phronomy::TimeoutError` | `Phronomy::Error` | `lib/phronomy/execution/timeout_error.rb` |
|
|
213
217
|
| `Phronomy::FilterBlockError` | `Phronomy::Error` | `lib/phronomy/filter/filter_block_error.rb` |
|
|
214
218
|
| `Phronomy::GeneratorVerifier::UnacceptedResultError` | `Phronomy::Error` | `lib/phronomy/generation/generator_verifier/unaccepted_result_error.rb` |
|
|
@@ -83,10 +83,11 @@ with lazy grammar` is a known native schema/Tool rejection. It is classified as
|
|
|
83
83
|
stream operations. Unrelated string errors and requests without a schema retain
|
|
84
84
|
their ordinary transport-error behavior. The adapter neither downgrades schemas
|
|
85
85
|
nor replays requests. RubyLLM's configured transport retry policy is independent.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
On the supported HTTP stack, the known schema rejection retains its
|
|
87
|
+
`RubyLLM::BadRequestError` cause even when streaming. An HTTP 500 with the same
|
|
88
|
+
body retains its `RubyLLM::ServerError` cause. The
|
|
89
|
+
[HTTP dependency rationale](trust-acceptance-followup.md#http-dependency-correction)
|
|
90
|
+
documents the tested versions and request counts.
|
|
90
91
|
|
|
91
92
|
`Response` records finish_reason (stop, tool_calls, length, content_filter, refusal,
|
|
92
93
|
unknown), refusal and raw_finish_reason metadata. Only normal final completion can
|
|
@@ -172,10 +173,9 @@ Normal low-quality output follows the configured evaluation loop as before.
|
|
|
172
173
|
|
|
173
174
|
`Result#failure` is nil except for output_failed, where it carries the owned error
|
|
174
175
|
diagnostic (without raw_output). Hash/JSON round trips, equality, hashing and freeze
|
|
175
|
-
include it. `Result.from_h` requires every
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
not overwrite the source or silently alter invalid data. `raise_if_unaccepted`
|
|
176
|
+
include it. `Result.from_h` requires every field, including `failure: nil` for
|
|
177
|
+
results without an output failure. It rejects incomplete serialized results.
|
|
178
|
+
`raise_if_unaccepted`
|
|
179
179
|
retains the Result and the original OutputError cause in the same process.
|
|
180
180
|
|
|
181
181
|
## Symmetric lifetime rules
|
|
@@ -37,6 +37,21 @@ retain the SDK server-error cause. Configured SDK retries for 429/500 remain;
|
|
|
37
37
|
a 400 rejection is one HTTP attempt even with three retries configured.
|
|
38
38
|
Partial-stream replay and callback identity regressions remain in place.
|
|
39
39
|
|
|
40
|
+
RubyLLM owns HTTP transport selection for model calls. Phronomy's LLM and
|
|
41
|
+
Embeddings adapters recognize Faraday exception classes at the SDK boundary.
|
|
42
|
+
The two gemspec declarations are a temporary workaround for the streaming
|
|
43
|
+
status bug. Remove both when all supported RubyLLM versions preserve the
|
|
44
|
+
status or exclude the affected HTTP stack through their own dependencies.
|
|
45
|
+
|
|
46
|
+
Verify removal with the two Phronomy declarations omitted. Resolve the minimum
|
|
47
|
+
supported RubyLLM and its permitted HTTP dependencies without the existing CI
|
|
48
|
+
pins. If the old pair still resolves, test it explicitly. If RubyLLM excludes
|
|
49
|
+
it, confirm resolution rejects it and test the new minimum. In both cases run
|
|
50
|
+
`spec/integration/transport_error_spec.rb` against the minimum and the normal
|
|
51
|
+
bundle. With three retries configured, a streaming HTTP 400 with a string
|
|
52
|
+
error body must retain its 400 cause and send one POST. An HTTP 500 with the
|
|
53
|
+
same body must retain its 500 cause and send four POSTs.
|
|
54
|
+
|
|
40
55
|
Before and after, application handling uses the same public API:
|
|
41
56
|
|
|
42
57
|
```ruby
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Context Policy budgeting
|
|
2
|
+
|
|
3
|
+
Applications choose how much model input capacity to use for one selection.
|
|
4
|
+
Context exposes that capacity and candidate estimates, while the framework keeps
|
|
5
|
+
its internal budget representation and final input validation.
|
|
6
|
+
|
|
7
|
+
For example, a review Policy can reserve part of the available input for its own
|
|
8
|
+
safety margin, then let DefaultPolicy choose history within the smaller limit.
|
|
9
|
+
No application needs to construct a private budget object for that operation.
|
|
10
|
+
|
|
11
|
+
## Reading and constraining input limits
|
|
12
|
+
|
|
13
|
+
A Policy input reports its current selection limit and can derive another input
|
|
14
|
+
with a smaller limit. Derivation preserves the original input and all its
|
|
15
|
+
material; it does not change model capabilities or generation settings.
|
|
16
|
+
|
|
17
|
+
For example, an input with a limit of 8192 can produce a selection input capped
|
|
18
|
+
at 5632. The original input still reports 8192. The derived input cannot later
|
|
19
|
+
increase its limit above 5632.
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
require "phronomy"
|
|
23
|
+
|
|
24
|
+
class ReservedInputPolicy < Phronomy::Context::ContextPolicy
|
|
25
|
+
def call(input)
|
|
26
|
+
limit = input.input_token_limit
|
|
27
|
+
unless limit
|
|
28
|
+
raise Phronomy::ConfigurationError, "Review requires known model input capacity"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
selection_limit = limit - limit / 4 - limit / 16
|
|
32
|
+
selected_input = input.with_input_token_limit(selection_limit)
|
|
33
|
+
Phronomy::Context::DefaultPolicy.instance.call(selected_input)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`input_token_limit` returns an Integer or `nil` for an unknown limit.
|
|
39
|
+
`with_input_token_limit(limit)` requires a positive Integer without coercion;
|
|
40
|
+
zero, negative values, strings and floating-point values raise `ArgumentError`.
|
|
41
|
+
Increasing a known limit also raises `ArgumentError`. The returned
|
|
42
|
+
`ContextPolicyInput` is immutable, and its other field values are unchanged.
|
|
43
|
+
|
|
44
|
+
When the original limit is unknown, an explicit positive selection cap is
|
|
45
|
+
allowed. That cap expresses application policy and does not establish that the
|
|
46
|
+
model can accept that input. Applications that require known capability must
|
|
47
|
+
reject `nil`, as the example above does.
|
|
48
|
+
|
|
49
|
+
The cap controls selection performed with the derived input. The framework's
|
|
50
|
+
final validation still uses the actual model budget; returning a different Plan
|
|
51
|
+
does not make the derived cap a new global validator. It does not set an output
|
|
52
|
+
token limit, alter a saved Manifest, or affect another Agent call.
|
|
53
|
+
|
|
54
|
+
## Estimating candidate content
|
|
55
|
+
|
|
56
|
+
A Policy can estimate proposed content through the same canonical encoding and
|
|
57
|
+
estimator used by its generated items. The Policy remains responsible for
|
|
58
|
+
choosing complete items and preserving required conversation groups.
|
|
59
|
+
|
|
60
|
+
For example, a retrieval Policy can fit a JSON page before adding it as
|
|
61
|
+
knowledge. JSON escaping contributes to the estimate because the complete
|
|
62
|
+
serialized page is measured.
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
require "phronomy"
|
|
66
|
+
require "json"
|
|
67
|
+
|
|
68
|
+
class PageKnowledgePolicy < Phronomy::Context::ContextPolicy
|
|
69
|
+
def initialize(page:, allowance:)
|
|
70
|
+
@page = page
|
|
71
|
+
@allowance = allowance
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def call(input)
|
|
75
|
+
content = JSON.generate(@page)
|
|
76
|
+
if estimate_tokens(content) > @allowance
|
|
77
|
+
raise Phronomy::Context::BudgetExceededError, "Retrieved page exceeds its allowance"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
page = knowledge_item(content: content, required: true)
|
|
81
|
+
Phronomy::Context::DefaultPolicy.instance.call(
|
|
82
|
+
input.with(knowledge: input.knowledge + [page])
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`estimate_tokens(content)` is a protected subclass helper. Strings are estimated
|
|
89
|
+
as text; other values use the framework's canonical JSON encoding. The helper
|
|
90
|
+
uses the existing estimator and does not introduce a second token-counting
|
|
91
|
+
algorithm. Estimates are approximate unless the configured estimator provides
|
|
92
|
+
model-specific counts. A successful estimate does not guarantee provider
|
|
93
|
+
acceptance or replace final input validation.
|
|
94
|
+
|
|
95
|
+
## Compatibility and ownership
|
|
96
|
+
|
|
97
|
+
These operations belong to the Context contract and reuse its existing budget
|
|
98
|
+
and estimation mechanisms. Applications own allocation ratios and retrieval
|
|
99
|
+
policy; DefaultPolicy retains its current selection algorithm.
|
|
100
|
+
|
|
101
|
+
For example, Example 22 can budget retrieved findings through these operations
|
|
102
|
+
while SharedState continues to coordinate Agents and application Tools continue
|
|
103
|
+
to own the finding store. No SharedState-to-Context dependency is introduced.
|
|
104
|
+
|
|
105
|
+
The operations are Experimental and available since v0.33.0. Existing private
|
|
106
|
+
budget and estimation classes remain implementation details. Existing ContextPolicy
|
|
107
|
+
factories and DefaultPolicy behavior are unchanged, and the protected helper
|
|
108
|
+
applies equally to normal Agent calls and SharedState member calls.
|
|
@@ -61,11 +61,12 @@ Tool-role messages are one indivisible conversation group. When the selected
|
|
|
61
61
|
Plan is realized, the Manifest records the Framework conversation-group identity
|
|
62
62
|
and classifies canonical Tool exchanges as `tool_exchanges` for Handoff.
|
|
63
63
|
|
|
64
|
-
`
|
|
65
|
-
group identity.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
internal Context-input
|
|
64
|
+
The current `Agent::TransferProjection` groups Manifest segments by that
|
|
65
|
+
conversation-group identity. Segments without a group identity are projected
|
|
66
|
+
independently. Historical selection-unit identifiers and kinds are no longer
|
|
67
|
+
interpreted, and Context assembly does not recreate `Selection::Unit`.
|
|
68
|
+
`Selection::Candidate` and `Selection::Constraint` remain internal Context-input
|
|
69
|
+
normalization details only.
|
|
69
70
|
|
|
70
71
|
Handoff Policy answers what may cross the Agent boundary. Target Context Policy
|
|
71
72
|
remains the final authority for what enters each Target LLM call. Transferred
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "phronomy"
|
|
5
|
+
|
|
6
|
+
module SharedStateCustomSharingExample
|
|
7
|
+
class IssueBoard
|
|
8
|
+
attr_reader :closed
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@records = []
|
|
12
|
+
@closed = false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def record(content:, category:, status:, author:, cycle:)
|
|
16
|
+
raise "Issue board is closed" if closed
|
|
17
|
+
|
|
18
|
+
row = {id: @records.length + 1, content: content.dup.freeze,
|
|
19
|
+
category: category.dup.freeze, status: status.dup.freeze,
|
|
20
|
+
author: author.dup.freeze, cycle: cycle}.freeze
|
|
21
|
+
@records << row
|
|
22
|
+
row
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def search(category: nil, status: nil)
|
|
26
|
+
snapshot.select do |row|
|
|
27
|
+
(category.nil? || row.fetch(:category) == category) &&
|
|
28
|
+
(status.nil? || row.fetch(:status) == status)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def snapshot
|
|
33
|
+
raise "Issue board is closed" if closed
|
|
34
|
+
|
|
35
|
+
@records.dup.freeze
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def close
|
|
39
|
+
@records.clear
|
|
40
|
+
@closed = true
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class IssueSharing
|
|
45
|
+
def open(input:, config:)
|
|
46
|
+
board = IssueBoard.new
|
|
47
|
+
yield Phronomy::MultiAgent::SharedState::Binding.new(
|
|
48
|
+
store: board,
|
|
49
|
+
tools: ->(member:, cycle:) { tool_definitions(board, member, cycle) }
|
|
50
|
+
)
|
|
51
|
+
ensure
|
|
52
|
+
board&.close
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def tool_definitions(board, member, cycle)
|
|
58
|
+
{
|
|
59
|
+
record_tool(board, member.agent_definition.fetch(:id), cycle) => nil,
|
|
60
|
+
search_tool(board) => nil
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def record_tool(board, author, cycle)
|
|
65
|
+
Class.new(Phronomy::Tool::Base) do
|
|
66
|
+
tool_name "record_issue"
|
|
67
|
+
description "Record one issue with an explicit category and status."
|
|
68
|
+
execution_mode :cooperative
|
|
69
|
+
param :content, type: :string, desc: "The issue description."
|
|
70
|
+
param :category, type: :string, enum: %w[security performance],
|
|
71
|
+
desc: "The issue category."
|
|
72
|
+
param :status, type: :string, enum: %w[open closed],
|
|
73
|
+
desc: "The issue status."
|
|
74
|
+
|
|
75
|
+
define_method(:execute) do |content:, category:, status:|
|
|
76
|
+
board.record(content: content, category: category, status: status,
|
|
77
|
+
author: author, cycle: cycle).to_json
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def search_tool(board)
|
|
83
|
+
Class.new(Phronomy::Tool::Base) do
|
|
84
|
+
tool_name "search_issues"
|
|
85
|
+
description "Read issues matching all supplied category and status filters."
|
|
86
|
+
execution_mode :cooperative
|
|
87
|
+
param :category, type: :string, required: false, enum: %w[security performance],
|
|
88
|
+
desc: "Optional exact category filter."
|
|
89
|
+
param :status, type: :string, required: false, enum: %w[open closed],
|
|
90
|
+
desc: "Optional exact status filter."
|
|
91
|
+
|
|
92
|
+
define_method(:execute) do |category: nil, status: nil|
|
|
93
|
+
board.search(category: category, status: status).to_json
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class Researcher < Phronomy::Agent::Base
|
|
100
|
+
agent_definition id: "shared-issue-researcher", version: 1
|
|
101
|
+
model ENV.fetch("PHRONOMY_EXAMPLE_MODEL", "configured-model")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class ReviewTeam < Phronomy::MultiAgent::SharedState
|
|
105
|
+
sharing IssueSharing.new, id: "shared-issues", version: 1
|
|
106
|
+
member Researcher
|
|
107
|
+
max_cycles 2
|
|
108
|
+
coordination "Use search_issues to inspect relevant issues. " \
|
|
109
|
+
"Use record_issue to record each new issue with its category and status."
|
|
110
|
+
aggregate { |board| board.snapshot }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
module OfflineDemo
|
|
114
|
+
def self.run
|
|
115
|
+
opened_board = nil
|
|
116
|
+
output = IssueSharing.new.open(input: "Review authentication", config: {}) do |binding|
|
|
117
|
+
opened_board = binding.store
|
|
118
|
+
definitions = binding.tool_definitions(member: Researcher, cycle: 1)
|
|
119
|
+
tools = definitions.keys.to_h { |tool| [tool.tool_name, tool.new] }
|
|
120
|
+
recorder = tools.fetch("record_issue")
|
|
121
|
+
recorder.call({"content" => "Missing token validation", "category" => "security", "status" => "open"})
|
|
122
|
+
recorder.call({"content" => "Slow cache lookup", "category" => "performance", "status" => "open"})
|
|
123
|
+
recorder.call({"content" => "Session expiry corrected", "category" => "security", "status" => "closed"})
|
|
124
|
+
|
|
125
|
+
matches = JSON.parse(tools.fetch("search_issues").call({"category" => "security", "status" => "open"}))
|
|
126
|
+
unless matches.map { |row| row.fetch("content") } == ["Missing token validation"]
|
|
127
|
+
raise "Search did not apply both filters"
|
|
128
|
+
end
|
|
129
|
+
all_records = binding.store.snapshot
|
|
130
|
+
raise "Search changed the board" unless all_records.length == 3
|
|
131
|
+
|
|
132
|
+
{matches: matches, total_records: all_records.length}
|
|
133
|
+
end
|
|
134
|
+
raise "Provider did not close the board" unless opened_board.closed
|
|
135
|
+
|
|
136
|
+
puts JSON.pretty_generate(output.merge(provider_closed: true))
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
if $PROGRAM_NAME == __FILE__
|
|
142
|
+
abort "Run with --offline, or require this file from a configured Phronomy application." unless ARGV == ["--offline"]
|
|
143
|
+
|
|
144
|
+
SharedStateCustomSharingExample::OfflineDemo.run
|
|
145
|
+
end
|