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
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
> **CURRENT explanatory architecture**
|
|
2
|
+
>
|
|
3
|
+
> This document describes the reconciled current Phronomy system. Normative
|
|
4
|
+
> architecture decisions remain in the [ADR index](../decisions/README.md);
|
|
5
|
+
> source/runtime behavior remains implementation reality and does not silently
|
|
6
|
+
> amend an ADR.
|
|
7
|
+
|
|
8
|
+
# Knowledge and RAG
|
|
9
|
+
|
|
10
|
+
## 1. Boundary
|
|
11
|
+
|
|
12
|
+
Phronomy separates information acquisition from Agent Context management.
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
Application / Tool / retrieval pipeline
|
|
16
|
+
|
|
|
17
|
+
v
|
|
18
|
+
plain logical Knowledge content
|
|
19
|
+
|
|
|
20
|
+
+-- persist: Agent Journal (`kind: :knowledge`)
|
|
21
|
+
|
|
|
22
|
+
+-- request-scoped: before_llm_input candidate
|
|
23
|
+
|
|
|
24
|
+
v
|
|
25
|
+
ContextPolicyInput.knowledge
|
|
26
|
+
|
|
|
27
|
+
v
|
|
28
|
+
ContextPolicy
|
|
29
|
+
|
|
|
30
|
+
v
|
|
31
|
+
Manifest
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Phronomy does not define a core hierarchy of `StaticKnowledge`,
|
|
35
|
+
`EntityKnowledge`, `RAGKnowledge`, or `KnowledgeSource` objects. How information
|
|
36
|
+
was obtained is not a Context-selection type.
|
|
37
|
+
|
|
38
|
+
Persistent Knowledge authority is defined by
|
|
39
|
+
[ADR-013](../decisions/013-journal-backed-knowledge-as-context-candidates.md).
|
|
40
|
+
|
|
41
|
+
## 2. Persistent Knowledge
|
|
42
|
+
|
|
43
|
+
Persistent Knowledge is registered on a live Agent:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
agent = ResearchAgent.new(
|
|
47
|
+
knowledge: [
|
|
48
|
+
"Account type: enterprise",
|
|
49
|
+
"Data residency: Japan"
|
|
50
|
+
]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
agent.add_knowledge(
|
|
54
|
+
"Customer locale: ja-JP",
|
|
55
|
+
metadata: {"origin" => "customer_profile"}
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Content is stored through the durable content/Journal path and remains available
|
|
60
|
+
after Agent reload.
|
|
61
|
+
|
|
62
|
+
`clear_knowledge!` logically invalidates prior Knowledge for future Context
|
|
63
|
+
selection without deleting historical Journal records. `clear_transcript!` does
|
|
64
|
+
not clear Knowledge. `reset_context!` resets both relevant eligibility domains
|
|
65
|
+
without rewriting old Journal facts.
|
|
66
|
+
|
|
67
|
+
## 3. Request-scoped Knowledge
|
|
68
|
+
|
|
69
|
+
Information needed for only one LLM Call should not be persisted merely to make
|
|
70
|
+
it available to Context Policy.
|
|
71
|
+
|
|
72
|
+
`before_llm_input` may return an `LLMInputPatch` with a Knowledge
|
|
73
|
+
`segment_candidate`. The candidate enters the same typed Policy path and is not
|
|
74
|
+
automatically journaled.
|
|
75
|
+
|
|
76
|
+
See [before_llm_input](before-llm-input.md).
|
|
77
|
+
|
|
78
|
+
## 4. Retrieval paths
|
|
79
|
+
|
|
80
|
+
RAG can enter Agent reasoning through more than one ordinary application path.
|
|
81
|
+
|
|
82
|
+
### Tool-result retrieval
|
|
83
|
+
|
|
84
|
+
A Tool may query a VectorStore/external search system and return results to the
|
|
85
|
+
Agent. The Tool result participates in the normal Tool protocol and can influence
|
|
86
|
+
later Context through normal Journal/Context rules.
|
|
87
|
+
|
|
88
|
+
### Pre-Manifest retrieval
|
|
89
|
+
|
|
90
|
+
Application code may retrieve/rank content before Context finalization and supply
|
|
91
|
+
selected logical Knowledge through `before_llm_input` or an Application
|
|
92
|
+
`ContextPolicy`.
|
|
93
|
+
|
|
94
|
+
The core does not require one vector-retrieval lifecycle to be the universal RAG
|
|
95
|
+
path.
|
|
96
|
+
|
|
97
|
+
## 5. Vector/embedding responsibility
|
|
98
|
+
|
|
99
|
+
Vector stores, loaders, splitters, embeddings, ranking, and external retrieval
|
|
100
|
+
are acquisition/integration capabilities. They are not themselves durable Agent
|
|
101
|
+
Knowledge state.
|
|
102
|
+
|
|
103
|
+
An application may:
|
|
104
|
+
|
|
105
|
+
1. retrieve content;
|
|
106
|
+
2. rank/filter it;
|
|
107
|
+
3. convert it to plain logical Knowledge;
|
|
108
|
+
4. persist it with `add_knowledge`, or keep it request-scoped.
|
|
109
|
+
|
|
110
|
+
## 6. Selection and budget
|
|
111
|
+
|
|
112
|
+
Knowledge is optional by default. Context Policy decides whether it enters one
|
|
113
|
+
LLM Call and may omit it under budget pressure.
|
|
114
|
+
|
|
115
|
+
Information that is structurally required for the call must be represented
|
|
116
|
+
through the corresponding required Context mechanism; registration as Knowledge
|
|
117
|
+
does not make content mandatory merely by existence.
|
|
118
|
+
|
|
119
|
+
## 7. Trust/security boundary
|
|
120
|
+
|
|
121
|
+
Phronomy does not apply a universal semantic security Filter when Knowledge is
|
|
122
|
+
added or retrieved. Source validation, retrieval policy, ingestion sanitization,
|
|
123
|
+
and domain-specific trust decisions are Application/Tool responsibilities.
|
|
124
|
+
|
|
125
|
+
At one-call Context selection time, Application `ContextPolicy` may inspect
|
|
126
|
+
Knowledge provenance/metadata and decide whether to select, derive/sanitize, or
|
|
127
|
+
fail the preparation. Framework validation still enforces structural authority
|
|
128
|
+
and final Manifest invariants.
|
|
129
|
+
|
|
130
|
+
See [Security Boundaries](security-boundaries.md).
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
> **CURRENT explanatory architecture**
|
|
2
|
+
>
|
|
3
|
+
> This document describes the reconciled current Phronomy system. Normative
|
|
4
|
+
> architecture decisions remain in the [ADR index](../decisions/README.md);
|
|
5
|
+
> source/runtime behavior remains implementation reality and does not silently
|
|
6
|
+
> amend an ADR.
|
|
7
|
+
|
|
8
|
+
# Semantic Multi-Agent Handoff
|
|
9
|
+
|
|
10
|
+
## 1. Meaning
|
|
11
|
+
|
|
12
|
+
Handoff transfers active responsibility from one live Source Agent to one live
|
|
13
|
+
Target Agent together with sufficient policy-bounded Context for the Target to
|
|
14
|
+
continue.
|
|
15
|
+
|
|
16
|
+
It is a control-plane operation, not an ordinary Tool-result protocol.
|
|
17
|
+
|
|
18
|
+
Handoff is distinct from Agent-as-Tool delegation. Delegation performs work and
|
|
19
|
+
returns control to the caller. Handoff changes the active Agent for the current
|
|
20
|
+
coordination lifetime.
|
|
21
|
+
|
|
22
|
+
Normative Handoff intent is
|
|
23
|
+
[ADR-016](../decisions/016-semantic-multi-agent-handoff.md).
|
|
24
|
+
|
|
25
|
+
## 2. Public API
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
handoff = Phronomy::MultiAgent::Handoff.new(
|
|
29
|
+
source_agent: triage,
|
|
30
|
+
target_agent: billing,
|
|
31
|
+
description: "Transfer billing responsibility",
|
|
32
|
+
policy: policy
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
runner = Phronomy::MultiAgent::Runner.new(
|
|
36
|
+
main_agent: triage,
|
|
37
|
+
handoffs: [handoff]
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
result = runner.invoke("My invoice is wrong")
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The current public Runner facade is synchronous `#invoke`. Handoff architecture
|
|
44
|
+
does not add async/stream APIs merely for symmetry.
|
|
45
|
+
|
|
46
|
+
## 3. Private transport
|
|
47
|
+
|
|
48
|
+
Outgoing Handoffs are represented to the Source LLM through generated Tool
|
|
49
|
+
schemas. The generated Tool name is private transport encoding, not Handoff
|
|
50
|
+
identity or public semantic contract.
|
|
51
|
+
|
|
52
|
+
Phronomy intercepts the Provider Tool Call into a typed private
|
|
53
|
+
`HandoffRequest`. It does not execute an ordinary `ToolInvocation` or emit a
|
|
54
|
+
sentinel Tool result.
|
|
55
|
+
|
|
56
|
+
## 4. Handoff Policy
|
|
57
|
+
|
|
58
|
+
`HandoffPolicy` controls what material from the effective finalized Source
|
|
59
|
+
Manifest may cross the Agent boundary.
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
current_request
|
|
63
|
+
history
|
|
64
|
+
knowledge
|
|
65
|
+
tool_exchanges
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Each category is required, forbidden, or selectable. Handoff selection is bounded
|
|
69
|
+
to the finalized Source Manifest.
|
|
70
|
+
|
|
71
|
+
## 5. Context Policy relationship
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
Source ContextPolicy
|
|
75
|
+
-> Source LLMInputManifest
|
|
76
|
+
|
|
77
|
+
HandoffPolicy
|
|
78
|
+
-> what may cross Source -> Target
|
|
79
|
+
|
|
80
|
+
immutable HandoffContext
|
|
81
|
+
-> request-scoped Target material
|
|
82
|
+
|
|
83
|
+
Target ContextPolicy
|
|
84
|
+
-> what enters one Target LLM Call
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Transfer does not automatically append content to Target Journal or persistent
|
|
88
|
+
Knowledge.
|
|
89
|
+
|
|
90
|
+
## 6. Conversation/Tool dependencies
|
|
91
|
+
|
|
92
|
+
An assistant Tool Call and its corresponding Tool-role result message(s) remain
|
|
93
|
+
an indivisible conversation group across the Handoff boundary.
|
|
94
|
+
|
|
95
|
+
Framework-owned semantic/category/content-format metadata is carried only through
|
|
96
|
+
the trusted typed Handoff boundary.
|
|
97
|
+
|
|
98
|
+
## 7. Responsibility and provenance
|
|
99
|
+
|
|
100
|
+
Transferred `responsibility` is the dynamic instruction for what the Target must
|
|
101
|
+
continue. `description` is the static edge/capability description. No mandatory
|
|
102
|
+
generic `handoff_reason` identity field is added.
|
|
103
|
+
|
|
104
|
+
Handoff Context preserves origin Agent and, where available, Journal record,
|
|
105
|
+
Agent execution, LLM call, and Tool call provenance. Multi-hop transfers extend
|
|
106
|
+
the transfer path while retaining original provenance.
|
|
107
|
+
|
|
108
|
+
## 8. Target state ownership
|
|
109
|
+
|
|
110
|
+
Handoff does not give Source authority to mutate Target canonical state. Target
|
|
111
|
+
state changes only through normal Target-owned execution/mutation paths.
|
|
112
|
+
|
|
113
|
+
A Target Agent execution has its own `execution_id`; Source execution identity is
|
|
114
|
+
provenance/audit context, not Target execution identity.
|
|
115
|
+
|
|
116
|
+
## 9. Next-turn continuity and durability
|
|
117
|
+
|
|
118
|
+
The same `main_agent` instance plus the same Runtime define one coordination
|
|
119
|
+
lifetime.
|
|
120
|
+
|
|
121
|
+
Within that lifetime, the active Target remains active on later turns and across
|
|
122
|
+
Runner-facade recreation. Runtime admission rejects racing concurrent turns for
|
|
123
|
+
the same coordination lifetime.
|
|
124
|
+
|
|
125
|
+
Active routing is **not durably rehydrated**. Runtime/process reset starts a new
|
|
126
|
+
coordination lifetime at `main_agent`. Historical Handoff audit facts do not
|
|
127
|
+
reconstruct active Target ownership.
|
|
128
|
+
|
|
129
|
+
## 10. Cancellation and tracing
|
|
130
|
+
|
|
131
|
+
Handoff does not create a separate cancellation domain; active Agent execution
|
|
132
|
+
uses normal Agent semantics.
|
|
133
|
+
|
|
134
|
+
One Runner user turn is automatically observable as `multi_agent.turn`. Source
|
|
135
|
+
and Target Agent/LLM/Tool logical operations keep their own automatic spans and
|
|
136
|
+
semantic IDs. Handoff adds no generic correlation identity or cross-Runtime
|
|
137
|
+
parent-span guarantee.
|
|
138
|
+
|
|
139
|
+
See [Tracing](tracing.md).
|
|
140
|
+
|
|
141
|
+
## 11. Safety and removed API
|
|
142
|
+
|
|
143
|
+
`Phronomy::MultiAgent::Runner::MAX_HANDOFFS` bounds transfers in one user turn.
|
|
144
|
+
|
|
145
|
+
Not current contracts:
|
|
146
|
+
|
|
147
|
+
- sentinel Handoff Tool results;
|
|
148
|
+
- `Phronomy::Agent::Runner`;
|
|
149
|
+
- `agents:` / `routes:` Runner configuration;
|
|
150
|
+
- Agent-owned Handoff Tool registration;
|
|
151
|
+
- generated Tool-name identity;
|
|
152
|
+
- blanket Source history/Knowledge copying.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
> **CURRENT explanatory architecture**
|
|
2
|
+
>
|
|
3
|
+
> This document describes the reconciled current Phronomy system. Normative
|
|
4
|
+
> architecture decisions remain in the [ADR index](../decisions/README.md);
|
|
5
|
+
> source/runtime behavior remains implementation reality and does not silently
|
|
6
|
+
> amend an ADR.
|
|
7
|
+
|
|
8
|
+
# Persistence, Durability, and Recovery
|
|
9
|
+
|
|
10
|
+
## 1. Durable boundary
|
|
11
|
+
|
|
12
|
+
`Phronomy::Persistence` is the single durable-state backend abstraction for
|
|
13
|
+
stateful Agents and durable Workflows.
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
Persistence
|
|
17
|
+
├─ contents
|
|
18
|
+
├─ agents
|
|
19
|
+
├─ journals
|
|
20
|
+
├─ executions
|
|
21
|
+
└─ workflow_states
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Persistence stores defined durable logical state. It is not serialization of the
|
|
25
|
+
currently running Runtime object graph.
|
|
26
|
+
|
|
27
|
+
Detailed custom-backend method/codec contracts belong in
|
|
28
|
+
[Persistence backends](../persistence-backends.md).
|
|
29
|
+
|
|
30
|
+
Normative durability vocabulary is
|
|
31
|
+
[ADR-018](../decisions/018-durability-guarantees-and-failure-model.md).
|
|
32
|
+
|
|
33
|
+
## 2. Durable versus Runtime-only state
|
|
34
|
+
|
|
35
|
+
Durable examples include AgentRoot, Journal records, AgentExecution records,
|
|
36
|
+
content/Manifest references, and durable Workflow snapshots.
|
|
37
|
+
|
|
38
|
+
Runtime-only examples include FSMSession objects/IDs, AgentInvocation objects,
|
|
39
|
+
Task instances/callbacks, EventLoop entries, Runtime admission entries, and
|
|
40
|
+
in-flight Provider/Tool operation objects.
|
|
41
|
+
|
|
42
|
+
Runtime/process loss removes Runtime-only objects but does not imply confirmed
|
|
43
|
+
durable state was lost.
|
|
44
|
+
|
|
45
|
+
## 3. Agent live ownership
|
|
46
|
+
|
|
47
|
+
After create/load, one live Agent instance owns current mutable logical Agent
|
|
48
|
+
state for that `agent_id` within one Runtime/process.
|
|
49
|
+
|
|
50
|
+
During execution, EventLoop is the single writer of Phronomy-managed live Agent
|
|
51
|
+
execution state.
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
EventLoop authoritative snapshot
|
|
55
|
+
-> OffloadPool command
|
|
56
|
+
-> Persistence transaction
|
|
57
|
+
-> immutable operation-specific result
|
|
58
|
+
-> EventLoop authority validation
|
|
59
|
+
-> live apply
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
See
|
|
63
|
+
[ADR-024](../decisions/024-event-loop-single-writer-agent-runtime.md).
|
|
64
|
+
|
|
65
|
+
## 4. Durable transitions and conflicts
|
|
66
|
+
|
|
67
|
+
Defined semantic durable transitions are atomic according to the Persistence
|
|
68
|
+
transaction contract and conforming backend.
|
|
69
|
+
|
|
70
|
+
Revision/watermark/CAS checks reject stale durable transitions with
|
|
71
|
+
`Persistence::ConflictError` rather than silently merging/reloading competing
|
|
72
|
+
state.
|
|
73
|
+
|
|
74
|
+
Conflict detection is not competing-execution exclusion and cannot undo an
|
|
75
|
+
external side effect already performed.
|
|
76
|
+
|
|
77
|
+
## 5. Agent and Workflow admission
|
|
78
|
+
|
|
79
|
+
Within one Runtime/process, Agent admission prevents prohibited competing
|
|
80
|
+
top-level execution for one logical Agent owner.
|
|
81
|
+
|
|
82
|
+
The canonical logical/durable Workflow identity is `workflow_instance_id`, which
|
|
83
|
+
is separate from one Runtime `fsm_session_id`.
|
|
84
|
+
|
|
85
|
+
Workflow same-process admission is acquired before durable hydration and retained
|
|
86
|
+
through the authoritative terminal/halted save barrier. The caller-facing Task
|
|
87
|
+
settles after that authoritative durable barrier.
|
|
88
|
+
|
|
89
|
+
See
|
|
90
|
+
[ADR-025](../decisions/025-process-local-agent-ownership-and-runtime-admission.md),
|
|
91
|
+
[ADR-020](../decisions/020-canonical-workflow-instance-identity.md), and
|
|
92
|
+
[ADR-026](../decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md).
|
|
93
|
+
|
|
94
|
+
## 6. Cross-process guarantee
|
|
95
|
+
|
|
96
|
+
Cross-process competing-execution exclusion is conditional.
|
|
97
|
+
|
|
98
|
+
A multi-process deployment must provide stable routing/partitioning by logical
|
|
99
|
+
identity or another coordination mechanism that establishes exclusive authority.
|
|
100
|
+
Persistence CAS/revision checks alone detect stale commits; they do not prevent
|
|
101
|
+
duplicate semantic execution from starting.
|
|
102
|
+
|
|
103
|
+
Phronomy does not claim arbitrary external exactly-once side effects.
|
|
104
|
+
|
|
105
|
+
## 7. Durable codec
|
|
106
|
+
|
|
107
|
+
Durable backend exchange uses immutable
|
|
108
|
+
`Phronomy::Persistence::DurableRecord` values with:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
record_type
|
|
112
|
+
format_version
|
|
113
|
+
payload
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`format_version` is a durable representation version, not Agent definition
|
|
117
|
+
version, Workflow identity, or Runtime incarnation identity.
|
|
118
|
+
|
|
119
|
+
Payloads follow the canonical durable value/codec contract. A backend may use its
|
|
120
|
+
own physical storage representation but must return an equivalent logical
|
|
121
|
+
DurableRecord.
|
|
122
|
+
|
|
123
|
+
## 8. Recovery model
|
|
124
|
+
|
|
125
|
+
Recovery reconstructs logical state from confirmed durable evidence; it does not
|
|
126
|
+
restore the old Runtime object graph.
|
|
127
|
+
|
|
128
|
+
The shared Recovery vocabulary classifies unfinished work as:
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
resumable
|
|
132
|
+
reconcilable
|
|
133
|
+
resolution_required
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Where outcome is uncertain, Phronomy does not infer "not performed" merely from
|
|
137
|
+
connection/process failure and does not blindly re-dispatch an external
|
|
138
|
+
operation.
|
|
139
|
+
|
|
140
|
+
Application resolution, when required, records one of:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
succeeded
|
|
144
|
+
failed
|
|
145
|
+
not_performed
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
before dependent continuation may proceed.
|
|
149
|
+
|
|
150
|
+
Agent Recovery is integrated into supported `Agent.load` lifecycle semantics.
|
|
151
|
+
|
|
152
|
+
## 9. Manifest authority during Recovery
|
|
153
|
+
|
|
154
|
+
A finalized historical `LLMInputManifest` is reused for the Provider input it
|
|
155
|
+
represents. Recovery does not rerun historical ContextPolicy code to reconstruct
|
|
156
|
+
that finalized input.
|
|
157
|
+
|
|
158
|
+
## 10. External-effect boundary
|
|
159
|
+
|
|
160
|
+
External Provider/Tool/Application effects are outside the ordinary Phronomy
|
|
161
|
+
Persistence transaction boundary (`X0` in ADR-018).
|
|
162
|
+
|
|
163
|
+
Therefore:
|
|
164
|
+
|
|
165
|
+
- durable-transition atomicity is not external-effect atomicity;
|
|
166
|
+
- semantic IDs alone do not prevent duplicate external effects;
|
|
167
|
+
- retry eligibility depends on outcome certainty plus operation-specific
|
|
168
|
+
idempotency/reconciliation contracts; and
|
|
169
|
+
- arbitrary exactly-once external side-effect execution is not an unconditional
|
|
170
|
+
Phronomy guarantee.
|
|
171
|
+
|
|
172
|
+
## 11. Reference backend details are not architecture
|
|
173
|
+
|
|
174
|
+
InMemory implementation mechanisms do not become requirements for external
|
|
175
|
+
backends unless the Backend SPI states the corresponding semantic property.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
> **CURRENT negative architecture guidance**
|
|
2
|
+
>
|
|
3
|
+
> This document records removed Agent Context/Memory designs that must not be
|
|
4
|
+
> mistaken for current contracts. Current architecture starts at
|
|
5
|
+
> [Agent Context](../agent-context.md) and
|
|
6
|
+
> [Context Management](../context-management.md).
|
|
7
|
+
|
|
8
|
+
# Removed Agent Context and Memory Architecture
|
|
9
|
+
|
|
10
|
+
## Mutable Provider chat as canonical state
|
|
11
|
+
|
|
12
|
+
RubyLLM/runtime message collections are projections/materializations, not
|
|
13
|
+
canonical Agent history. Canonical facts live in the append-only Journal and
|
|
14
|
+
each Provider input is fixed by an `LLMInputManifest`.
|
|
15
|
+
|
|
16
|
+
## Generic Memory subsystem
|
|
17
|
+
|
|
18
|
+
Phronomy does not use `Memory::ConversationManager`, `Memory::WindowMemory`,
|
|
19
|
+
`SummaryMemory`, or another mutable Memory object as Agent Context authority.
|
|
20
|
+
|
|
21
|
+
Long-context selection/derivation belongs to Context Policy.
|
|
22
|
+
|
|
23
|
+
## LlmContextWindow / ContextVersionCache
|
|
24
|
+
|
|
25
|
+
The removed window assembler/cache model is not a current extension boundary.
|
|
26
|
+
Context selection uses typed `ContextPolicyInput` and `ContextPlan`, with final
|
|
27
|
+
canonicalization/budget validation by `ContextAssembler`.
|
|
28
|
+
|
|
29
|
+
## Automatic history rewriting / Memory Compression
|
|
30
|
+
|
|
31
|
+
Phronomy does not delete or rewrite canonical Journal history to fit a model
|
|
32
|
+
window.
|
|
33
|
+
|
|
34
|
+
Application Policy may omit optional source items for one call and create
|
|
35
|
+
derived/compacted current-call content. Canonical source facts remain unchanged.
|
|
36
|
+
|
|
37
|
+
There is no separate current Memory Compression subsystem and no automatic
|
|
38
|
+
Default-Policy summarization LLM call.
|
|
39
|
+
|
|
40
|
+
## Static / Entity / RAG Knowledge hierarchy
|
|
41
|
+
|
|
42
|
+
Persistent Knowledge is Journal-backed logical Context, not a hierarchy of
|
|
43
|
+
source object classes. Retrieval/vector/entity extraction are acquisition
|
|
44
|
+
strategies owned by Application/Tool integrations.
|
|
45
|
+
|
|
46
|
+
## Removed Context selection DSLs
|
|
47
|
+
|
|
48
|
+
Not current public contracts:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
ContextRequest
|
|
52
|
+
ContextPolicyDescriptor
|
|
53
|
+
ContextPolicyRegistry
|
|
54
|
+
DerivedContentSpec
|
|
55
|
+
Selection::Unit
|
|
56
|
+
UnitBuilder / Selector / TokenBudgetPacker
|
|
57
|
+
ContextPlan#derived_contents
|
|
58
|
+
ContextPlan#ordering_hints
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Generic Agent session/thread identity
|
|
62
|
+
|
|
63
|
+
Agent Context is not keyed by a generic Application conversation/thread/session
|
|
64
|
+
identity. Phronomy uses purpose-specific logical/runtime operation identities.
|
|
65
|
+
|
|
66
|
+
## Cross-Agent shared history as Handoff
|
|
67
|
+
|
|
68
|
+
Semantic Handoff does not merge Journal histories or automatically copy all
|
|
69
|
+
Source history/Knowledge into Target durable state. It transfers policy-bounded
|
|
70
|
+
immutable Context and Target ContextPolicy still decides one-call input.
|
|
71
|
+
|
|
72
|
+
See [Multi-Agent Handoff](../multi-agent-handoff.md).
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
> **CURRENT explanatory architecture**
|
|
2
|
+
>
|
|
3
|
+
> This document describes the reconciled current Phronomy system. Normative
|
|
4
|
+
> architecture decisions remain in the [ADR index](../decisions/README.md);
|
|
5
|
+
> source/runtime behavior remains implementation reality and does not silently
|
|
6
|
+
> amend an ADR.
|
|
7
|
+
|
|
8
|
+
# Security Boundaries
|
|
9
|
+
|
|
10
|
+
## 1. Responsibility model
|
|
11
|
+
|
|
12
|
+
Phronomy deliberately keeps content-policy interception, Context semantic trust,
|
|
13
|
+
Tool authorization, and execution isolation as separate mechanisms.
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
raw invocation input
|
|
17
|
+
-> input_filter
|
|
18
|
+
|
|
19
|
+
Tool result
|
|
20
|
+
-> tool_result_filter
|
|
21
|
+
|
|
22
|
+
possible LLM Context material
|
|
23
|
+
-> ContextPolicyInput
|
|
24
|
+
-> Application ContextPolicy
|
|
25
|
+
semantic selection / omission / derivation /
|
|
26
|
+
source-aware trust/security policy
|
|
27
|
+
-> ContextPlan
|
|
28
|
+
-> Framework structural validation
|
|
29
|
+
-> LLMInputManifest
|
|
30
|
+
|
|
31
|
+
final Agent output
|
|
32
|
+
-> output_filter
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
No mechanism above is an all-purpose security layer.
|
|
36
|
+
|
|
37
|
+
The current Filter contract is
|
|
38
|
+
[ADR-019](../decisions/019-filter-contract-and-security-boundaries.md).
|
|
39
|
+
|
|
40
|
+
## 2. Explicit Filter call sites
|
|
41
|
+
|
|
42
|
+
`Phronomy::Filter::Base` may transform a value or block processing through
|
|
43
|
+
`FilterBlockError`.
|
|
44
|
+
|
|
45
|
+
Framework Filter call sites are explicit:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
input
|
|
49
|
+
output
|
|
50
|
+
Tool result
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
A Filter registered at one call site is not silently applied to every value that
|
|
54
|
+
may later reach an LLM.
|
|
55
|
+
|
|
56
|
+
In particular, Phronomy does **not** add a fourth `context_filter` /
|
|
57
|
+
`add_context_filter` Framework call site.
|
|
58
|
+
|
|
59
|
+
## 3. Context semantic trust belongs to Application Policy
|
|
60
|
+
|
|
61
|
+
LLM Context is domain-dependent: content that is suspicious in one application
|
|
62
|
+
may be exactly the content another Agent is supposed to analyze.
|
|
63
|
+
|
|
64
|
+
The Framework therefore owns structural/integrity validation, while
|
|
65
|
+
Application-defined `ContextPolicy` owns semantic trust decisions for one LLM
|
|
66
|
+
Call.
|
|
67
|
+
|
|
68
|
+
An Application Policy may:
|
|
69
|
+
|
|
70
|
+
- inspect typed category/provenance/metadata;
|
|
71
|
+
- omit optional material;
|
|
72
|
+
- fail preparation when required material is unacceptable;
|
|
73
|
+
- derive sanitized/redacted current-call material;
|
|
74
|
+
- retrieve/rerank content; or
|
|
75
|
+
- call a `Filter::Base` instance internally as ordinary Ruby policy logic.
|
|
76
|
+
|
|
77
|
+
There is no separate Framework invocation guarantee when a Policy chooses to
|
|
78
|
+
compose a Filter internally.
|
|
79
|
+
|
|
80
|
+
## 4. Non-destructive transformation
|
|
81
|
+
|
|
82
|
+
Context trust policy must not rewrite canonical Journal history.
|
|
83
|
+
|
|
84
|
+
For optional content, transformation follows the current Context model:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
canonical/source item remains intact
|
|
88
|
+
|
|
|
89
|
+
Policy omits it for this call
|
|
90
|
+
+
|
|
91
|
+
Policy creates derived current-call item
|
|
92
|
+
|
|
|
93
|
+
ContextPlan -> Manifest
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Required material is protected by Framework invariants. A Policy cannot silently
|
|
97
|
+
drop a required source item; if the required input cannot be accepted, Context
|
|
98
|
+
preparation fails.
|
|
99
|
+
|
|
100
|
+
## 5. Framework validation is not semantic filtering
|
|
101
|
+
|
|
102
|
+
After Policy selection, Phronomy validates:
|
|
103
|
+
|
|
104
|
+
- typed category/item identity;
|
|
105
|
+
- required material;
|
|
106
|
+
- Framework-owned metadata/provenance authority;
|
|
107
|
+
- conversation and Tool-call/result dependency structure;
|
|
108
|
+
- effective Tool wiring;
|
|
109
|
+
- canonical representation; and
|
|
110
|
+
- final token budget.
|
|
111
|
+
|
|
112
|
+
These checks establish integrity, not truth, safety, trustworthiness, or domain
|
|
113
|
+
appropriateness of arbitrary content.
|
|
114
|
+
|
|
115
|
+
## 6. Knowledge/RAG boundary
|
|
116
|
+
|
|
117
|
+
Phronomy does not add a mandatory semantic security hook to `add_knowledge`.
|
|
118
|
+
Retrieval-source validation and ingestion policy remain Application/Tool
|
|
119
|
+
responsibilities.
|
|
120
|
+
|
|
121
|
+
Per-call use of Knowledge is decided by Context Policy.
|
|
122
|
+
|
|
123
|
+
See [Knowledge and RAG](knowledge-and-rag.md).
|
|
124
|
+
|
|
125
|
+
## 7. Tool approval is authorization, not sanitization
|
|
126
|
+
|
|
127
|
+
Tool approval controls whether a side-effecting/capability operation may execute.
|
|
128
|
+
It does not sanitize arbitrary Tool arguments/results, establish prompt-injection
|
|
129
|
+
safety, or create an OS sandbox.
|
|
130
|
+
|
|
131
|
+
Authorization and content policy are separate concerns.
|
|
132
|
+
|
|
133
|
+
## 8. PromptInjectionFilter
|
|
134
|
+
|
|
135
|
+
`Phronomy::Filter::PromptInjectionFilter` is an optional bounded heuristic for
|
|
136
|
+
common patterns.
|
|
137
|
+
|
|
138
|
+
It does not guarantee detection of all prompt injection, automatically inspect
|
|
139
|
+
all Context sources, or turn LLM execution into a security sandbox.
|
|
140
|
+
|
|
141
|
+
Applications remain responsible for domain-specific controls.
|
|
142
|
+
|
|
143
|
+
## 9. Execution placement is not isolation
|
|
144
|
+
|
|
145
|
+
EventLoop, OffloadPool, cooperative cancellation, and Tool `execution_mode`
|
|
146
|
+
describe execution/lifecycle mechanics.
|
|
147
|
+
|
|
148
|
+
They do not provide:
|
|
149
|
+
|
|
150
|
+
- OS process isolation;
|
|
151
|
+
- container isolation;
|
|
152
|
+
- filesystem or network sandboxing;
|
|
153
|
+
- privilege separation; or
|
|
154
|
+
- containment of arbitrary malicious Application/Tool code.
|
|
155
|
+
|
|
156
|
+
Those properties are Application/deployment responsibilities unless a future
|
|
157
|
+
explicit isolation subsystem is accepted.
|
|
158
|
+
|
|
159
|
+
## 10. No new security-policy SPI
|
|
160
|
+
|
|
161
|
+
This architecture does not add:
|
|
162
|
+
|
|
163
|
+
```text
|
|
164
|
+
context_filter
|
|
165
|
+
SecurityContext
|
|
166
|
+
TrustLevel
|
|
167
|
+
security metadata authority API
|
|
168
|
+
sandbox SPI
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Arbitrary item `metadata` remains Application data except for
|
|
172
|
+
Framework-reserved control keys. Applications must not treat arbitrary metadata
|
|
173
|
+
as a Framework-certified trust assertion.
|