phronomy 0.22.0 → 0.23.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/.mutant.yml +3 -4
- data/CHANGELOG.md +200 -10
- data/CONTRIBUTING.md +81 -9
- data/README.md +15 -6
- data/VERIFY.sh +587 -0
- data/benchmark/bench_agent_invoke.rb +2 -2
- data/benchmark/bench_context_assembler.rb +39 -68
- data/benchmark/bench_regression.rb +2 -2
- data/docs/architecture/agent-context.md +174 -0
- data/docs/architecture/before-llm-input.md +78 -0
- data/docs/architecture/context-management.md +232 -0
- data/docs/architecture/knowledge-and-rag.md +130 -0
- data/docs/architecture/multi-agent-handoff.md +152 -0
- data/docs/architecture/persistence.md +175 -0
- data/docs/architecture/removed/agent-context.md +72 -0
- data/docs/architecture/security-boundaries.md +173 -0
- data/docs/architecture/tracing.md +194 -0
- data/docs/architecture.md +82 -0
- data/docs/archive/design/archived/04_api_design.md +507 -0
- data/docs/archive/design/archived/09_guardrails.md +186 -0
- data/docs/archive/design/archived/17_rails_integration.md +175 -0
- data/docs/archive/design/historical/00_design_philosophy.md +122 -0
- data/docs/archive/design/historical/01_rubyllm_evaluation.md +178 -0
- data/docs/archive/design/historical/06_design_decisions.md +143 -0
- data/docs/changelog/0.14-and-earlier.md +1 -1
- data/docs/decisions/001-rubyllm-as-provider-layer.md +6 -1
- data/docs/decisions/002-workflow-context-immutability.md +26 -1
- data/docs/decisions/006-no-built-in-guardrails.md +2 -1
- data/docs/decisions/012-canonical-execution-log-and-context-policy.md +120 -38
- data/docs/decisions/014-unified-persistence-durable-state.md +9 -2
- data/docs/decisions/016-semantic-multi-agent-handoff.md +112 -0
- data/docs/decisions/017-design-authority-and-adr-governance.md +200 -0
- data/docs/decisions/018-durability-guarantees-and-failure-model.md +488 -0
- data/docs/decisions/019-filter-contract-and-security-boundaries.md +229 -0
- data/docs/decisions/020-canonical-workflow-instance-identity.md +177 -0
- data/docs/decisions/021-generic-agent-invocation-identity-removal.md +119 -0
- data/docs/decisions/022-agent-execution-parent-identity-and-runtime-routing-boundary.md +193 -0
- data/docs/decisions/023-fsm-session-incarnation-identity-and-routing.md +139 -0
- data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +188 -0
- data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +249 -0
- data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +257 -0
- data/docs/decisions/027-llm-adapter-provider-boundary.md +93 -0
- data/docs/decisions/README.md +172 -0
- data/docs/features.md +31 -11
- data/docs/getting-started.md +77 -45
- data/docs/migrations/0.19.md +14 -7
- data/docs/migrations/0.22.md +390 -0
- data/docs/persistence-backends.md +88 -38
- data/docs/runtime-and-concurrency.md +227 -33
- data/examples/README.md +13 -0
- data/lib/phronomy/agent/agent_execution.rb +19 -15
- data/lib/phronomy/agent/agent_invocation.rb +288 -93
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +236 -202
- data/lib/phronomy/agent/agent_root.rb +3 -3
- data/lib/phronomy/agent/approval_evaluation_request.rb +37 -19
- data/lib/phronomy/agent/async_event_api.rb +145 -72
- data/lib/phronomy/agent/base.rb +388 -181
- data/lib/phronomy/agent/concerns/before_llm_input.rb +1 -1
- data/lib/phronomy/agent/context_assembler.rb +437 -178
- data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
- data/lib/phronomy/agent/context_plan.rb +18 -13
- data/lib/phronomy/agent/context_plan_validator.rb +246 -88
- data/lib/phronomy/agent/context_policies/default.rb +123 -34
- data/lib/phronomy/agent/context_policy.rb +109 -3
- data/lib/phronomy/agent/context_policy_input.rb +244 -0
- data/lib/phronomy/agent/context_policy_input_builder.rb +241 -0
- data/lib/phronomy/agent/execution_coordinator.rb +1975 -587
- data/lib/phronomy/agent/journal_record.rb +17 -4
- data/lib/phronomy/agent/llm_input_build_context.rb +1 -1
- data/lib/phronomy/agent/llm_input_manifest.rb +277 -2
- data/lib/phronomy/agent/llm_operation_result.rb +12 -7
- data/lib/phronomy/agent/phase_machine_builder.rb +19 -7
- data/lib/phronomy/agent/provider_call_outcome.rb +23 -7
- data/lib/phronomy/agent/recovery_coordinator/continuation.rb +271 -0
- data/lib/phronomy/agent/recovery_coordinator/installation.rb +427 -0
- data/lib/phronomy/agent/recovery_coordinator/resolution.rb +635 -0
- data/lib/phronomy/agent/recovery_coordinator.rb +211 -0
- data/lib/phronomy/agent/recovery_support.rb +512 -0
- data/lib/phronomy/agent/ruby_llm_materializer.rb +22 -13
- data/lib/phronomy/agent/selection/candidate.rb +53 -0
- data/lib/phronomy/agent/selection/constraint.rb +49 -0
- data/lib/phronomy/agent/shared_state.rb +38 -1
- data/lib/phronomy/agent/tool_approval_request.rb +33 -5
- data/lib/phronomy/agent/tool_definition_set.rb +49 -3
- data/lib/phronomy/agent/tool_invocation.rb +336 -102
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +49 -45
- data/lib/phronomy/agent.rb +20 -2
- data/lib/phronomy/agent_already_exists_error.rb +5 -0
- data/lib/phronomy/agent_purged_error.rb +5 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +17 -3
- data/lib/phronomy/engine/concurrency/physical_completion_task.rb +135 -0
- data/lib/phronomy/engine/event_loop.rb +622 -63
- data/lib/phronomy/engine/fsm_session.rb +194 -21
- data/lib/phronomy/engine/runtime/agent_ownership_registry.rb +352 -0
- data/lib/phronomy/engine/runtime.rb +77 -20
- data/lib/phronomy/generator_verifier.rb +12 -14
- data/lib/phronomy/invocation_context.rb +9 -29
- data/lib/phronomy/multi_agent/admission_registry.rb +51 -0
- data/lib/phronomy/multi_agent/coordination_state.rb +18 -0
- data/lib/phronomy/multi_agent/coordinator.rb +154 -0
- data/lib/phronomy/multi_agent/execution_coordinator.rb +116 -0
- data/lib/phronomy/multi_agent/fan_out_invocation.rb +24 -33
- data/lib/phronomy/multi_agent/fan_out_session_builder.rb +12 -19
- data/lib/phronomy/multi_agent/handoff.rb +24 -45
- data/lib/phronomy/multi_agent/handoff_capability_factory.rb +87 -0
- data/lib/phronomy/multi_agent/handoff_context.rb +95 -0
- data/lib/phronomy/multi_agent/handoff_policy.rb +137 -0
- data/lib/phronomy/multi_agent/handoff_projection.rb +191 -0
- data/lib/phronomy/multi_agent/handoff_request.rb +45 -0
- data/lib/phronomy/multi_agent/orchestrator.rb +12 -15
- data/lib/phronomy/multi_agent/runner.rb +98 -0
- data/lib/phronomy/persistence/durable_codec.rb +646 -0
- data/lib/phronomy/persistence/durable_record.rb +117 -0
- data/lib/phronomy/persistence/in_memory.rb +210 -134
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +226 -0
- data/lib/phronomy/persistence/repository_facades.rb +316 -0
- data/lib/phronomy/persistence.rb +81 -41
- data/lib/phronomy/recovery.rb +186 -0
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +19 -19
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +3 -3
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
- data/lib/phronomy/tracing/automatic.rb +176 -0
- data/lib/phronomy/tracing/base.rb +11 -2
- data/lib/phronomy/tracing/langfuse_tracer.rb +20 -12
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow.rb +3 -6
- data/lib/phronomy/workflow_context.rb +14 -5
- data/lib/phronomy/workflow_recovery.rb +123 -0
- data/lib/phronomy/workflow_runner.rb +468 -256
- data/lib/phronomy.rb +6 -0
- data/scripts/api_snapshot.rb +12 -0
- data/sig/phronomy/agent.rbs +209 -7
- data/sig/phronomy/multi_agent.rbs +39 -0
- data/sig/phronomy/persistence.rbs +62 -4
- data/sig/phronomy/runtime.rbs +1 -4
- data/sig/phronomy/workflow.rbs +2 -2
- data/sig/phronomy.rbs +10 -0
- metadata +65 -17
- data/examples/workflows/agent_event_mapping.rb +0 -101
- data/examples/workflows/generic_task_event_mapping.rb +0 -66
- data/lib/phronomy/agent/activation_registry.rb +0 -28
- data/lib/phronomy/agent/agent_execution_activation.rb +0 -172
- data/lib/phronomy/agent/context_candidate.rb +0 -47
- data/lib/phronomy/agent/context_parts/budget/token_budget_packer.rb +0 -53
- data/lib/phronomy/agent/context_parts/requirements/required_context_resolver.rb +0 -56
- data/lib/phronomy/agent/context_parts/selectors/recent_first_selector.rb +0 -30
- data/lib/phronomy/agent/context_parts/unit_builders/dependency_aware_unit_builder.rb +0 -118
- data/lib/phronomy/agent/context_policy_descriptor.rb +0 -49
- data/lib/phronomy/agent/context_policy_registry.rb +0 -46
- data/lib/phronomy/agent/context_request.rb +0 -35
- data/lib/phronomy/agent/context_selection_unit.rb +0 -38
- data/lib/phronomy/agent/derived_content_spec.rb +0 -34
- data/lib/phronomy/agent/runner.rb +0 -97
|
@@ -4,6 +4,15 @@ module Phronomy
|
|
|
4
4
|
module Agent
|
|
5
5
|
# Implements peer coordination through a shared KnowledgeStore.
|
|
6
6
|
class SharedState
|
|
7
|
+
# Semantic revision of the framework-owned SharedState instrumentation
|
|
8
|
+
# applied on top of one specific researcher definition revision.
|
|
9
|
+
#
|
|
10
|
+
# The generated definition ID below already includes the wrapped
|
|
11
|
+
# researcher's definition ID/version. Increment this value only when the
|
|
12
|
+
# semantics of SharedState's injected coordination capabilities change.
|
|
13
|
+
INSTRUMENTATION_DEFINITION_VERSION = 1
|
|
14
|
+
private_constant :INSTRUMENTATION_DEFINITION_VERSION
|
|
15
|
+
|
|
7
16
|
class KnowledgeStore
|
|
8
17
|
def initialize
|
|
9
18
|
@findings = []
|
|
@@ -168,7 +177,35 @@ module Phronomy
|
|
|
168
177
|
definitions[read_tool] = nil
|
|
169
178
|
definitions[write_tool] = nil
|
|
170
179
|
|
|
171
|
-
|
|
180
|
+
# The anonymous subclass has a different effective Agent definition from the
|
|
181
|
+
# configured researcher because SharedState adds LLM-visible coordination
|
|
182
|
+
# Tools. D04 therefore requires a distinct semantic definition identity
|
|
183
|
+
# instead of reusing the researcher's exact definition revision.
|
|
184
|
+
#
|
|
185
|
+
# This is a framework-private generated lineage. It is derived from the
|
|
186
|
+
# wrapped researcher's *definition revision*, while its own version tracks the
|
|
187
|
+
# semantic revision of the SharedState instrumentation itself. This keeps the
|
|
188
|
+
# application's definition-version namespace independent from framework
|
|
189
|
+
# instrumentation.
|
|
190
|
+
instrumented_def = instrumented_definition_for(researcher_class)
|
|
191
|
+
Class.new(researcher_class) do
|
|
192
|
+
agent_definition(
|
|
193
|
+
id: instrumented_def.fetch(:id),
|
|
194
|
+
version: instrumented_def.fetch(:version)
|
|
195
|
+
)
|
|
196
|
+
tools(definitions)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def instrumented_definition_for(researcher_class)
|
|
201
|
+
parent_def = researcher_class.agent_definition
|
|
202
|
+
parent_id = parent_def.fetch(:id)
|
|
203
|
+
parent_version = parent_def.fetch(:version)
|
|
204
|
+
|
|
205
|
+
{
|
|
206
|
+
id: "Phronomy::Agent::SharedState::Instrumented/#{parent_id}@#{parent_version}".freeze,
|
|
207
|
+
version: INSTRUMENTATION_DEFINITION_VERSION
|
|
208
|
+
}.freeze
|
|
172
209
|
end
|
|
173
210
|
|
|
174
211
|
def build_prompt(
|
|
@@ -45,6 +45,20 @@ module Phronomy
|
|
|
45
45
|
freeze
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def self.from_h(hash)
|
|
49
|
+
values = hash.to_h { |key, value| [key.to_s, value] }
|
|
50
|
+
new(
|
|
51
|
+
tool_invocation_id: values.fetch("tool_invocation_id"),
|
|
52
|
+
tool_call_id: values["tool_call_id"],
|
|
53
|
+
tool_name: values.fetch("tool_name"),
|
|
54
|
+
arguments: values.fetch("arguments", {}),
|
|
55
|
+
facts: values.fetch("facts", {}),
|
|
56
|
+
reason: values["reason"],
|
|
57
|
+
origin: values.fetch("origin", "local"),
|
|
58
|
+
metadata: values.fetch("metadata", {})
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
48
62
|
def to_h
|
|
49
63
|
{
|
|
50
64
|
tool_invocation_id: @tool_invocation_id,
|
|
@@ -74,12 +88,23 @@ module Phronomy
|
|
|
74
88
|
end
|
|
75
89
|
end
|
|
76
90
|
|
|
77
|
-
attr_reader :id, :
|
|
91
|
+
attr_reader :id, :execution_id, :items, :created_at
|
|
92
|
+
|
|
93
|
+
def self.from_h(hash)
|
|
94
|
+
source = hash.to_h { |key, value| [key.to_s, value] }
|
|
95
|
+
items = Array(source.fetch("items")).map { |item| Item.from_h(item) }
|
|
96
|
+
new(
|
|
97
|
+
execution_id: source.fetch("execution_id"),
|
|
98
|
+
items: items,
|
|
99
|
+
id: source.fetch("id"),
|
|
100
|
+
created_at: Time.iso8601(source.fetch("created_at"))
|
|
101
|
+
)
|
|
102
|
+
end
|
|
78
103
|
|
|
79
104
|
def self.build(agent_invocation)
|
|
80
105
|
pending = agent_invocation.tool_invocations.select(&:awaiting_approval?)
|
|
81
106
|
new(
|
|
82
|
-
|
|
107
|
+
execution_id: agent_invocation.execution_id,
|
|
83
108
|
items: pending.map { |invocation| build_item(invocation) }
|
|
84
109
|
)
|
|
85
110
|
end
|
|
@@ -98,11 +123,14 @@ module Phronomy
|
|
|
98
123
|
end
|
|
99
124
|
private_class_method :build_item
|
|
100
125
|
|
|
101
|
-
def initialize(
|
|
126
|
+
def initialize(execution_id:, items:, id: SecureRandom.uuid, created_at: Time.now.utc)
|
|
102
127
|
raise ArgumentError, "ToolApprovalRequest requires at least one item" if items.empty?
|
|
128
|
+
if execution_id.nil? || execution_id.to_s.empty?
|
|
129
|
+
raise ArgumentError, "ToolApprovalRequest requires execution_id"
|
|
130
|
+
end
|
|
103
131
|
|
|
104
132
|
@id = id.to_s.freeze
|
|
105
|
-
@
|
|
133
|
+
@execution_id = execution_id.to_s.freeze
|
|
106
134
|
@items = items.dup.freeze
|
|
107
135
|
@created_at = created_at
|
|
108
136
|
freeze
|
|
@@ -111,7 +139,7 @@ module Phronomy
|
|
|
111
139
|
def to_h
|
|
112
140
|
{
|
|
113
141
|
id: @id,
|
|
114
|
-
|
|
142
|
+
execution_id: @execution_id,
|
|
115
143
|
items: @items.map(&:to_h),
|
|
116
144
|
created_at: @created_at.iso8601
|
|
117
145
|
}
|
|
@@ -5,8 +5,8 @@ module Phronomy
|
|
|
5
5
|
class ToolDefinitionSet
|
|
6
6
|
attr_reader :runtime_tools, :definitions
|
|
7
7
|
|
|
8
|
-
def self.build(agent)
|
|
9
|
-
runtime_tools = (agent.class.tools +
|
|
8
|
+
def self.build(agent, additional_tools: [])
|
|
9
|
+
runtime_tools = (agent.class.tools + Array(additional_tools)).freeze
|
|
10
10
|
definitions = runtime_tools.map do |tool_class|
|
|
11
11
|
prepared = agent.send(:prepare_tool_class, tool_class)
|
|
12
12
|
tool = prepared.is_a?(Class) ? prepared.new : prepared
|
|
@@ -46,10 +46,56 @@ module Phronomy
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def initialize(runtime_tools:, definitions:)
|
|
49
|
-
@runtime_tools = runtime_tools
|
|
49
|
+
@runtime_tools = Array(runtime_tools).freeze
|
|
50
50
|
@definitions = Immutable.copy(definitions)
|
|
51
|
+
validate_unique_names!
|
|
51
52
|
freeze
|
|
52
53
|
end
|
|
54
|
+
|
|
55
|
+
def select_definitions(expected_definitions)
|
|
56
|
+
expected = Immutable.copy(Array(expected_definitions))
|
|
57
|
+
current_by_name = definitions.each_with_index.to_h do |definition, index|
|
|
58
|
+
[definition.fetch("name"), [definition, runtime_tools.fetch(index)]]
|
|
59
|
+
end
|
|
60
|
+
seen = {}
|
|
61
|
+
selected_tools = []
|
|
62
|
+
selected_definitions = []
|
|
63
|
+
|
|
64
|
+
expected.each do |definition|
|
|
65
|
+
name = definition.fetch("name").to_s
|
|
66
|
+
raise ArgumentError, "duplicate selected Tool definition: #{name}" if seen[name]
|
|
67
|
+
seen[name] = true
|
|
68
|
+
|
|
69
|
+
current_definition, runtime_tool = current_by_name.fetch(name) do
|
|
70
|
+
raise Phronomy::ConfigurationError,
|
|
71
|
+
"ContextPolicy selected Tool not present in current Agent configuration: #{name}"
|
|
72
|
+
end
|
|
73
|
+
unless Phronomy::CanonicalJSON.dump(current_definition) ==
|
|
74
|
+
Phronomy::CanonicalJSON.dump(definition)
|
|
75
|
+
raise Phronomy::ConfigurationError,
|
|
76
|
+
"Agent Tool definition changed after ContextPolicy selection: #{name}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
selected_tools << runtime_tool
|
|
80
|
+
selected_definitions << current_definition
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
self.class.new(
|
|
84
|
+
runtime_tools: selected_tools,
|
|
85
|
+
definitions: selected_definitions
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
def validate_unique_names!
|
|
92
|
+
names = definitions.map { |definition| definition.fetch("name").to_s }
|
|
93
|
+
duplicates = names.group_by(&:itself).select { |_name, values| values.length > 1 }.keys
|
|
94
|
+
return if duplicates.empty?
|
|
95
|
+
|
|
96
|
+
raise Phronomy::ConfigurationError,
|
|
97
|
+
"duplicate effective Tool definition name(s): #{duplicates.inspect}"
|
|
98
|
+
end
|
|
53
99
|
end
|
|
54
100
|
end
|
|
55
101
|
end
|