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.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +3 -4
  3. data/CHANGELOG.md +200 -10
  4. data/CONTRIBUTING.md +81 -9
  5. data/README.md +15 -6
  6. data/VERIFY.sh +587 -0
  7. data/benchmark/bench_agent_invoke.rb +2 -2
  8. data/benchmark/bench_context_assembler.rb +39 -68
  9. data/benchmark/bench_regression.rb +2 -2
  10. data/docs/architecture/agent-context.md +174 -0
  11. data/docs/architecture/before-llm-input.md +78 -0
  12. data/docs/architecture/context-management.md +232 -0
  13. data/docs/architecture/knowledge-and-rag.md +130 -0
  14. data/docs/architecture/multi-agent-handoff.md +152 -0
  15. data/docs/architecture/persistence.md +175 -0
  16. data/docs/architecture/removed/agent-context.md +72 -0
  17. data/docs/architecture/security-boundaries.md +173 -0
  18. data/docs/architecture/tracing.md +194 -0
  19. data/docs/architecture.md +82 -0
  20. data/docs/archive/design/archived/04_api_design.md +507 -0
  21. data/docs/archive/design/archived/09_guardrails.md +186 -0
  22. data/docs/archive/design/archived/17_rails_integration.md +175 -0
  23. data/docs/archive/design/historical/00_design_philosophy.md +122 -0
  24. data/docs/archive/design/historical/01_rubyllm_evaluation.md +178 -0
  25. data/docs/archive/design/historical/06_design_decisions.md +143 -0
  26. data/docs/changelog/0.14-and-earlier.md +1 -1
  27. data/docs/decisions/001-rubyllm-as-provider-layer.md +6 -1
  28. data/docs/decisions/002-workflow-context-immutability.md +26 -1
  29. data/docs/decisions/006-no-built-in-guardrails.md +2 -1
  30. data/docs/decisions/012-canonical-execution-log-and-context-policy.md +120 -38
  31. data/docs/decisions/014-unified-persistence-durable-state.md +9 -2
  32. data/docs/decisions/016-semantic-multi-agent-handoff.md +112 -0
  33. data/docs/decisions/017-design-authority-and-adr-governance.md +200 -0
  34. data/docs/decisions/018-durability-guarantees-and-failure-model.md +488 -0
  35. data/docs/decisions/019-filter-contract-and-security-boundaries.md +229 -0
  36. data/docs/decisions/020-canonical-workflow-instance-identity.md +177 -0
  37. data/docs/decisions/021-generic-agent-invocation-identity-removal.md +119 -0
  38. data/docs/decisions/022-agent-execution-parent-identity-and-runtime-routing-boundary.md +193 -0
  39. data/docs/decisions/023-fsm-session-incarnation-identity-and-routing.md +139 -0
  40. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +188 -0
  41. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +249 -0
  42. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +257 -0
  43. data/docs/decisions/027-llm-adapter-provider-boundary.md +93 -0
  44. data/docs/decisions/README.md +172 -0
  45. data/docs/features.md +31 -11
  46. data/docs/getting-started.md +77 -45
  47. data/docs/migrations/0.19.md +14 -7
  48. data/docs/migrations/0.22.md +390 -0
  49. data/docs/persistence-backends.md +88 -38
  50. data/docs/runtime-and-concurrency.md +227 -33
  51. data/examples/README.md +13 -0
  52. data/lib/phronomy/agent/agent_execution.rb +19 -15
  53. data/lib/phronomy/agent/agent_invocation.rb +288 -93
  54. data/lib/phronomy/agent/agent_invocation_session_builder.rb +236 -202
  55. data/lib/phronomy/agent/agent_root.rb +3 -3
  56. data/lib/phronomy/agent/approval_evaluation_request.rb +37 -19
  57. data/lib/phronomy/agent/async_event_api.rb +145 -72
  58. data/lib/phronomy/agent/base.rb +388 -181
  59. data/lib/phronomy/agent/concerns/before_llm_input.rb +1 -1
  60. data/lib/phronomy/agent/context_assembler.rb +437 -178
  61. data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
  62. data/lib/phronomy/agent/context_plan.rb +18 -13
  63. data/lib/phronomy/agent/context_plan_validator.rb +246 -88
  64. data/lib/phronomy/agent/context_policies/default.rb +123 -34
  65. data/lib/phronomy/agent/context_policy.rb +109 -3
  66. data/lib/phronomy/agent/context_policy_input.rb +244 -0
  67. data/lib/phronomy/agent/context_policy_input_builder.rb +241 -0
  68. data/lib/phronomy/agent/execution_coordinator.rb +1975 -587
  69. data/lib/phronomy/agent/journal_record.rb +17 -4
  70. data/lib/phronomy/agent/llm_input_build_context.rb +1 -1
  71. data/lib/phronomy/agent/llm_input_manifest.rb +277 -2
  72. data/lib/phronomy/agent/llm_operation_result.rb +12 -7
  73. data/lib/phronomy/agent/phase_machine_builder.rb +19 -7
  74. data/lib/phronomy/agent/provider_call_outcome.rb +23 -7
  75. data/lib/phronomy/agent/recovery_coordinator/continuation.rb +271 -0
  76. data/lib/phronomy/agent/recovery_coordinator/installation.rb +427 -0
  77. data/lib/phronomy/agent/recovery_coordinator/resolution.rb +635 -0
  78. data/lib/phronomy/agent/recovery_coordinator.rb +211 -0
  79. data/lib/phronomy/agent/recovery_support.rb +512 -0
  80. data/lib/phronomy/agent/ruby_llm_materializer.rb +22 -13
  81. data/lib/phronomy/agent/selection/candidate.rb +53 -0
  82. data/lib/phronomy/agent/selection/constraint.rb +49 -0
  83. data/lib/phronomy/agent/shared_state.rb +38 -1
  84. data/lib/phronomy/agent/tool_approval_request.rb +33 -5
  85. data/lib/phronomy/agent/tool_definition_set.rb +49 -3
  86. data/lib/phronomy/agent/tool_invocation.rb +336 -102
  87. data/lib/phronomy/agent/tool_invocation_session_builder.rb +49 -45
  88. data/lib/phronomy/agent.rb +20 -2
  89. data/lib/phronomy/agent_already_exists_error.rb +5 -0
  90. data/lib/phronomy/agent_purged_error.rb +5 -0
  91. data/lib/phronomy/engine/concurrency/offload_pool.rb +17 -3
  92. data/lib/phronomy/engine/concurrency/physical_completion_task.rb +135 -0
  93. data/lib/phronomy/engine/event_loop.rb +622 -63
  94. data/lib/phronomy/engine/fsm_session.rb +194 -21
  95. data/lib/phronomy/engine/runtime/agent_ownership_registry.rb +352 -0
  96. data/lib/phronomy/engine/runtime.rb +77 -20
  97. data/lib/phronomy/generator_verifier.rb +12 -14
  98. data/lib/phronomy/invocation_context.rb +9 -29
  99. data/lib/phronomy/multi_agent/admission_registry.rb +51 -0
  100. data/lib/phronomy/multi_agent/coordination_state.rb +18 -0
  101. data/lib/phronomy/multi_agent/coordinator.rb +154 -0
  102. data/lib/phronomy/multi_agent/execution_coordinator.rb +116 -0
  103. data/lib/phronomy/multi_agent/fan_out_invocation.rb +24 -33
  104. data/lib/phronomy/multi_agent/fan_out_session_builder.rb +12 -19
  105. data/lib/phronomy/multi_agent/handoff.rb +24 -45
  106. data/lib/phronomy/multi_agent/handoff_capability_factory.rb +87 -0
  107. data/lib/phronomy/multi_agent/handoff_context.rb +95 -0
  108. data/lib/phronomy/multi_agent/handoff_policy.rb +137 -0
  109. data/lib/phronomy/multi_agent/handoff_projection.rb +191 -0
  110. data/lib/phronomy/multi_agent/handoff_request.rb +45 -0
  111. data/lib/phronomy/multi_agent/orchestrator.rb +12 -15
  112. data/lib/phronomy/multi_agent/runner.rb +98 -0
  113. data/lib/phronomy/persistence/durable_codec.rb +646 -0
  114. data/lib/phronomy/persistence/durable_record.rb +117 -0
  115. data/lib/phronomy/persistence/in_memory.rb +210 -134
  116. data/lib/phronomy/persistence/migration/initial_format_migration.rb +226 -0
  117. data/lib/phronomy/persistence/repository_facades.rb +316 -0
  118. data/lib/phronomy/persistence.rb +81 -41
  119. data/lib/phronomy/recovery.rb +186 -0
  120. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
  121. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +1 -1
  122. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +19 -19
  123. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +3 -3
  124. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
  125. data/lib/phronomy/tracing/automatic.rb +176 -0
  126. data/lib/phronomy/tracing/base.rb +11 -2
  127. data/lib/phronomy/tracing/langfuse_tracer.rb +20 -12
  128. data/lib/phronomy/version.rb +1 -1
  129. data/lib/phronomy/workflow.rb +3 -6
  130. data/lib/phronomy/workflow_context.rb +14 -5
  131. data/lib/phronomy/workflow_recovery.rb +123 -0
  132. data/lib/phronomy/workflow_runner.rb +468 -256
  133. data/lib/phronomy.rb +6 -0
  134. data/scripts/api_snapshot.rb +12 -0
  135. data/sig/phronomy/agent.rbs +209 -7
  136. data/sig/phronomy/multi_agent.rbs +39 -0
  137. data/sig/phronomy/persistence.rbs +62 -4
  138. data/sig/phronomy/runtime.rbs +1 -4
  139. data/sig/phronomy/workflow.rbs +2 -2
  140. data/sig/phronomy.rbs +10 -0
  141. metadata +65 -17
  142. data/examples/workflows/agent_event_mapping.rb +0 -101
  143. data/examples/workflows/generic_task_event_mapping.rb +0 -66
  144. data/lib/phronomy/agent/activation_registry.rb +0 -28
  145. data/lib/phronomy/agent/agent_execution_activation.rb +0 -172
  146. data/lib/phronomy/agent/context_candidate.rb +0 -47
  147. data/lib/phronomy/agent/context_parts/budget/token_budget_packer.rb +0 -53
  148. data/lib/phronomy/agent/context_parts/requirements/required_context_resolver.rb +0 -56
  149. data/lib/phronomy/agent/context_parts/selectors/recent_first_selector.rb +0 -30
  150. data/lib/phronomy/agent/context_parts/unit_builders/dependency_aware_unit_builder.rb +0 -118
  151. data/lib/phronomy/agent/context_policy_descriptor.rb +0 -49
  152. data/lib/phronomy/agent/context_policy_registry.rb +0 -46
  153. data/lib/phronomy/agent/context_request.rb +0 -35
  154. data/lib/phronomy/agent/context_selection_unit.rb +0 -38
  155. data/lib/phronomy/agent/derived_content_spec.rb +0 -34
  156. data/lib/phronomy/agent/runner.rb +0 -97
