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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 478de6acedf6c72ba3a5c715bc57f3124505486720b473b2db536d4abe48c58c
4
- data.tar.gz: 8846a168b7d882d7e981a3d87eb87565e25b10ac24530221748382fdda1128cb
3
+ metadata.gz: '081b534c7eaf4efbc0265eada2d3b8f11b37b93a3fb34be77068d0ce4f0e842d'
4
+ data.tar.gz: ce8a3839f617dc41d904ccf54a98a208037178cd89eefb39cd031fce8a89e315
5
5
  SHA512:
6
- metadata.gz: 20df44e864e415b127e1f26a0d28bb0ed19c6589b53a40afbace9587c1166be962b6b8305e74c7ad4d4ea6b41eb9091f90328cbc3e23c3ab44ab9172cced706f
7
- data.tar.gz: c768c0cc1794a6502e37d2c6524feaec104c8d47116e654f7d5dd19330768130212516cc074011b97b4e8e97f21c33931242e4cebf9ce0fba92064919bf7396d
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,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Self
7
+ module RelationshipArc
8
+ VERSION = '0.1.0'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ 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
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Agentic
6
6
  module Self
7
- VERSION = '0.1.9'
7
+ VERSION = '0.1.10'
8
8
  end
9
9
  end
10
10
  end
@@ -17,6 +17,7 @@ require_relative 'self/agency'
17
17
  require_relative 'self/reflection'
18
18
  require_relative 'self/anosognosia'
19
19
  require_relative 'self/default_mode_network'
20
+ require_relative 'self/relationship_arc'
20
21
 
21
22
  module Legion
22
23
  module Extensions
@@ -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.9
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