phronomy 0.32.0 → 0.33.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 +77 -0
- data/README.md +7 -1
- data/RELEASE_CHECKLIST.md +4 -0
- data/docs/architecture/structured-output-and-scope.md +5 -4
- data/docs/architecture/trust-acceptance-followup.md +15 -0
- data/docs/context-policy-budgeting.md +108 -0
- data/docs/examples/shared_state_custom_sharing.rb +145 -0
- data/docs/features.md +2 -0
- data/docs/mcp-client.md +4 -2
- data/docs/migrations/shared-state-multi-agent.md +5 -0
- data/docs/runtime-and-concurrency.md +36 -0
- data/docs/shared-state-sharing.md +223 -0
- data/lib/phronomy/agent/base.rb +53 -0
- data/lib/phronomy/agent/execution/execution_coordinator.rb +11 -0
- data/lib/phronomy/agent/lifecycle/hosted_invocation.rb +2 -5
- data/lib/phronomy/agent/tool_execution/tool_approval_request.rb +3 -2
- data/lib/phronomy/agent/tool_execution/tool_invocation.rb +12 -36
- data/lib/phronomy/context/context_policy.rb +11 -2
- data/lib/phronomy/context/context_policy_input.rb +26 -0
- 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 +147 -2
- 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 +7 -2
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "digest"
|
|
4
|
+
|
|
3
5
|
module Phronomy
|
|
4
6
|
module MultiAgent
|
|
5
|
-
#
|
|
7
|
+
# Coordinates members through default findings or application-defined shared data.
|
|
6
8
|
# @api public
|
|
7
9
|
class SharedState
|
|
8
10
|
# Semantic revision of the framework-owned SharedState instrumentation
|
|
@@ -70,6 +72,29 @@ module Phronomy
|
|
|
70
72
|
block ? @aggregator = block : @aggregator
|
|
71
73
|
end
|
|
72
74
|
|
|
75
|
+
# Declare application-owned sharing for each invocation. The provider's
|
|
76
|
+
# open(input:, config:) method must synchronously yield one Binding.
|
|
77
|
+
# Its block scope owns any connection or other application resource.
|
|
78
|
+
# Increment version when the shared Tool schemas or meanings change.
|
|
79
|
+
# @param provider [#open]
|
|
80
|
+
# @param id [String] stable application sharing definition identity
|
|
81
|
+
# @param version [Integer] positive sharing definition revision
|
|
82
|
+
# @api public
|
|
83
|
+
def sharing(provider, id:, version:)
|
|
84
|
+
raise ArgumentError, "sharing provider must respond to open" unless provider.respond_to?(:open)
|
|
85
|
+
unless id.is_a?(String) && !id.strip.empty?
|
|
86
|
+
raise ArgumentError, "sharing id must be a nonempty String"
|
|
87
|
+
end
|
|
88
|
+
unless version.is_a?(Integer) && version.positive?
|
|
89
|
+
raise ArgumentError, "sharing version must be a positive Integer"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
@sharing = {provider: provider, id: id.dup.freeze, version: version}.freeze
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @api private
|
|
96
|
+
def _sharing = @sharing
|
|
97
|
+
|
|
73
98
|
def _members = Array(@members)
|
|
74
99
|
def _coordination = @coordination
|
|
75
100
|
def _max_cycles = @max_cycles
|
|
@@ -81,6 +106,8 @@ module Phronomy
|
|
|
81
106
|
# @api public
|
|
82
107
|
def invoke(input, config: {})
|
|
83
108
|
validate_termination!
|
|
109
|
+
return invoke_with_sharing(input, config, self.class._sharing) if self.class._sharing
|
|
110
|
+
|
|
84
111
|
store = KnowledgeStore.new
|
|
85
112
|
cycles, terminated_by = coordinate_members(input, store)
|
|
86
113
|
|
|
@@ -97,7 +124,11 @@ module Phronomy
|
|
|
97
124
|
cycle_limit = max_cycles || Float::INFINITY
|
|
98
125
|
|
|
99
126
|
(1..cycle_limit).each do |cycle|
|
|
100
|
-
|
|
127
|
+
if block_given?
|
|
128
|
+
yield cycle
|
|
129
|
+
else
|
|
130
|
+
invoke_members(input, store, cycle)
|
|
131
|
+
end
|
|
101
132
|
completed_cycles = cycle
|
|
102
133
|
|
|
103
134
|
if self.class._terminate_when&.call(store)
|
|
@@ -137,6 +168,120 @@ module Phronomy
|
|
|
137
168
|
"max_cycles or timeout must be configured before invoking SharedState"
|
|
138
169
|
end
|
|
139
170
|
|
|
171
|
+
def invoke_with_sharing(input, config, sharing)
|
|
172
|
+
unless self.class._aggregator
|
|
173
|
+
raise ArgumentError, "aggregate must be configured when sharing is configured"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
open_sharing(sharing, input, config) do |binding|
|
|
177
|
+
cycles, terminated_by = coordinate_members(input, binding.store) do |cycle|
|
|
178
|
+
invoke_shared_members(input, binding, cycle, sharing)
|
|
179
|
+
end
|
|
180
|
+
{output: aggregate_findings(binding.store), cycles: cycles, terminated_by: terminated_by}
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def open_sharing(sharing, input, config)
|
|
185
|
+
opening_thread = Thread.current
|
|
186
|
+
active = true
|
|
187
|
+
entered = completed = false
|
|
188
|
+
result = callback_error = nil
|
|
189
|
+
|
|
190
|
+
sharing.fetch(:provider).open(input: input, config: config) do |binding|
|
|
191
|
+
unless active && Thread.current.equal?(opening_thread)
|
|
192
|
+
raise ArgumentError, "sharing provider must yield synchronously within open"
|
|
193
|
+
end
|
|
194
|
+
raise ArgumentError, "sharing provider must yield exactly once" if entered
|
|
195
|
+
|
|
196
|
+
entered = true
|
|
197
|
+
unless binding.is_a?(Binding)
|
|
198
|
+
raise ArgumentError, "sharing provider must yield a SharedState::Binding"
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
result = yield binding
|
|
202
|
+
completed = true
|
|
203
|
+
rescue => error
|
|
204
|
+
callback_error = error
|
|
205
|
+
raise
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
raise callback_error if callback_error
|
|
209
|
+
unless entered && completed
|
|
210
|
+
raise ArgumentError, "sharing provider must yield exactly once and complete the block"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
result
|
|
214
|
+
ensure
|
|
215
|
+
active = false
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def invoke_shared_members(input, binding, cycle, sharing)
|
|
219
|
+
self.class._members.each do |member_config|
|
|
220
|
+
member = member_config.fetch(:klass)
|
|
221
|
+
definitions = shared_tool_definitions(member, binding, cycle)
|
|
222
|
+
instrumented = build_shared_researcher(member, definitions, sharing)
|
|
223
|
+
prompt = shared_prompt(input, member_config[:instruction])
|
|
224
|
+
invoke_shared_researcher(instrumented, prompt)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def invoke_shared_researcher(instrumented, prompt)
|
|
229
|
+
agent = instrumented.new
|
|
230
|
+
suspended = false
|
|
231
|
+
result = agent.invoke(prompt)
|
|
232
|
+
if result.is_a?(Hash) && result[:suspended]
|
|
233
|
+
suspended = true
|
|
234
|
+
raise MemberLifecycleError.new(agent: agent, result: result, reason: :suspended)
|
|
235
|
+
end
|
|
236
|
+
result
|
|
237
|
+
ensure
|
|
238
|
+
release_shared_member(agent, result, $!) if agent && !suspended
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def release_shared_member(agent, result, primary_error)
|
|
242
|
+
agent.unload!
|
|
243
|
+
rescue => release_error
|
|
244
|
+
error = MemberLifecycleError.new(agent: agent, result: result, reason: :release_failed,
|
|
245
|
+
primary_error: primary_error, release_error: release_error)
|
|
246
|
+
raise error, cause: primary_error || release_error
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def shared_tool_definitions(member, binding, cycle)
|
|
250
|
+
definitions = member.tools.to_h { |tool| [tool, member.tool_aliases[tool]] }
|
|
251
|
+
added = binding.tool_definitions(member: member, cycle: cycle)
|
|
252
|
+
unless (definitions.keys & added.keys).empty?
|
|
253
|
+
raise ArgumentError, "sharing Tool classes must not replace member Tools"
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
definitions = definitions.merge(added)
|
|
257
|
+
names = definitions.map { |tool, alias_name| (alias_name || tool.tool_name).to_s }
|
|
258
|
+
if names.any?(&:empty?) || names.uniq.length != names.length
|
|
259
|
+
raise ArgumentError, "sharing and member Tools must have distinct nonempty names or aliases"
|
|
260
|
+
end
|
|
261
|
+
definitions
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def build_shared_researcher(member, definitions, sharing)
|
|
265
|
+
definition = shared_definition_for(member, sharing)
|
|
266
|
+
member.with_tools(definitions, id: definition.fetch(:id), version: definition.fetch(:version))
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def shared_definition_for(member, sharing)
|
|
270
|
+
parent = member.agent_definition
|
|
271
|
+
identity = [parent.fetch(:id), parent.fetch(:version), sharing.fetch(:id), sharing.fetch(:version)]
|
|
272
|
+
digest = Digest::SHA256.hexdigest(Phronomy::CanonicalJSON.dump(identity))
|
|
273
|
+
{id: "Phronomy::MultiAgent::SharedState::Application/#{digest}", version: 1}
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def shared_prompt(input, instruction)
|
|
277
|
+
guide = self.class._coordination ||
|
|
278
|
+
"Use the provided Tools to collaborate through the application's shared data."
|
|
279
|
+
parts = [guide]
|
|
280
|
+
parts << "\nYour specific focus for this session: #{instruction}" if instruction
|
|
281
|
+
parts << "\n\nTask: #{input}"
|
|
282
|
+
parts.join
|
|
283
|
+
end
|
|
284
|
+
|
|
140
285
|
def invoke_researcher(
|
|
141
286
|
researcher_class,
|
|
142
287
|
store,
|
data/lib/phronomy/tool/base.rb
CHANGED
|
@@ -363,10 +363,17 @@ module Phronomy
|
|
|
363
363
|
# @api public
|
|
364
364
|
def validate_arguments(args)
|
|
365
365
|
schema = parameters_schema
|
|
366
|
-
|
|
367
|
-
|
|
366
|
+
validator = @validation_schema
|
|
367
|
+
if !validator || validator.json_schema != schema
|
|
368
|
+
declaration = self.class.parameters_schema_definition
|
|
369
|
+
validator = if Schema === declaration && declaration.instance_of?(Schema) && declaration.json_schema.equal?(schema)
|
|
370
|
+
declaration.for_tool_instance
|
|
371
|
+
else
|
|
372
|
+
Schema.new(schema)
|
|
373
|
+
end
|
|
374
|
+
@validation_schema = validator
|
|
368
375
|
end
|
|
369
|
-
values, error =
|
|
376
|
+
values, error = validator.validate(args || {}, coerce: self.class.on_schema_error == :coerce)
|
|
370
377
|
if values && !self.class.parameters_schema_definition
|
|
371
378
|
# Legacy optional param nil means omission, preserving Ruby defaults.
|
|
372
379
|
self.class.declared_parameters.each do |name, parameter|
|
|
@@ -442,6 +449,24 @@ module Phronomy
|
|
|
442
449
|
{}
|
|
443
450
|
end
|
|
444
451
|
|
|
452
|
+
# Selects the arguments and policy facts that may appear in a human approval
|
|
453
|
+
# request. Arguments may be raw or validated. This does not change either input.
|
|
454
|
+
# @api public
|
|
455
|
+
def approval_display(arguments:, facts:)
|
|
456
|
+
sensitive = arguments.transform_keys(&:to_sym).slice(*self.class.redact_params).values
|
|
457
|
+
indexed = {}
|
|
458
|
+
sensitive.each do |value|
|
|
459
|
+
if Hash === value || Array === value
|
|
460
|
+
approval_comparison_key(value) { |key| indexed[key] = true }
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
display_facts = redact_approval_fact(facts) do |value|
|
|
464
|
+
sensitive_fact_value?(value, sensitive) || (!indexed.empty? && indexed.key?(approval_comparison_key(value)))
|
|
465
|
+
end
|
|
466
|
+
# If the entire facts Hash matches a sensitive argument, hide all of its keys.
|
|
467
|
+
{arguments: redacted_args(arguments), facts: display_facts.is_a?(Hash) ? display_facts : {}}
|
|
468
|
+
end
|
|
469
|
+
|
|
445
470
|
# @api public
|
|
446
471
|
def execute(**_args)
|
|
447
472
|
raise NotImplementedError, "#{self.class}#execute is not implemented"
|
|
@@ -536,6 +561,47 @@ module Phronomy
|
|
|
536
561
|
end
|
|
537
562
|
end
|
|
538
563
|
|
|
564
|
+
# Index each sensitive subtree and scalar once. Object member names describe
|
|
565
|
+
# structure; its member values and array elements can be copied into facts.
|
|
566
|
+
def approval_comparison_key(value, &index)
|
|
567
|
+
key = case value
|
|
568
|
+
when Hash then value.transform_values { |item| approval_comparison_key(item, &index) }
|
|
569
|
+
when Array then value.map { |item| approval_comparison_key(item, &index) }
|
|
570
|
+
when Symbol then value.to_s
|
|
571
|
+
when Float then (value.finite? && value == value.to_i) ? value.to_i : value
|
|
572
|
+
else value
|
|
573
|
+
end
|
|
574
|
+
index&.call(key)
|
|
575
|
+
key
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
def sensitive_fact_value?(value, sensitive_values)
|
|
579
|
+
sensitive_values.any? do |sensitive|
|
|
580
|
+
sensitive == value ||
|
|
581
|
+
(Symbol === value && value.to_s == sensitive) ||
|
|
582
|
+
(Symbol === sensitive && sensitive.to_s == value)
|
|
583
|
+
end
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def redact_approval_fact(value, &sensitive)
|
|
587
|
+
return "[REDACTED]" if sensitive.call(value)
|
|
588
|
+
|
|
589
|
+
case value
|
|
590
|
+
when Hash
|
|
591
|
+
value.each_with_object({}) do |(key, item), result|
|
|
592
|
+
next if sensitive.call(key)
|
|
593
|
+
|
|
594
|
+
result[key] = redact_approval_fact(item, &sensitive)
|
|
595
|
+
end.freeze
|
|
596
|
+
when Array
|
|
597
|
+
value.map { |item| redact_approval_fact(item, &sensitive) }.freeze
|
|
598
|
+
when String
|
|
599
|
+
"[REDACTED]"
|
|
600
|
+
else
|
|
601
|
+
value
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
|
|
539
605
|
def nested_schema_to_json_schema(nested)
|
|
540
606
|
nested.each_with_object({}) do |(prop_name, spec), result|
|
|
541
607
|
type = spec[:type].to_s
|
data/lib/phronomy/tool/schema.rb
CHANGED
|
@@ -43,19 +43,17 @@ module Phronomy
|
|
|
43
43
|
unless @json_schema.is_a?(Hash) && @json_schema["type"] == "object"
|
|
44
44
|
raise ArgumentError, "Tool schema must declare an object"
|
|
45
45
|
end
|
|
46
|
+
@json_schema = Phronomy::Values::Immutable.copy(@json_schema)
|
|
46
47
|
@references = []
|
|
47
48
|
validate_keywords!(@json_schema)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
freeze
|
|
57
|
-
rescue JSONSchemer::InvalidRefPointer, JSONSchemer::InvalidRefResolution => error
|
|
58
|
-
raise ArgumentError, "invalid local Tool schema reference: #{error.message}"
|
|
49
|
+
@references.freeze
|
|
50
|
+
compile_validator!
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Build an independent validator from this immutable Tool definition.
|
|
54
|
+
# @api private
|
|
55
|
+
def for_tool_instance
|
|
56
|
+
dup.compile_validator!
|
|
59
57
|
end
|
|
60
58
|
|
|
61
59
|
# @api public
|
|
@@ -99,6 +97,20 @@ module Phronomy
|
|
|
99
97
|
end
|
|
100
98
|
end
|
|
101
99
|
|
|
100
|
+
protected
|
|
101
|
+
|
|
102
|
+
def compile_validator!
|
|
103
|
+
options = {ref_resolver: ->(uri) { raise ArgumentError, "external Tool schema reference: #{uri}" }}
|
|
104
|
+
unless JSONSchemer.valid_schema?(@json_schema, **options)
|
|
105
|
+
raise ArgumentError, "invalid Tool JSON Schema"
|
|
106
|
+
end
|
|
107
|
+
@validator = JSONSchemer.schema(@json_schema, **options)
|
|
108
|
+
@references.each { |reference| @validator.ref(reference) }
|
|
109
|
+
freeze
|
|
110
|
+
rescue JSONSchemer::InvalidRefPointer, JSONSchemer::InvalidRefResolution => error
|
|
111
|
+
raise ArgumentError, "invalid local Tool schema reference: #{error.message}"
|
|
112
|
+
end
|
|
113
|
+
|
|
102
114
|
private
|
|
103
115
|
|
|
104
116
|
def error_message(error)
|
data/lib/phronomy/version.rb
CHANGED
data/scripts/api_snapshot.rb
CHANGED
|
@@ -106,9 +106,12 @@ def snapshot_entry(klass)
|
|
|
106
106
|
"public_class_methods" => (klass.public_methods(false) - BASELINE_CLASS_METHODS).sort
|
|
107
107
|
}
|
|
108
108
|
else
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
internal_methods = case klass.name
|
|
110
|
+
when "Phronomy::InvocationContext" then %i[__bind_execution __execution_scope]
|
|
111
|
+
when "Phronomy::Tool::Schema" then %i[for_tool_instance]
|
|
112
|
+
else []
|
|
113
|
+
end
|
|
114
|
+
instance_methods = (klass.public_instance_methods - BASELINE_INSTANCE_METHODS - internal_methods).sort
|
|
112
115
|
internal_class_methods = (klass == Phronomy::Storage::AsyncClient) ? %i[submit] : []
|
|
113
116
|
class_methods = (klass.public_methods(false) - BASELINE_CLASS_METHODS - internal_class_methods).sort
|
|
114
117
|
{
|
data/sig/phronomy/agent.rbs
CHANGED
|
@@ -83,6 +83,7 @@ module Phronomy
|
|
|
83
83
|
def self.instructions: (?untyped text) { (untyped) -> untyped } -> untyped
|
|
84
84
|
| (?untyped text) -> untyped
|
|
85
85
|
def self.tools: (?Hash[Class, String?] definitions) -> untyped
|
|
86
|
+
def self.with_tools: (Hash[Class, String?] definitions, id: String, version: Integer) -> singleton(Base)
|
|
86
87
|
def self.tool_aliases: () -> Hash[Class, String]
|
|
87
88
|
def self.provider: (?Symbol name) -> Symbol?
|
|
88
89
|
def self.temperature: (?Float value) -> Float?
|
data/sig/phronomy/context.rbs
CHANGED
|
@@ -110,6 +110,8 @@ module Phronomy
|
|
|
110
110
|
attr_reader previous_manifest: LLMInputManifest?
|
|
111
111
|
attr_reader metadata: Hash[untyped, untyped]
|
|
112
112
|
def initialize: (agent_id: String, ?execution_id: String?, call_sequence: Integer, call_mode: Symbol, ?instruction: Array[InstructionItem], ?knowledge: Array[KnowledgeItem], ?tools: Array[ToolItem], ?conversation: Array[Array[ConversationItem]], token_budget: untyped, ?model_config: Hash[untyped, untyped], previous_manifest: LLMInputManifest?, ?metadata: Hash[untyped, untyped]) -> void
|
|
113
|
+
def input_token_limit: () -> Integer?
|
|
114
|
+
def with_input_token_limit: (Integer limit) -> ContextPolicyInput
|
|
113
115
|
end
|
|
114
116
|
|
|
115
117
|
class ContextPlan
|
|
@@ -124,6 +126,7 @@ module Phronomy
|
|
|
124
126
|
class ContextPolicy
|
|
125
127
|
def call: (ContextPolicyInput input) -> ContextPlan
|
|
126
128
|
|
|
129
|
+
def estimate_tokens: (untyped content) -> Integer
|
|
127
130
|
def instruction_item: (content: untyped, ?role: Symbol, ?kind: Symbol, ?content_format: Symbol?, ?required: bool, ?metadata: Hash[untyped, untyped]) -> ContextPolicyInput::InstructionItem
|
|
128
131
|
def knowledge_item: (content: untyped, ?role: Symbol, ?kind: Symbol, ?content_format: Symbol?, ?required: bool, ?metadata: Hash[untyped, untyped]) -> ContextPolicyInput::KnowledgeItem
|
|
129
132
|
def conversation_item: (content: untyped, role: Symbol, ?kind: Symbol, ?content_format: Symbol?, ?sequence: Integer?, ?required: bool, ?tool_call_id: String?, ?tool_call_ids: Array[String], ?delivery: Symbol, ?metadata: Hash[untyped, untyped]) -> ContextPolicyInput::ConversationItem
|
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
module Phronomy
|
|
2
2
|
module MultiAgent
|
|
3
3
|
class SharedState
|
|
4
|
+
interface _SharingTools
|
|
5
|
+
def call: (member: singleton(::Phronomy::Agent::Base), cycle: Integer) -> Hash[singleton(::Phronomy::Tool::Base), untyped]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
interface _SharingProvider
|
|
9
|
+
def open: (input: untyped, config: Hash[Symbol, untyped]) { (Binding) -> untyped } -> untyped
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Binding
|
|
13
|
+
def self.new: (store: untyped, tools: _SharingTools) -> instance
|
|
14
|
+
attr_reader store: untyped
|
|
15
|
+
def tool_definitions: (member: singleton(::Phronomy::Agent::Base), cycle: Integer) -> Hash[singleton(::Phronomy::Tool::Base), String?]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class MemberLifecycleError < ::Phronomy::Error
|
|
19
|
+
attr_reader agent: ::Phronomy::Agent::Base
|
|
20
|
+
attr_reader result: untyped
|
|
21
|
+
attr_reader reason: Symbol
|
|
22
|
+
attr_reader primary_error: Exception?
|
|
23
|
+
attr_reader release_error: Exception?
|
|
24
|
+
def self.new: (agent: ::Phronomy::Agent::Base, result: untyped, reason: Symbol, ?primary_error: Exception?, ?release_error: Exception?) -> instance
|
|
25
|
+
end
|
|
26
|
+
|
|
4
27
|
class KnowledgeStore
|
|
5
28
|
def self.new: () -> instance
|
|
6
29
|
def read_all: () -> Array[Hash[Symbol, untyped]]
|
|
@@ -13,8 +36,9 @@ module Phronomy
|
|
|
13
36
|
def self.coordination: (?untyped text) -> untyped
|
|
14
37
|
def self.max_cycles: (?untyped value) -> Integer?
|
|
15
38
|
def self.timeout: (?untyped value) -> Float?
|
|
16
|
-
def self.terminate_when: () ?{ (
|
|
17
|
-
def self.aggregate: () ?{ (
|
|
39
|
+
def self.terminate_when: () ?{ (untyped) -> untyped } -> untyped
|
|
40
|
+
def self.aggregate: () ?{ (untyped) -> untyped } -> untyped
|
|
41
|
+
def self.sharing: (_SharingProvider provider, id: String, version: Integer) -> Hash[Symbol, untyped]
|
|
18
42
|
def invoke: (untyped input, ?config: Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
|
|
19
43
|
end
|
|
20
44
|
|
data/sig/phronomy/tool.rbs
CHANGED
|
@@ -55,6 +55,7 @@ module Phronomy
|
|
|
55
55
|
def requires_approval?: () -> untyped
|
|
56
56
|
def tool_origin: () -> Symbol
|
|
57
57
|
def approval_metadata: () -> Hash[untyped, untyped]
|
|
58
|
+
def approval_display: (arguments: Hash[String | Symbol, untyped], facts: Hash[untyped, untyped]) -> { arguments: Hash[String | Symbol, untyped], facts: Hash[untyped, untyped] }
|
|
58
59
|
def execute: (**untyped args) -> untyped
|
|
59
60
|
|
|
60
61
|
private
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: phronomy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.33.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Raizo T.C.S
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-11-
|
|
11
|
+
date: 2026-11-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ruby_llm
|
|
@@ -257,6 +257,7 @@ files:
|
|
|
257
257
|
- docs/archive/design/historical/06_design_decisions.md
|
|
258
258
|
- docs/async-composition.md
|
|
259
259
|
- docs/changelog/0.14-and-earlier.md
|
|
260
|
+
- docs/context-policy-budgeting.md
|
|
260
261
|
- docs/decisions/001-rubyllm-as-provider-layer.md
|
|
261
262
|
- docs/decisions/002-workflow-context-immutability.md
|
|
262
263
|
- docs/decisions/003-event-loop-singleton.md
|
|
@@ -333,6 +334,7 @@ files:
|
|
|
333
334
|
- docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md
|
|
334
335
|
- docs/design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md
|
|
335
336
|
- docs/design/durable-semantic-coordination/RESPONSIBILITY_BOUNDARY_REVIEW.md
|
|
337
|
+
- docs/examples/shared_state_custom_sharing.rb
|
|
336
338
|
- docs/execution-boundaries-preview.md
|
|
337
339
|
- docs/failure-reason-codes.md
|
|
338
340
|
- docs/features.md
|
|
@@ -376,6 +378,7 @@ files:
|
|
|
376
378
|
- docs/ordered-event-delivery.md
|
|
377
379
|
- docs/persistence-backends.md
|
|
378
380
|
- docs/runtime-and-concurrency.md
|
|
381
|
+
- docs/shared-state-sharing.md
|
|
379
382
|
- docs/workflow-completion.md
|
|
380
383
|
- examples/README.md
|
|
381
384
|
- gemfiles/http_minimum.gemfile
|
|
@@ -616,6 +619,8 @@ files:
|
|
|
616
619
|
- lib/phronomy/multi_agent/reserved_child_admission.rb
|
|
617
620
|
- lib/phronomy/multi_agent/runtime_binding/engine_environment.rb
|
|
618
621
|
- lib/phronomy/multi_agent/shared_state.rb
|
|
622
|
+
- lib/phronomy/multi_agent/shared_state/binding.rb
|
|
623
|
+
- lib/phronomy/multi_agent/shared_state/member_lifecycle_error.rb
|
|
619
624
|
- lib/phronomy/multi_agent/store.rb
|
|
620
625
|
- lib/phronomy/multi_agent/team_busy_error.rb
|
|
621
626
|
- lib/phronomy/multi_agent/team_coordinator.rb
|