data/lib/phronomy.rb CHANGED
@@ -113,3 +113,9 @@ module Phronomy
113
113
  end
114
114
  end
115
115
  end
116
+
117
+ # Shared Recovery primitives and Workflow Persistence F1 reconciliation.
118
+ # Entity-specific Agent Recovery is loaded from lib/phronomy/agent.rb when the
119
+ # Agent namespace is materialized.
120
+ require_relative "phronomy/recovery"
121
+ require_relative "phronomy/workflow_recovery"
@@ -19,12 +19,24 @@ require_relative "../lib/phronomy"
19
19
  PUBLIC_API_ENTRIES = [
20
20
  # Stable
21
21
  Phronomy::Agent::Base,
22
+ Phronomy::Agent::ContextPolicy,
23
+ Phronomy::Agent::ContextPolicyInput,
24
+ Phronomy::Agent::ContextPolicyInput::Provenance,
25
+ Phronomy::Agent::ContextPolicyInput::InstructionItem,
26
+ Phronomy::Agent::ContextPolicyInput::KnowledgeItem,
27
+ Phronomy::Agent::ContextPolicyInput::ToolItem,
28
+ Phronomy::Agent::ContextPolicyInput::ConversationItem,
29
+ Phronomy::Agent::ContextPlan,
30
+ Phronomy::InvocationContext,
22
31
  Phronomy::Agent::Context::Capability::Base,
23
32
  Phronomy::Workflow,
24
33
  Phronomy::WorkflowContext,
25
34
  Phronomy::Runnable,
26
35
  Phronomy::Agent::Context::Instruction::PromptTemplate,
27
36
  # Beta
37
+ Phronomy::MultiAgent::Handoff,
38
+ Phronomy::MultiAgent::HandoffPolicy,
39
+ Phronomy::MultiAgent::Runner,
28
40
  Phronomy::MultiAgent::Orchestrator,
29
41
  Phronomy::MultiAgent::TeamCoordinator,
30
42
  Phronomy::Filter::Base,
@@ -1,5 +1,184 @@
1
1
  module Phronomy
2
2
  module Agent
3
+ interface _BeforeLLMInputHook
4
+ def call: (LLMInputBuildContext context) -> LLMInputPatch?
5
+ end
6
+
7
+ class LLMInputPatch
8
+ attr_reader model_config_patch: Hash[untyped, untyped]?
9
+ attr_reader segment_candidates: Array[untyped]?
10
+
11
+ def initialize: (?model_config_patch: Hash[untyped, untyped]?, ?segment_candidates: Array[untyped]?) -> void
12
+ def self.empty: () -> LLMInputPatch
13
+ end
14
+
15
+ class LLMInputBuildContext
16
+ attr_reader agent_id: String
17
+ attr_reader agent_definition_id: String
18
+ attr_reader agent_definition_version: Integer
19
+ attr_reader config: Hash[untyped, untyped]
20
+ attr_reader call_sequence: Integer
21
+
22
+ def initialize: (agent_id: String, agent_definition_id: String, agent_definition_version: Integer, ?config: Hash[untyped, untyped], call_sequence: Integer) -> void
23
+ end
24
+
25
+ class ToolApprovalRequest
26
+ class Item
27
+ attr_reader tool_invocation_id: String
28
+ attr_reader tool_call_id: String?
29
+ attr_reader tool_name: String
30
+ attr_reader arguments: Hash[untyped, untyped]
31
+ attr_reader facts: Hash[untyped, untyped]
32
+ attr_reader reason: String?
33
+ attr_reader origin: Symbol
34
+ attr_reader metadata: Hash[untyped, untyped]
35
+
36
+ def initialize: (tool_invocation_id: String, tool_call_id: String?, tool_name: String, arguments: Hash[untyped, untyped], facts: Hash[untyped, untyped], reason: String?, origin: Symbol, metadata: Hash[untyped, untyped]) -> void
37
+ def self.from_h: (Hash[untyped, untyped]) -> Item
38
+ def to_h: () -> Hash[Symbol, untyped]
39
+ end
40
+
41
+ attr_reader id: String
42
+ attr_reader execution_id: String
43
+ attr_reader items: Array[Item]
44
+ attr_reader created_at: untyped
45
+
46
+ def initialize: (execution_id: String, items: Array[Item], ?id: String, ?created_at: untyped) -> void
47
+ def self.from_h: (Hash[untyped, untyped]) -> ToolApprovalRequest
48
+ def to_h: () -> Hash[Symbol, untyped]
49
+ end
50
+
51
+ class ApprovalEvaluationRequest
52
+ attr_reader agent_id: String
53
+ attr_reader agent_definition_id: String
54
+ attr_reader agent_definition_version: Integer
55
+ attr_reader execution_id: String
56
+ attr_reader tool_name: String
57
+ attr_reader tool_schema: Hash[untyped, untyped]
58
+ attr_reader tool_invocation_id: String
59
+ attr_reader tool_call_id: String?
60
+ attr_reader arguments: Hash[untyped, untyped]
61
+ attr_reader facts: Hash[untyped, untyped]
62
+ attr_reader invocation_context: Hash[untyped, untyped]
63
+ attr_reader origin: Symbol
64
+ attr_reader metadata: Hash[untyped, untyped]
65
+ attr_reader default_decision: Symbol?
66
+
67
+ def initialize: (agent_id: String, agent_definition_id: String, agent_definition_version: Integer, execution_id: String, tool_name: String, tool_schema: Hash[untyped, untyped], tool_invocation_id: String, tool_call_id: String?, arguments: Hash[untyped, untyped], ?facts: Hash[untyped, untyped], ?invocation_context: Hash[untyped, untyped], ?origin: Symbol, ?metadata: Hash[untyped, untyped], ?default_decision: Symbol?) -> void
68
+ end
69
+
70
+ class LLMInputManifest
71
+ VERSION: String
72
+ CALL_MODES: Array[Symbol]
73
+ SEGMENT_DELIVERIES: Array[String]
74
+
75
+ def self.from_h: (Hash[String, untyped] hash) -> LLMInputManifest
76
+ def version: () -> String
77
+ def to_h: () -> Hash[String, untyped]
78
+ end
79
+
80
+ class ContextPolicyInput
81
+ class Provenance
82
+ attr_reader origin: Symbol
83
+ attr_reader content_ref: String?
84
+ attr_reader record_id: String?
85
+ attr_reader agent_id: String?
86
+ attr_reader execution_id: String?
87
+ attr_reader llm_call_id: String?
88
+ def initialize: (origin: Symbol, ?content_ref: String?, ?record_id: String?, ?agent_id: String?, ?execution_id: String?, ?llm_call_id: String?) -> void
89
+ end
90
+
91
+ class InstructionItem
92
+ attr_reader id: String
93
+ attr_reader kind: Symbol
94
+ attr_reader role: Symbol?
95
+ attr_reader content: untyped
96
+ attr_reader content_format: Symbol
97
+ attr_reader estimated_tokens: Integer
98
+ attr_reader required: bool
99
+ attr_reader provenance: Provenance
100
+ attr_reader metadata: Hash[untyped, untyped]
101
+ def initialize: (id: String, kind: Symbol, ?role: Symbol?, content: untyped, ?content_format: Symbol?, ?estimated_tokens: Integer?, ?required: bool?, ?provenance: Provenance?, ?metadata: Hash[untyped, untyped]) -> void
102
+ def required?: () -> bool
103
+ end
104
+
105
+ class KnowledgeItem
106
+ attr_reader id: String
107
+ attr_reader kind: Symbol
108
+ attr_reader role: Symbol?
109
+ attr_reader content: untyped
110
+ attr_reader content_format: Symbol
111
+ attr_reader estimated_tokens: Integer
112
+ attr_reader required: bool
113
+ attr_reader provenance: Provenance
114
+ attr_reader metadata: Hash[untyped, untyped]
115
+ def initialize: (id: String, kind: Symbol, ?role: Symbol?, content: untyped, ?content_format: Symbol?, ?estimated_tokens: Integer?, ?required: bool?, ?provenance: Provenance?, ?metadata: Hash[untyped, untyped]) -> void
116
+ def required?: () -> bool
117
+ end
118
+
119
+ class ToolItem
120
+ attr_reader id: String
121
+ attr_reader definition: Hash[untyped, untyped]
122
+ attr_reader estimated_tokens: Integer
123
+ attr_reader required: bool
124
+ attr_reader provenance: Provenance
125
+ attr_reader metadata: Hash[untyped, untyped]
126
+ def initialize: (id: String, definition: Hash[untyped, untyped], ?estimated_tokens: Integer?, ?required: bool?, ?provenance: Provenance?, ?metadata: Hash[untyped, untyped]) -> void
127
+ def required?: () -> bool
128
+ end
129
+
130
+ class ConversationItem
131
+ attr_reader id: String
132
+ attr_reader kind: Symbol
133
+ attr_reader role: Symbol?
134
+ attr_reader content: untyped
135
+ attr_reader content_format: Symbol
136
+ attr_reader sequence: Integer?
137
+ attr_reader estimated_tokens: Integer
138
+ attr_reader required: bool
139
+ attr_reader provenance: Provenance
140
+ attr_reader tool_call_id: String?
141
+ attr_reader tool_call_ids: Array[String]
142
+ attr_reader delivery: Symbol
143
+ attr_reader metadata: Hash[untyped, untyped]
144
+ def initialize: (id: String, kind: Symbol, ?role: Symbol?, content: untyped, ?content_format: Symbol?, ?sequence: Integer?, ?estimated_tokens: Integer?, ?required: bool?, ?provenance: Provenance?, ?tool_call_id: String?, ?tool_call_ids: Array[String], ?delivery: Symbol, ?metadata: Hash[untyped, untyped]) -> void
145
+ def required?: () -> bool
146
+ def with_required: (bool required) -> ConversationItem
147
+ end
148
+
149
+ attr_reader agent_id: String
150
+ attr_reader execution_id: String?
151
+ attr_reader call_sequence: Integer
152
+ attr_reader call_mode: Symbol
153
+ attr_reader instruction: Array[InstructionItem]
154
+ attr_reader knowledge: Array[KnowledgeItem]
155
+ attr_reader tools: Array[ToolItem]
156
+ attr_reader conversation: Array[Array[ConversationItem]]
157
+ attr_reader token_budget: untyped
158
+ attr_reader model_config: Hash[untyped, untyped]
159
+ attr_reader previous_manifest: LLMInputManifest?
160
+ attr_reader metadata: Hash[untyped, untyped]
161
+ 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
162
+ end
163
+
164
+ class ContextPlan
165
+ attr_reader instruction: Array[ContextPolicyInput::InstructionItem]
166
+ attr_reader knowledge: Array[ContextPolicyInput::KnowledgeItem]
167
+ attr_reader tools: Array[ContextPolicyInput::ToolItem]
168
+ attr_reader conversation: Array[Array[ContextPolicyInput::ConversationItem]]
169
+ attr_reader metadata: Hash[untyped, untyped]
170
+ def initialize: (?instruction: Array[ContextPolicyInput::InstructionItem], ?knowledge: Array[ContextPolicyInput::KnowledgeItem], ?tools: Array[ContextPolicyInput::ToolItem], ?conversation: Array[Array[ContextPolicyInput::ConversationItem]], ?metadata: Hash[untyped, untyped]) -> void
171
+ end
172
+
173
+ class ContextPolicy
174
+ def call: (ContextPolicyInput input) -> ContextPlan
175
+
176
+ def instruction_item: (content: untyped, ?role: Symbol, ?kind: Symbol, ?content_format: Symbol?, ?required: bool, ?metadata: Hash[untyped, untyped]) -> ContextPolicyInput::InstructionItem
177
+ def knowledge_item: (content: untyped, ?role: Symbol, ?kind: Symbol, ?content_format: Symbol?, ?required: bool, ?metadata: Hash[untyped, untyped]) -> ContextPolicyInput::KnowledgeItem
178
+ 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
179
+ def plan: (?instruction: Array[ContextPolicyInput::InstructionItem], ?knowledge: Array[ContextPolicyInput::KnowledgeItem], ?tools: Array[ContextPolicyInput::ToolItem], ?conversation: Array[Array[ContextPolicyInput::ConversationItem]], ?metadata: Hash[untyped, untyped]) -> ContextPlan
180
+ end
181
+
3
182
  class Base
4
183
  def self.model: (?String name) -> String?
5
184
  def self.instructions: (?untyped text) { (untyped) -> untyped } -> untyped
@@ -12,18 +191,41 @@ module Phronomy
12
191
  def self.cache_instructions: (?bool enabled) -> bool?
13
192
  def self.max_output_tokens: (?Integer value) -> Integer?
14
193
  def self.context_window: (?Integer value) -> Integer?
15
- def self.agent_definition: (?id: String?, ?version: Integer?) -> Hash[Symbol, untyped]
16
- def self.create: (?agent_id: String, ?context: untyped, ?knowledge: Array[untyped], ?persistence: Persistence?, ?metadata: Hash[untyped, untyped]) -> instance
17
- def self.load: (String agent_id, persistence: Persistence) -> instance
194
+ def self.context_policy: () -> ContextPolicy
195
+ | (ContextPolicy policy) -> ContextPolicy
196
+ def self.agent_definition: () -> Hash[Symbol, untyped]
197
+ | (version: Integer, ?id: String?) -> Hash[Symbol, untyped]
198
+ def self.before_llm_input: (?_BeforeLLMInputHook callable) ?{ (LLMInputBuildContext context) -> LLMInputPatch? } -> _BeforeLLMInputHook?
199
+
200
+ # on_event is bound once to the live Agent Runtime incarnation. The block
201
+ # is an alternative spelling of on_event; supplying both is an error.
202
+ def self.create: (?agent_id: String, ?context: untyped, ?knowledge: Array[untyped], ?persistence: Persistence?, ?metadata: Hash[untyped, untyped], ?on_event: ^(untyped) -> void) ?{ (untyped event) -> void } -> instance
203
+ def self.load: (String agent_id, persistence: Persistence, ?on_event: ^(untyped) -> void) ?{ (untyped event) -> void } -> instance
204
+ def self.get: (String agent_id) -> instance?
18
205
  def self.live_for_execution: (String execution_id) -> instance
19
206
 
207
+ def initialize: (?agent_id: String?, ?context: untyped, ?knowledge: Array[untyped], ?persistence: Persistence?, ?metadata: Hash[untyped, untyped], ?on_event: ^(untyped) -> void) ?{ (untyped event) -> void } -> void
208
+
20
209
  attr_reader agent_id: String
21
210
  attr_reader persistence: Persistence
211
+ attr_accessor before_llm_input: _BeforeLLMInputHook?
212
+
213
+ # Public operation methods no longer accept per-invocation event listeners
214
+ # or event-listener blocks.
215
+ def invoke: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> Hash[Symbol, untyped]
216
+ def invoke_async: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> Task[Hash[Symbol, untyped]]
217
+ def stream: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> Hash[Symbol, untyped]
218
+ def stream_async: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> Task[Hash[Symbol, untyped]]
219
+
220
+ def approve: (String execution_id, approval_request_id: String, ?approved: bool, ?config: Hash[untyped, untyped]) -> untyped
221
+ def approve_async: (String execution_id, approval_request_id: String, ?approved: bool, ?config: Hash[untyped, untyped]) -> Task[untyped]
222
+
223
+ # Resolves a factual Recovery ambiguity. result: is meaningful for
224
+ # :succeeded; error: is required for :failed; neither is used by
225
+ # :not_performed.
226
+ def resolve: (String execution_id, expected_execution_revision: Integer, subject: Hash[Symbol, untyped], outcome: Symbol, ?result: untyped, ?error: Exception) -> untyped
227
+ def resolve_async: (String execution_id, expected_execution_revision: Integer, subject: Hash[Symbol, untyped], outcome: Symbol, ?result: untyped, ?error: Exception) -> Task[untyped]
22
228
 
23
- def invoke: (untyped input, **untyped) -> Hash[Symbol, untyped]
24
- def invoke_async: (untyped input, **untyped) -> Task[Hash[Symbol, untyped]]
25
- def stream: (untyped input, **untyped) { (untyped event) -> void } -> Hash[Symbol, untyped]
26
- def stream_async: (untyped input, **untyped) -> Task[Hash[Symbol, untyped]]
27
229
  def transcript: () -> Array[untyped]
28
230
  def add_knowledge: (untyped content, ?metadata: Hash[untyped, untyped]) -> self
29
231
  def clear_transcript!: () -> untyped
@@ -0,0 +1,39 @@
1
+ module Phronomy
2
+ module MultiAgent
3
+ class Handoff
4
+ attr_reader source_agent: ::Phronomy::Agent::Base
5
+ attr_reader target_agent: ::Phronomy::Agent::Base
6
+ attr_reader policy: HandoffPolicy
7
+ attr_reader description: String
8
+
9
+ def initialize: (source_agent: ::Phronomy::Agent::Base, target_agent: ::Phronomy::Agent::Base, ?policy: HandoffPolicy, ?description: String?) -> void
10
+ end
11
+
12
+ class HandoffPolicy
13
+ CATEGORIES: Array[Symbol]
14
+ MODES: Array[Symbol]
15
+ DEFAULTS: Array[Symbol]
16
+
17
+ def self.define: () { () -> void } -> HandoffPolicy
18
+ def self.default: () -> HandoffPolicy
19
+
20
+ def rule: (Symbol | String) -> untyped
21
+ def required?: (Symbol | String) -> bool
22
+ def forbidden?: (Symbol | String) -> bool
23
+ def selectable?: (Symbol | String) -> bool
24
+ def default_include?: (Symbol | String) -> bool
25
+ def selectable_categories: () -> Array[Symbol]
26
+ def to_h: () -> Hash[String, Hash[String, String]]
27
+ end
28
+
29
+ class Runner
30
+ MAX_HANDOFFS: Integer
31
+
32
+ attr_reader main_agent: ::Phronomy::Agent::Base
33
+ attr_reader handoffs: Array[Handoff]
34
+
35
+ def initialize: (main_agent: ::Phronomy::Agent::Base, ?handoffs: Array[Handoff]) -> void
36
+ def invoke: (untyped input, ?config: Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
37
+ end
38
+ end
39
+ end
@@ -5,6 +5,7 @@ module Phronomy
5
5
  def exist?: (String content_id) -> bool
6
6
  end
7
7
 
8
+ # Runtime/domain-facing repository interfaces.
8
9
  interface _AgentRepository
9
10
  def create: (untyped root) -> untyped
10
11
  def load: (String agent_id) -> untyped
@@ -30,9 +31,46 @@ module Phronomy
30
31
  end
31
32
 
32
33
  interface _WorkflowStateRepository
33
- def load: (String thread_id) -> Hash[untyped, untyped]?
34
- def save: (String thread_id, expected_revision: Integer?, snapshot: Hash[untyped, untyped]) -> untyped
35
- def delete: (String thread_id, expected_revision: Integer) -> untyped
34
+ def load: (String workflow_instance_id) -> Hash[untyped, untyped]?
35
+ def save: (String workflow_instance_id, expected_revision: Integer?, snapshot: Hash[untyped, untyped]) -> Integer
36
+ def delete: (String workflow_instance_id, expected_revision: Integer) -> untyped
37
+ end
38
+
39
+ # Backend-side record repository interfaces. DurableRecord is the opaque data
40
+ # carrier. Identity/revision/index metadata is passed separately so backends do
41
+ # not need to interpret DurableRecord#payload.
42
+ interface _BackendAgentRecordRepository
43
+ def create: (agent_id: String, agent_revision: Integer, record: Persistence::DurableRecord) -> Persistence::DurableRecord
44
+ def load: (String agent_id) -> Persistence::DurableRecord
45
+ def save: (String agent_id, expected_revision: Integer, next_revision: Integer, record: Persistence::DurableRecord) -> Persistence::DurableRecord
46
+ def delete: (String agent_id) -> untyped
47
+ end
48
+
49
+ interface _BackendJournalRecordRepository
50
+ def append: (String agent_id, expected_position: Integer, records: Array[Persistence::DurableRecord], record_ids: Array[String]) -> Array[Persistence::DurableRecord]
51
+ def read: (String agent_id, ?after: Integer?, ?limit: Integer?) -> Array[Persistence::DurableRecord]
52
+ def head: (String agent_id) -> Integer
53
+ def delete: (String agent_id) -> untyped
54
+ end
55
+
56
+ interface _BackendExecutionRecordRepository
57
+ def create_active: (execution_id: String, agent_id: String, execution_revision: Integer, record: Persistence::DurableRecord) -> Persistence::DurableRecord
58
+ def load: (String execution_id) -> Persistence::DurableRecord
59
+ def save: (String execution_id, expected_revision: Integer, next_revision: Integer, agent_id: String, active: bool, record: Persistence::DurableRecord) -> Persistence::DurableRecord
60
+ def list_active: (String agent_id) -> Array[Persistence::DurableRecord]
61
+ def delete: (String execution_id) -> untyped
62
+ def delete_for_agent: (String agent_id) -> untyped
63
+ def assert_idle!: (String agent_id) -> untyped
64
+ end
65
+
66
+ interface _BackendWorkflowStateRecordRepository
67
+ def load: (String workflow_instance_id) -> Persistence::DurableRecord?
68
+ def save: (String workflow_instance_id, expected_revision: Integer?, next_revision: Integer, record: Persistence::DurableRecord) -> Persistence::DurableRecord
69
+ def delete: (String workflow_instance_id, expected_revision: Integer) -> untyped
70
+ end
71
+
72
+ interface _BackendWatermark
73
+ def assert_agent_watermark!: (agent_id: String, agent_revision: Integer, journal_position: Integer) -> true
36
74
  end
37
75
 
38
76
  interface _PersistenceTransactionView
@@ -54,6 +92,25 @@ module Phronomy
54
92
  class SerializationError < Phronomy::Error
55
93
  end
56
94
 
95
+ class DurableRecord
96
+ FORMAT_VERSION_PATTERN: Regexp
97
+ attr_reader record_type: String
98
+ attr_reader format_version: String
99
+ attr_reader payload: Hash[String, untyped]
100
+ def initialize: (record_type: String, format_version: String, payload: Hash[String, untyped]) -> void
101
+ def copy: () -> DurableRecord
102
+ end
103
+
104
+ module Migration
105
+ module InitialFormatMigration
106
+ def self.agent_root: (Hash[untyped, untyped] hash) -> DurableRecord
107
+ def self.agent_execution: (Hash[untyped, untyped] hash) -> DurableRecord
108
+ def self.journal_record: (Hash[untyped, untyped] hash) -> DurableRecord
109
+ def self.workflow_state: (workflow_instance_id: String, revision: Integer, snapshot: Hash[untyped, untyped]) -> DurableRecord
110
+ def self.llm_input_manifest: (Hash[untyped, untyped] hash) -> Hash[String, untyped]
111
+ end
112
+ end
113
+
57
114
  REQUIRED_CAPABILITIES: Hash[Symbol, bool]
58
115
 
59
116
  attr_reader contents: _ContentRepository
@@ -62,9 +119,10 @@ module Phronomy
62
119
  attr_reader executions: _ExecutionRepository
63
120
  attr_reader workflow_states: _WorkflowStateRepository
64
121
 
65
- def initialize: (contents: _ContentRepository, agents: _AgentRepository, journals: _JournalRepository, executions: _ExecutionRepository, workflow_states: _WorkflowStateRepository) -> void
122
+ def initialize: (contents: _ContentRepository, agents: _BackendAgentRecordRepository, journals: _BackendJournalRecordRepository, executions: _BackendExecutionRecordRepository, workflow_states: _BackendWorkflowStateRecordRepository) -> void
66
123
  def capabilities: () -> Hash[Symbol, bool]
67
124
  def transaction: () { (_PersistenceTransactionView) -> untyped } -> untyped
125
+ def build_transaction_view: (contents: _ContentRepository, agents: _BackendAgentRecordRepository, journals: _BackendJournalRecordRepository, executions: _BackendExecutionRecordRepository, workflow_states: _BackendWorkflowStateRecordRepository, watermark: _BackendWatermark) -> _PersistenceTransactionView
68
126
  def assert_agent_watermark!: (agent_id: String, agent_revision: Integer, journal_position: Integer) -> true
69
127
  end
70
128
  end
@@ -26,18 +26,15 @@ end
26
26
 
27
27
  module Phronomy
28
28
  class InvocationContext
29
- attr_reader thread_id: String?
30
- attr_reader session_id: String?
31
29
  attr_reader user_id: String?
32
30
  attr_reader cancellation_token: Concurrency::CancellationToken?
33
31
  attr_reader deadline: untyped
34
- attr_reader tracer_span: untyped
35
32
  attr_reader token_budget: Integer?
36
33
  attr_reader approval_policy: untyped
37
34
  attr_reader redaction_policy: untyped
38
35
  attr_reader task_id: String?
39
36
  attr_reader parent_task_id: String?
40
37
 
41
- def initialize: (?thread_id: String?, ?session_id: String?, ?user_id: String?, ?cancellation_token: Concurrency::CancellationToken?, ?deadline: untyped, ?tracer_span: untyped, ?token_budget: Integer?, ?approval_policy: untyped, ?redaction_policy: untyped, ?task_id: String?, ?parent_task_id: String?) -> void
38
+ def initialize: (?user_id: String?, ?cancellation_token: Concurrency::CancellationToken?, ?deadline: untyped, ?token_budget: Integer?, ?approval_policy: untyped, ?redaction_policy: untyped, ?task_id: String?, ?parent_task_id: String?) -> void
42
39
  end
43
40
  end
@@ -2,7 +2,7 @@ module Phronomy
2
2
  module WorkflowContext
3
3
  def phase: () -> Symbol
4
4
  def halted?: () -> bool
5
- def thread_id: () -> String?
5
+ def workflow_instance_id: () -> String?
6
6
  def merge: (Hash[Symbol, untyped] updates) -> self
7
7
  def to_h: () -> Hash[Symbol, untyped]
8
8
  end
@@ -15,7 +15,7 @@ module Phronomy
15
15
  def stream: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) { (untyped) -> void } -> untyped
16
16
  def resume: (state: untyped, ?input: untyped) -> untyped
17
17
  def send_event: (state: untyped, event: Symbol, ?input: untyped) -> untyped
18
- def signal: (thread_id: String, event: Symbol, ?payload: untyped) -> bool
18
+ def signal: (workflow_instance_id: String, event: Symbol, ?payload: untyped) -> bool
19
19
 
20
20
  class Builder
21
21
  def initial: (Symbol state_name) -> untyped
data/sig/phronomy.rbs CHANGED
@@ -19,6 +19,9 @@ module Phronomy
19
19
  class ConfigurationError < Error
20
20
  end
21
21
 
22
+ class HandoffError < Error
23
+ end
24
+
22
25
  class TransportError < Error
23
26
  end
24
27
 
@@ -37,6 +40,12 @@ module Phronomy
37
40
  class EventLoopReentrancyError < Error
38
41
  end
39
42
 
43
+ class AgentAlreadyExistsError < Error
44
+ end
45
+
46
+ class AgentPurgedError < Error
47
+ end
48
+
40
49
  class PoolShutdownError < Error
41
50
  end
42
51
 
@@ -45,6 +54,7 @@ module Phronomy
45
54
 
46
55
  class Configuration
47
56
  attr_accessor llm_adapter: LLMAdapter::Base
57
+ attr_accessor before_llm_input: Agent::_BeforeLLMInputHook?
48
58
  end
49
59
 
50
60
  def self.configuration: () -> Configuration