lex-agentic-self 0.1.9 → 0.1.10
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 +9 -0
- data/lib/legion/extensions/agentic/self/relationship_arc/client.rb +20 -0
- data/lib/legion/extensions/agentic/self/relationship_arc/helpers/arc_engine.rb +122 -0
- data/lib/legion/extensions/agentic/self/relationship_arc/helpers/constants.rb +39 -0
- data/lib/legion/extensions/agentic/self/relationship_arc/helpers/milestone.rb +42 -0
- data/lib/legion/extensions/agentic/self/relationship_arc/runners/relationship_arc.rb +80 -0
- data/lib/legion/extensions/agentic/self/relationship_arc/version.rb +13 -0
- data/lib/legion/extensions/agentic/self/relationship_arc.rb +19 -0
- data/lib/legion/extensions/agentic/self/version.rb +1 -1
- data/lib/legion/extensions/agentic/self.rb +1 -0
- data/spec/legion/extensions/agentic/self/relationship_arc/helpers/arc_engine_spec.rb +124 -0
- data/spec/legion/extensions/agentic/self/relationship_arc/helpers/constants_spec.rb +31 -0
- data/spec/legion/extensions/agentic/self/relationship_arc/helpers/milestone_spec.rb +46 -0
- data/spec/legion/extensions/agentic/self/relationship_arc/runners/relationship_arc_spec.rb +64 -0
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '081b534c7eaf4efbc0265eada2d3b8f11b37b93a3fb34be77068d0ce4f0e842d'
|
|
4
|
+
data.tar.gz: ce8a3839f617dc41d904ccf54a98a208037178cd89eefb39cd031fce8a89e315
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 49409e3e358267476770c66db88a4eff2f7968857c130e547cc3170a3c3e891b27358d7b28ac77e81e5733d480ab65480b38c10c908ef9323738b4159bab527e
|
|
7
|
+
data.tar.gz: fd913ef78cfa8b14ccb5bdadbd95e3133e5bcf67d21a40332ba360ba6393ebd1b34e7db0ccebe3a4bf177aea506d1fc666dcb0efc89820e26853e0574888998d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.10] - 2026-03-31
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- RelationshipArc sub-module for Phase C relational intelligence
|
|
7
|
+
- Constants: chapters, milestone types, health weights, chapter thresholds
|
|
8
|
+
- Milestone: typed data class with UUID, significance clamping, serialization
|
|
9
|
+
- ArcEngine: chapter progression, milestone tracking, relationship health, Apollo Local persistence
|
|
10
|
+
- RelationshipArc runner: record_milestone, update_arc, arc_stats with NarrativeIdentity episode stamping
|
|
11
|
+
|
|
3
12
|
## [0.1.9] - 2026-03-31
|
|
4
13
|
|
|
5
14
|
### Added
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/constants'
|
|
4
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/milestone'
|
|
5
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/arc_engine'
|
|
6
|
+
require 'legion/extensions/agentic/self/relationship_arc/runners/relationship_arc'
|
|
7
|
+
|
|
8
|
+
module Legion
|
|
9
|
+
module Extensions
|
|
10
|
+
module Agentic
|
|
11
|
+
module Self
|
|
12
|
+
module RelationshipArc
|
|
13
|
+
class Client
|
|
14
|
+
include Runners::RelationshipArc
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Self
|
|
7
|
+
module RelationshipArc
|
|
8
|
+
module Helpers
|
|
9
|
+
class ArcEngine
|
|
10
|
+
attr_reader :agent_id, :current_chapter, :milestones
|
|
11
|
+
|
|
12
|
+
BOND_STAGE_ORDER = %i[initial forming established deep].freeze
|
|
13
|
+
private_constant :BOND_STAGE_ORDER
|
|
14
|
+
|
|
15
|
+
def initialize(agent_id:)
|
|
16
|
+
@agent_id = agent_id
|
|
17
|
+
@current_chapter = :formative
|
|
18
|
+
@milestones = []
|
|
19
|
+
@dirty = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_milestone(type:, description:, significance:, **)
|
|
23
|
+
ms = Milestone.new(type: type, description: description, significance: significance)
|
|
24
|
+
@milestones << ms
|
|
25
|
+
@milestones.shift while @milestones.size > Constants::MAX_MILESTONES
|
|
26
|
+
@dirty = true
|
|
27
|
+
ms
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def update_chapter!(bond_stage: :initial, **)
|
|
31
|
+
new_chapter = derive_chapter(bond_stage)
|
|
32
|
+
return if Constants::CHAPTERS.index(new_chapter) <= Constants::CHAPTERS.index(@current_chapter)
|
|
33
|
+
|
|
34
|
+
@current_chapter = new_chapter
|
|
35
|
+
@dirty = true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def relationship_health(attachment_strength: 0.0, reciprocity_balance: 0.5,
|
|
39
|
+
communication_consistency: 0.5, **)
|
|
40
|
+
w = Constants::HEALTH_WEIGHTS
|
|
41
|
+
score = (attachment_strength.to_f * w[:attachment_strength]) +
|
|
42
|
+
(reciprocity_balance.to_f * w[:reciprocity_balance]) +
|
|
43
|
+
(communication_consistency.to_f * w[:communication_consistency])
|
|
44
|
+
score.clamp(0.0, 1.0)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def dirty?
|
|
48
|
+
@dirty
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def mark_clean!
|
|
52
|
+
@dirty = false
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def to_apollo_entries
|
|
57
|
+
tags = Constants::TAG_PREFIX.dup + [@agent_id]
|
|
58
|
+
tags << 'partner' if partner?(@agent_id)
|
|
59
|
+
[{ content: serialize(arc_state_hash), tags: tags }]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def from_apollo(store:)
|
|
63
|
+
result = store.query(text: 'relationship_arc', tags: Constants::TAG_PREFIX + [@agent_id])
|
|
64
|
+
return false unless result[:success] && result[:results]&.any?
|
|
65
|
+
|
|
66
|
+
parsed = deserialize(result[:results].first[:content])
|
|
67
|
+
return false unless parsed
|
|
68
|
+
|
|
69
|
+
@current_chapter = parsed[:current_chapter]&.to_sym || :formative
|
|
70
|
+
@milestones = (parsed[:milestones] || []).map { |mh| Milestone.from_h(mh.transform_keys(&:to_sym)) }
|
|
71
|
+
true
|
|
72
|
+
rescue StandardError => e
|
|
73
|
+
warn "[arc_engine] from_apollo error: #{e.message}"
|
|
74
|
+
false
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def to_h
|
|
78
|
+
{ agent_id: @agent_id, current_chapter: @current_chapter,
|
|
79
|
+
milestones: @milestones.map(&:to_h),
|
|
80
|
+
relationship_health: nil, milestone_count: @milestones.size }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def arc_state_hash
|
|
86
|
+
{ agent_id: @agent_id, current_chapter: @current_chapter,
|
|
87
|
+
milestones: @milestones.map(&:to_h) }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def derive_chapter(bond_stage)
|
|
91
|
+
Constants::CHAPTER_THRESHOLDS.each_key.reverse_each do |chapter|
|
|
92
|
+
threshold = Constants::CHAPTER_THRESHOLDS[chapter]
|
|
93
|
+
stage_idx = BOND_STAGE_ORDER.index(threshold[:stage]) || 0
|
|
94
|
+
current_stage_idx = BOND_STAGE_ORDER.index(bond_stage) || 0
|
|
95
|
+
|
|
96
|
+
return chapter if @milestones.size >= threshold[:milestones] &&
|
|
97
|
+
current_stage_idx >= stage_idx
|
|
98
|
+
end
|
|
99
|
+
:formative
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def partner?(agent_id)
|
|
103
|
+
defined?(Legion::Gaia::BondRegistry) && Legion::Gaia::BondRegistry.partner?(agent_id)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def serialize(hash)
|
|
107
|
+
defined?(Legion::JSON) ? Legion::JSON.dump(hash) : ::JSON.dump(hash)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def deserialize(content)
|
|
111
|
+
parsed = defined?(Legion::JSON) ? Legion::JSON.parse(content) : ::JSON.parse(content, symbolize_names: true)
|
|
112
|
+
parsed.is_a?(Hash) ? parsed.transform_keys(&:to_sym) : nil
|
|
113
|
+
rescue StandardError => _e
|
|
114
|
+
nil
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Self
|
|
7
|
+
module RelationshipArc
|
|
8
|
+
module Helpers
|
|
9
|
+
module Constants
|
|
10
|
+
CHAPTERS = %i[formative developing established deepening].freeze
|
|
11
|
+
|
|
12
|
+
MILESTONE_TYPES = %i[
|
|
13
|
+
first_interaction first_direct_address stage_transition
|
|
14
|
+
prediction_accuracy communication_shift absence_return
|
|
15
|
+
].freeze
|
|
16
|
+
|
|
17
|
+
HEALTH_WEIGHTS = {
|
|
18
|
+
attachment_strength: 0.4,
|
|
19
|
+
reciprocity_balance: 0.3,
|
|
20
|
+
communication_consistency: 0.3
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
MAX_MILESTONES = 200
|
|
24
|
+
|
|
25
|
+
CHAPTER_THRESHOLDS = {
|
|
26
|
+
developing: { milestones: 3, stage: :forming },
|
|
27
|
+
established: { milestones: 10, stage: :established },
|
|
28
|
+
deepening: { milestones: 25, stage: :deep }
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
TAG_PREFIX = %w[bond relationship_arc].freeze
|
|
32
|
+
MILESTONE_TAG_PREFIX = %w[bond milestone].freeze
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Agentic
|
|
8
|
+
module Self
|
|
9
|
+
module RelationshipArc
|
|
10
|
+
module Helpers
|
|
11
|
+
class Milestone
|
|
12
|
+
attr_reader :id, :type, :description, :significance, :created_at
|
|
13
|
+
|
|
14
|
+
def initialize(type:, description:, significance:, id: nil, created_at: nil)
|
|
15
|
+
@id = id || SecureRandom.uuid
|
|
16
|
+
@type = type.to_sym
|
|
17
|
+
@description = description
|
|
18
|
+
@significance = significance.to_f.clamp(0.0, 1.0)
|
|
19
|
+
@created_at = created_at || Time.now.utc
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_h
|
|
23
|
+
{ id: @id, type: @type, description: @description,
|
|
24
|
+
significance: @significance, created_at: @created_at.iso8601 }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.from_h(hash)
|
|
28
|
+
new(
|
|
29
|
+
id: hash[:id],
|
|
30
|
+
type: hash[:type],
|
|
31
|
+
description: hash[:description],
|
|
32
|
+
significance: hash[:significance],
|
|
33
|
+
created_at: hash[:created_at] ? Time.parse(hash[:created_at].to_s) : nil
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agentic
|
|
6
|
+
module Self
|
|
7
|
+
module RelationshipArc
|
|
8
|
+
module Runners
|
|
9
|
+
module RelationshipArc
|
|
10
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers) &&
|
|
11
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
12
|
+
|
|
13
|
+
def record_milestone(agent_id:, type:, description:, significance:, **)
|
|
14
|
+
unless Helpers::Constants::MILESTONE_TYPES.include?(type.to_sym)
|
|
15
|
+
return { success: false,
|
|
16
|
+
error: "unknown type: #{type}" }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
engine = arc_engine_for(agent_id)
|
|
20
|
+
ms = engine.add_milestone(type: type, description: description, significance: significance)
|
|
21
|
+
|
|
22
|
+
stamp_narrative_episode(ms)
|
|
23
|
+
|
|
24
|
+
{ success: true, milestone: ms.to_h }
|
|
25
|
+
rescue StandardError => e
|
|
26
|
+
{ success: false, error: e.message }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def update_arc(agent_id:, attachment_state: {}, **)
|
|
30
|
+
engine = arc_engine_for(agent_id)
|
|
31
|
+
engine.update_chapter!(bond_stage: attachment_state[:bond_stage] || :initial)
|
|
32
|
+
|
|
33
|
+
{ success: true, current_chapter: engine.current_chapter,
|
|
34
|
+
milestone_count: engine.milestones.size }
|
|
35
|
+
rescue StandardError => e
|
|
36
|
+
{ success: false, error: e.message }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def arc_stats(agent_id:, **)
|
|
40
|
+
engine = arc_engine_for(agent_id)
|
|
41
|
+
engine.to_h
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def arc_engine_for(agent_id)
|
|
47
|
+
@arc_engines ||= {}
|
|
48
|
+
@arc_engines[agent_id.to_s] ||= Helpers::ArcEngine.new(agent_id: agent_id.to_s)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def stamp_narrative_episode(milestone)
|
|
52
|
+
narrator = resolve_narrative_identity
|
|
53
|
+
return unless narrator
|
|
54
|
+
|
|
55
|
+
narrator.record_episode(
|
|
56
|
+
content: milestone.description,
|
|
57
|
+
episode_type: :relationship,
|
|
58
|
+
emotional_valence: 0.3,
|
|
59
|
+
significance: milestone.significance,
|
|
60
|
+
domain: :relationship,
|
|
61
|
+
tags: ['partner', 'milestone', milestone.type.to_s]
|
|
62
|
+
)
|
|
63
|
+
rescue StandardError => e
|
|
64
|
+
warn "[relationship_arc] narrative stamp failed: #{e.message}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def resolve_narrative_identity
|
|
68
|
+
return nil unless defined?(Legion::Extensions::Agentic::Self::NarrativeIdentity::Client)
|
|
69
|
+
|
|
70
|
+
@narrative_client ||= Legion::Extensions::Agentic::Self::NarrativeIdentity::Client.new
|
|
71
|
+
rescue StandardError => _e
|
|
72
|
+
nil
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'relationship_arc/version'
|
|
4
|
+
require_relative 'relationship_arc/helpers/constants'
|
|
5
|
+
require_relative 'relationship_arc/helpers/milestone'
|
|
6
|
+
require_relative 'relationship_arc/helpers/arc_engine'
|
|
7
|
+
require_relative 'relationship_arc/runners/relationship_arc'
|
|
8
|
+
require_relative 'relationship_arc/client'
|
|
9
|
+
|
|
10
|
+
module Legion
|
|
11
|
+
module Extensions
|
|
12
|
+
module Agentic
|
|
13
|
+
module Self
|
|
14
|
+
module RelationshipArc
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/constants'
|
|
5
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/milestone'
|
|
6
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/arc_engine'
|
|
7
|
+
|
|
8
|
+
RSpec.describe Legion::Extensions::Agentic::Self::RelationshipArc::Helpers::ArcEngine do
|
|
9
|
+
subject(:engine) { described_class.new(agent_id: 'partner-1') }
|
|
10
|
+
|
|
11
|
+
describe '#initialize' do
|
|
12
|
+
it 'starts in formative chapter' do
|
|
13
|
+
expect(engine.current_chapter).to eq(:formative)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'starts with empty milestones' do
|
|
17
|
+
expect(engine.milestones).to be_empty
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe '#add_milestone' do
|
|
22
|
+
it 'adds a milestone' do
|
|
23
|
+
engine.add_milestone(type: :first_interaction, description: 'Hello', significance: 0.8)
|
|
24
|
+
expect(engine.milestones.size).to eq(1)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'returns the milestone' do
|
|
28
|
+
ms = engine.add_milestone(type: :first_interaction, description: 'Hello', significance: 0.8)
|
|
29
|
+
expect(ms).to be_a(Legion::Extensions::Agentic::Self::RelationshipArc::Helpers::Milestone)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'marks dirty' do
|
|
33
|
+
engine.add_milestone(type: :first_interaction, description: 'Hello', significance: 0.8)
|
|
34
|
+
expect(engine).to be_dirty
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'caps at MAX_MILESTONES' do
|
|
38
|
+
201.times { |i| engine.add_milestone(type: :first_interaction, description: "ms #{i}", significance: 0.1) }
|
|
39
|
+
expect(engine.milestones.size).to eq(200)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe '#update_chapter!' do
|
|
44
|
+
it 'transitions to developing after enough milestones' do
|
|
45
|
+
4.times { engine.add_milestone(type: :first_interaction, description: 'x', significance: 0.5) }
|
|
46
|
+
engine.update_chapter!(bond_stage: :forming)
|
|
47
|
+
expect(engine.current_chapter).to eq(:developing)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'never regresses' do
|
|
51
|
+
engine.instance_variable_set(:@current_chapter, :established)
|
|
52
|
+
engine.update_chapter!(bond_stage: :initial)
|
|
53
|
+
expect(engine.current_chapter).to eq(:established)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe '#relationship_health' do
|
|
58
|
+
it 'computes weighted health score' do
|
|
59
|
+
health = engine.relationship_health(
|
|
60
|
+
attachment_strength: 0.8,
|
|
61
|
+
reciprocity_balance: 0.6,
|
|
62
|
+
communication_consistency: 0.7
|
|
63
|
+
)
|
|
64
|
+
expected = (0.8 * 0.4) + (0.6 * 0.3) + (0.7 * 0.3)
|
|
65
|
+
expect(health).to be_within(0.01).of(expected)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'clamps to 0.0..1.0' do
|
|
69
|
+
health = engine.relationship_health(
|
|
70
|
+
attachment_strength: 1.5,
|
|
71
|
+
reciprocity_balance: 1.5,
|
|
72
|
+
communication_consistency: 1.5
|
|
73
|
+
)
|
|
74
|
+
expect(health).to eq(1.0)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe '#dirty? and #mark_clean!' do
|
|
79
|
+
it 'starts clean' do
|
|
80
|
+
expect(engine).not_to be_dirty
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'cleans up' do
|
|
84
|
+
engine.add_milestone(type: :first_interaction, description: 'x', significance: 0.5)
|
|
85
|
+
engine.mark_clean!
|
|
86
|
+
expect(engine).not_to be_dirty
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
describe '#to_apollo_entries' do
|
|
91
|
+
before { engine.add_milestone(type: :first_interaction, description: 'Hello', significance: 0.8) }
|
|
92
|
+
|
|
93
|
+
it 'returns entries with arc state' do
|
|
94
|
+
entries = engine.to_apollo_entries
|
|
95
|
+
expect(entries).to be_an(Array)
|
|
96
|
+
expect(entries.first[:tags]).to include('bond', 'relationship_arc', 'partner-1')
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe '#from_apollo' do
|
|
101
|
+
let(:mock_store) { double('apollo_local') }
|
|
102
|
+
|
|
103
|
+
it 'restores state from Apollo' do
|
|
104
|
+
engine.add_milestone(type: :first_interaction, description: 'Hello', significance: 0.8)
|
|
105
|
+
engine.instance_variable_set(:@current_chapter, :developing)
|
|
106
|
+
content = engine.send(:serialize, engine.send(:arc_state_hash))
|
|
107
|
+
|
|
108
|
+
new_engine = described_class.new(agent_id: 'partner-1')
|
|
109
|
+
allow(mock_store).to receive(:query)
|
|
110
|
+
.and_return({ success: true, results: [{ content: content, tags: %w[bond relationship_arc partner-1] }] })
|
|
111
|
+
|
|
112
|
+
expect(new_engine.from_apollo(store: mock_store)).to be true
|
|
113
|
+
expect(new_engine.current_chapter).to eq(:developing)
|
|
114
|
+
expect(new_engine.milestones.size).to eq(1)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe '#to_h' do
|
|
119
|
+
it 'includes all fields' do
|
|
120
|
+
h = engine.to_h
|
|
121
|
+
expect(h).to include(:agent_id, :current_chapter, :milestones, :relationship_health)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/constants'
|
|
5
|
+
|
|
6
|
+
RSpec.describe Legion::Extensions::Agentic::Self::RelationshipArc::Helpers::Constants do
|
|
7
|
+
describe 'CHAPTERS' do
|
|
8
|
+
it 'defines 4 chapters in order' do
|
|
9
|
+
expect(described_class::CHAPTERS).to eq(%i[formative developing established deepening])
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe 'MILESTONE_TYPES' do
|
|
14
|
+
it 'includes expected types' do
|
|
15
|
+
expect(described_class::MILESTONE_TYPES).to include(:first_interaction, :stage_transition,
|
|
16
|
+
:prediction_accuracy, :absence_return)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe 'HEALTH_WEIGHTS' do
|
|
21
|
+
it 'sums to 1.0' do
|
|
22
|
+
expect(described_class::HEALTH_WEIGHTS.values.sum).to eq(1.0)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe 'MAX_MILESTONES' do
|
|
27
|
+
it 'caps at 200' do
|
|
28
|
+
expect(described_class::MAX_MILESTONES).to eq(200)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/constants'
|
|
5
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/milestone'
|
|
6
|
+
|
|
7
|
+
RSpec.describe Legion::Extensions::Agentic::Self::RelationshipArc::Helpers::Milestone do
|
|
8
|
+
subject(:milestone) do
|
|
9
|
+
described_class.new(type: :first_interaction, description: 'First hello', significance: 0.8)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#initialize' do
|
|
13
|
+
it 'sets type' do
|
|
14
|
+
expect(milestone.type).to eq(:first_interaction)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'generates a UUID' do
|
|
18
|
+
expect(milestone.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'clamps significance to 0.0..1.0' do
|
|
22
|
+
ms = described_class.new(type: :first_interaction, description: 'test', significance: 1.5)
|
|
23
|
+
expect(ms.significance).to eq(1.0)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'records timestamp' do
|
|
27
|
+
expect(milestone.created_at).to be_a(Time)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe '#to_h' do
|
|
32
|
+
it 'returns complete hash' do
|
|
33
|
+
h = milestone.to_h
|
|
34
|
+
expect(h).to include(:id, :type, :description, :significance, :created_at)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '.from_h' do
|
|
39
|
+
it 'round-trips' do
|
|
40
|
+
restored = described_class.from_h(milestone.to_h)
|
|
41
|
+
expect(restored.type).to eq(milestone.type)
|
|
42
|
+
expect(restored.description).to eq(milestone.description)
|
|
43
|
+
expect(restored.significance).to eq(milestone.significance)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/constants'
|
|
5
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/milestone'
|
|
6
|
+
require 'legion/extensions/agentic/self/relationship_arc/helpers/arc_engine'
|
|
7
|
+
require 'legion/extensions/agentic/self/relationship_arc/runners/relationship_arc'
|
|
8
|
+
|
|
9
|
+
RSpec.describe Legion::Extensions::Agentic::Self::RelationshipArc::Runners::RelationshipArc do
|
|
10
|
+
let(:described_module) { described_class }
|
|
11
|
+
let(:host) { Object.new.extend(described_module) }
|
|
12
|
+
|
|
13
|
+
before { host.instance_variable_set(:@arc_engines, nil) }
|
|
14
|
+
|
|
15
|
+
describe '#record_milestone' do
|
|
16
|
+
it 'records a milestone for an agent' do
|
|
17
|
+
result = host.record_milestone(agent_id: 'p1', type: :first_interaction,
|
|
18
|
+
description: 'Hello', significance: 0.8)
|
|
19
|
+
expect(result[:success]).to be true
|
|
20
|
+
expect(result[:milestone][:type]).to eq(:first_interaction)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'returns error for unknown type' do
|
|
24
|
+
result = host.record_milestone(agent_id: 'p1', type: :bogus,
|
|
25
|
+
description: 'x', significance: 0.5)
|
|
26
|
+
expect(result[:success]).to be false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'calls NarrativeIdentity record_episode when available' do
|
|
30
|
+
narrator = double('narrator')
|
|
31
|
+
allow(host).to receive(:resolve_narrative_identity).and_return(narrator)
|
|
32
|
+
allow(narrator).to receive(:record_episode).and_return({ success: true })
|
|
33
|
+
|
|
34
|
+
host.record_milestone(agent_id: 'p1', type: :first_interaction,
|
|
35
|
+
description: 'Hello', significance: 0.8)
|
|
36
|
+
expect(narrator).to have_received(:record_episode)
|
|
37
|
+
.with(hash_including(episode_type: :relationship, significance: 0.8))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#update_arc' do
|
|
42
|
+
it 'updates chapter based on attachment state' do
|
|
43
|
+
3.times do
|
|
44
|
+
host.record_milestone(agent_id: 'p1', type: :first_interaction,
|
|
45
|
+
description: 'x', significance: 0.5)
|
|
46
|
+
end
|
|
47
|
+
result = host.update_arc(agent_id: 'p1', attachment_state: { bond_stage: :forming })
|
|
48
|
+
expect(result[:success]).to be true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'returns arc summary' do
|
|
52
|
+
result = host.update_arc(agent_id: 'p1', attachment_state: {})
|
|
53
|
+
expect(result).to have_key(:current_chapter)
|
|
54
|
+
expect(result).to have_key(:milestone_count)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe '#arc_stats' do
|
|
59
|
+
it 'returns stats' do
|
|
60
|
+
result = host.arc_stats(agent_id: 'p1')
|
|
61
|
+
expect(result).to have_key(:current_chapter)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-agentic-self
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -309,6 +309,13 @@ files:
|
|
|
309
309
|
- lib/legion/extensions/agentic/self/reflection/helpers/reflection_store.rb
|
|
310
310
|
- lib/legion/extensions/agentic/self/reflection/runners/reflection.rb
|
|
311
311
|
- lib/legion/extensions/agentic/self/reflection/version.rb
|
|
312
|
+
- lib/legion/extensions/agentic/self/relationship_arc.rb
|
|
313
|
+
- lib/legion/extensions/agentic/self/relationship_arc/client.rb
|
|
314
|
+
- lib/legion/extensions/agentic/self/relationship_arc/helpers/arc_engine.rb
|
|
315
|
+
- lib/legion/extensions/agentic/self/relationship_arc/helpers/constants.rb
|
|
316
|
+
- lib/legion/extensions/agentic/self/relationship_arc/helpers/milestone.rb
|
|
317
|
+
- lib/legion/extensions/agentic/self/relationship_arc/runners/relationship_arc.rb
|
|
318
|
+
- lib/legion/extensions/agentic/self/relationship_arc/version.rb
|
|
312
319
|
- lib/legion/extensions/agentic/self/self_model.rb
|
|
313
320
|
- lib/legion/extensions/agentic/self/self_model/client.rb
|
|
314
321
|
- lib/legion/extensions/agentic/self/self_model/helpers/capability.rb
|
|
@@ -416,6 +423,10 @@ files:
|
|
|
416
423
|
- spec/legion/extensions/agentic/self/reflection/helpers/reflection_spec.rb
|
|
417
424
|
- spec/legion/extensions/agentic/self/reflection/helpers/reflection_store_spec.rb
|
|
418
425
|
- spec/legion/extensions/agentic/self/reflection/runners/reflection_spec.rb
|
|
426
|
+
- spec/legion/extensions/agentic/self/relationship_arc/helpers/arc_engine_spec.rb
|
|
427
|
+
- spec/legion/extensions/agentic/self/relationship_arc/helpers/constants_spec.rb
|
|
428
|
+
- spec/legion/extensions/agentic/self/relationship_arc/helpers/milestone_spec.rb
|
|
429
|
+
- spec/legion/extensions/agentic/self/relationship_arc/runners/relationship_arc_spec.rb
|
|
419
430
|
- spec/legion/extensions/agentic/self/self_model/client_spec.rb
|
|
420
431
|
- spec/legion/extensions/agentic/self/self_model/helpers/capability_spec.rb
|
|
421
432
|
- spec/legion/extensions/agentic/self/self_model/helpers/knowledge_domain_spec.rb
|