lex-dream 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 18c812c7de44c6dd1a06bc41c54e04cc03d88d745f47da1d72765c4d399a342f
4
+ data.tar.gz: 90f655a9201fe82647715d70564df33e6a29b633e2cea4857bb04316719780b6
5
+ SHA512:
6
+ metadata.gz: 98b2744be8165cda9ed07087ba01ad57acb3b2dcf3179071923d265a71688c1b478e8ca5b0a029224db09254b756b856bf07de63c3ac321f85b3419ce0bbe675
7
+ data.tar.gz: 4cb73181be977b2681afa77002849ac000c6758b9d716d7064d507358a07cfaafd1cca30ca6fc061f247ef93bd639401a1a111d403a2815aeee6b87fff59f77a
@@ -0,0 +1,16 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+
7
+ jobs:
8
+ ci:
9
+ uses: LegionIO/.github/.github/workflows/ci.yml@main
10
+
11
+ release:
12
+ needs: ci
13
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
14
+ uses: LegionIO/.github/.github/workflows/release.yml@main
15
+ secrets:
16
+ rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.gem
10
+ .rspec_status
11
+ Gemfile.lock
12
+ logs/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,56 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.4
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+
6
+ Layout/LineLength:
7
+ Max: 160
8
+
9
+ Layout/SpaceAroundEqualsInParameterDefault:
10
+ EnforcedStyle: space
11
+
12
+ Layout/HashAlignment:
13
+ EnforcedHashRocketStyle: table
14
+ EnforcedColonStyle: table
15
+
16
+ Metrics/MethodLength:
17
+ Max: 50
18
+
19
+ Metrics/ClassLength:
20
+ Max: 1500
21
+
22
+ Metrics/ModuleLength:
23
+ Max: 1500
24
+
25
+ Metrics/BlockLength:
26
+ Max: 40
27
+ Exclude:
28
+ - 'spec/**/*'
29
+
30
+ Metrics/AbcSize:
31
+ Max: 60
32
+
33
+ Metrics/CyclomaticComplexity:
34
+ Max: 15
35
+
36
+ Metrics/PerceivedComplexity:
37
+ Max: 17
38
+
39
+ Style/Documentation:
40
+ Enabled: false
41
+
42
+ Style/SymbolArray:
43
+ Enabled: true
44
+
45
+ Style/FrozenStringLiteralComment:
46
+ Enabled: true
47
+ EnforcedStyle: always
48
+
49
+ Naming/FileName:
50
+ Enabled: false
51
+
52
+ Naming/PredicateMethod:
53
+ Enabled: false
54
+
55
+ Gemspec/DevelopmentDependencies:
56
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ## [0.1.1] - 2026-03-16
6
+
7
+ ### Fixed
8
+ - Guard `DreamCycle#memory` with `defined?` check and use `Memory::Client.new` instead of fragile `Object.new.extend` pattern
9
+ - Guard `DreamCycle#identity` with `defined?` check to prevent `NoMethodError` when lex-identity is not loaded
10
+ - Early return in `execute_dream_cycle` when lex-memory is unavailable (logs warning instead of crashing every 5 minutes)
11
+ - Guard `Dream::Client#initialize` against missing Memory, Identity, and Emotion extensions
12
+
13
+ ### Added
14
+ - `spec/legion/extensions/dream/actors/dream_cycle_spec.rb` (7 examples) — tests for the DreamCycle actor (Every 300s)
15
+
16
+ ## [0.1.0] - 2026-03-13
17
+
18
+ ### Added
19
+ - Initial release
data/CLAUDE.md ADDED
@@ -0,0 +1,152 @@
1
+ # lex-dream
2
+
3
+ **Level 3 Documentation**
4
+ - **Parent**: `/Users/miverso2/rubymine/legion/extensions-agentic/CLAUDE.md`
5
+ - **Grandparent**: `/Users/miverso2/rubymine/legion/CLAUDE.md`
6
+
7
+ ## Purpose
8
+
9
+ Autonomous dream cycle for the LegionIO cognitive architecture. When the agent is idle, it enters `dormant_active` mode and runs an eight-phase internal consolidation cycle: memory audit, association walking, contradiction resolution, identity entropy assessment, agenda formation, consolidation commit, dream reflection, and dream narration. Dream output feeds back into lex-memory as semantic traces that surface organically through normal retrieval — no explicit surfacing mechanism.
10
+
11
+ ## Gem Info
12
+
13
+ - **Gem name**: `lex-dream`
14
+ - **Version**: `0.1.1`
15
+ - **Module**: `Legion::Extensions::Dream`
16
+ - **Ruby**: `>= 3.4`
17
+ - **License**: MIT
18
+
19
+ ## File Structure
20
+
21
+ ```
22
+ lib/legion/extensions/dream/
23
+ version.rb
24
+ actors/
25
+ dream_cycle.rb # Periodic actor (Every 300s) — calls execute_dream_cycle
26
+ helpers/
27
+ constants.rb # Configuration defaults, DREAM_CYCLE_PHASES list, agenda item types
28
+ dream_store.rb # In-memory store for agenda, walk results, contradictions, entropy
29
+ association_walker.rb # Novelty-scored multi-hop association traversal + start trace selection
30
+ contradiction_detector.rb # Domain-tag overlap + valence divergence detection and resolution
31
+ agenda.rb # Phase output synthesis and semantic trace conversion
32
+ llm_enhancer.rb # Optional LLM integration for contradiction resolution, agenda synthesis, and journal narration
33
+ dream_journal.rb # Writes human-readable dream journal entries to logs/dreams/
34
+ runners/
35
+ dream_cycle.rb # Eight-phase dream cycle runner (all phase methods)
36
+ client.rb # Client with dependency injection for memory/identity/emotion
37
+ spec/
38
+ legion/extensions/dream/
39
+ actors/
40
+ dream_cycle_spec.rb
41
+ helpers/
42
+ dream_store_spec.rb
43
+ association_walker_spec.rb
44
+ contradiction_detector_spec.rb
45
+ agenda_spec.rb
46
+ runners/
47
+ dream_cycle_spec.rb
48
+ client_spec.rb
49
+ integration_spec.rb
50
+ ```
51
+
52
+ ## Key Constants (Helpers::Constants)
53
+
54
+ ```ruby
55
+ ASSOCIATION_WALK_HOPS = 12 # max BFS depth
56
+ ASSOCIATION_NOVELTY_THRESHOLD = 0.65 # min novelty to log a walk result
57
+ CONTRADICTION_RESOLUTION_STRATEGY = :recency_weighted # or :intensity_weighted
58
+ ENTROPY_WINDOW = 7 # sessions for identity entropy trend
59
+ AGENDA_MAX_ITEMS = 5 # max agenda items carried forward
60
+ DREAM_PARTITION_TTL = 604_800 # 7 days before unrecalled items expire
61
+ AGENDA_ITEM_TYPES = %i[unresolved surfacing curious corrective]
62
+ ```
63
+
64
+ ## The Eight Dream Phases
65
+
66
+ All phases execute sequentially via direct Ruby method calls (not queued through transport). Phases are defined in `Constants::DREAM_CYCLE_PHASES`.
67
+
68
+ | Phase | Method | What It Does |
69
+ |---|---|---|
70
+ | 1. Memory Audit | `phase_memory_audit` | Runs decay + tier migration, marks consolidation candidates, collects unresolved traces using `EMERGENT_UNRESOLVED` heuristics |
71
+ | 2. Association Walk | `phase_association_walk` | Selects highest-salience unresolved trace, BFS walks associations with novelty scoring, materializes novel links via `hebbian_link` |
72
+ | 3. Contradiction Resolution | `phase_contradiction_resolution` | Detects opposing-valence traces by domain overlap; resolves via LLM if available, otherwise falls back to recency/intensity weighting |
73
+ | 4. Identity Entropy Check | `phase_identity_entropy_check` | Calls `identity.check_entropy`, records result in DreamStore, flags drift as corrective agenda item |
74
+ | 5. Agenda Formation | `phase_agenda_formation` | Synthesizes phases 1-4 into typed, weighted agenda items; uses LLM synthesis if available, mechanical fallback otherwise |
75
+ | 6. Consolidation Commit | `phase_consolidation_commit` | Converts agenda to semantic traces in lex-memory, clears unresolved flags, expires stale dream state, flushes cache store |
76
+ | 7. Dream Reflection | `phase_dream_reflection` | Calls `lex-reflection` to assess cognitive health of the dream cycle (skipped if extension not loaded) |
77
+ | 8. Dream Narration | `phase_dream_narration` | Calls `lex-narrator` to produce a narrative summary (skipped if extension not loaded) |
78
+
79
+ ## DreamStore
80
+
81
+ In-memory store owned entirely by lex-dream. The agent's private journal:
82
+
83
+ - `agenda` — weighted orientation items (`:unresolved`, `:surfacing`, `:curious`, `:corrective`)
84
+ - `walk_results` — novel association paths with novelty scores
85
+ - `contradictions` — resolution logs with domain and outcome
86
+ - `entropy_history` — identity entropy snapshots
87
+
88
+ Items expire via `DREAM_PARTITION_TTL`. Oldest agenda items drop when `AGENDA_MAX_ITEMS` exceeded.
89
+
90
+ ## LLM Enhancement
91
+
92
+ `Helpers::LlmEnhancer` provides optional LLM-enhanced processing:
93
+ - `available?` — returns true when `Legion::LLM` is started
94
+ - `resolve_contradiction(trace_a, trace_b, strategy:)` — prompts LLM to reason about which trace is more reliable; falls back to mechanical resolution if unavailable or on error
95
+ - `synthesize_agenda(unresolved_traces:, contradictions:, walk_results:, entropy:)` — prompts LLM to synthesize agenda items from all phase data; mechanical fallback via `Helpers::Agenda.build_from_phases`
96
+ - `narrate_journal(results, phase_data)` — prompts LLM to write a 3-5 paragraph analytical narrative for the dream journal; called by `DreamJournal.section_narrative` when LLM is available
97
+
98
+ The system prompt instructs the LLM to act as the agent's internal dream processor: concise, analytical, structured reasoning only.
99
+
100
+ ## Dream Journal
101
+
102
+ `Helpers::DreamJournal.write_entry(results:, phase_data:, dream_store:)` is called after all phases complete but before dream state is cleared. It writes a human-readable Markdown journal entry to `<Dir.pwd>/logs/dreams/dream-<timestamp>.md`. When LLM is available, `LlmEnhancer.narrate_journal` prepends an analytical reflection section before the phase-by-phase breakdown.
103
+
104
+ ## Client
105
+
106
+ ```ruby
107
+ Client.new(memory:, identity:, emotion:)
108
+ ```
109
+
110
+ Dependency clients initialized once at boot, held for process lifetime. Defaults to creating its own instances if none injected. The `dream_store` is public (`attr_reader`). All other state is private.
111
+
112
+ ## Organic Recall — How Dream Output Reaches Active Sessions
113
+
114
+ No explicit surfacing mechanism. Dream output feeds back through normal lex-memory retrieval:
115
+
116
+ - Novel associations materialized as Hebbian links → participate in `retrieve_ranked`
117
+ - Agenda items become semantic traces with `dream:*` domain tags and high emotional intensity
118
+ - Reinforced traces have boosted strength → win retrieval ranking
119
+ - The agent doesn't "decide to bring something up" — relevant dream-formed knowledge surfaces contextually
120
+
121
+ ## Integration Points
122
+
123
+ - **lex-tick**: Dream runs in `dormant_active` mode. Eight dream phases registered in `MODE_PHASES[:dormant_active]`. Tick budget is uncapped (`Float::INFINITY`).
124
+ - **lex-memory**: Direct calls to `decay_cycle`, `migrate_tier`, `hebbian_link`. Calls `CacheStore#reload` before the cycle starts (picks up traces written by other processes). Calls `CacheStore#flush` after the cycle completes.
125
+ - **lex-identity**: Calls `check_entropy` during phase 4 for drift detection.
126
+ - **lex-emotion**: Uses emotional intensity/valence from traces for salience ranking and contradiction detection.
127
+ - **lex-reflection**: Phase 7 delegates to `lex-reflection` for cognitive health assessment (optional, skipped if not loaded).
128
+ - **lex-narrator**: Phase 8 delegates to `lex-narrator` for dream narrative generation (optional, skipped if not loaded).
129
+ - **legion-llm**: LlmEnhancer checks `Legion::LLM.started?` to determine if LLM-enhanced resolution/synthesis is available.
130
+
131
+ ## Transition Rules (in lex-tick)
132
+
133
+ ```
134
+ dormant → dormant_active: no signals for 1800s (DREAM_IDLE_THRESHOLD)
135
+ dormant_active → sentinel: high-salience signal (>= 0.7) or human_direct
136
+ dormant_active → dormant: dream cycle completes (dream_complete: true)
137
+ sentinel → dormant_active: no signals for 600s (SENTINEL_TO_DREAM_THRESHOLD)
138
+ ```
139
+
140
+ ## Development Notes
141
+
142
+ - Actor namespace is `module Actor` (singular), matching the pattern used in other agentic extensions
143
+ - Actor runner function is `execute_dream_cycle` (not `run_dream_cycle`)
144
+ - `memory.send(:default_store)` needed because `default_store` is private on the memory runner
145
+ - `store.reload` and `store.flush` are called defensively with `respond_to?` guards (only implemented by `CacheStore`, not `Store`)
146
+ - `EMERGENT_UNRESOLVED` lambda defines five heuristics for finding unprocessed traces beyond the simple `unresolved: true` flag — episodic with zero reinforcement and high emotion, low-confidence unreinforced, negative-valence semantic/procedural, high-intensity unreinforced
147
+ - Phase methods collect data into `@phase_data` for use by later phases and for the dream journal
148
+ - `@phase_data[:agenda_snapshot]` is taken at the start of `phase_consolidation_commit` before `dream_store.clear` is called, preserving the snapshot for the dream journal
149
+ - `dream_store.clear` is called at the end of `consolidation_commit` to reset state for the next dream cycle
150
+ - Dream reflection and narration phases return `{ status: :skipped, reason: :extension_not_loaded }` when their dependencies are absent — the cycle completes normally
151
+ - Guards: `memory` uses `defined?(Legion::Extensions::Memory::Client)`, `identity` uses `defined?(Legion::Extensions::Identity::Runners::Identity)` — avoids crashes when extensions are not loaded
152
+ - All state is in-memory (consistent with v0.1.1 pattern across agentic extensions)
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ group :test do
8
+ gem 'lex-emotion', path: '../lex-emotion'
9
+ gem 'lex-identity', path: '../lex-identity'
10
+ gem 'lex-memory', path: '../lex-memory'
11
+ gem 'lex-tick', path: '../lex-tick'
12
+ gem 'rspec', '~> 3.0'
13
+ gem 'rubocop', require: false
14
+ gem 'rubocop-rspec', require: false
15
+ end
16
+
17
+ gem 'legion-gaia', path: '../../legion-gaia'
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # lex-dream
2
+
3
+ Autonomous dream cycle for the [LegionIO](https://github.com/LegionIO/LegionIO) cognitive architecture. When the agent is idle, it runs an eight-phase internal consolidation cycle: memory audit, association walking, contradiction resolution, identity entropy assessment, agenda formation, consolidation commit, dream reflection, and dream narration.
4
+
5
+ ## Installation
6
+
7
+ Add to your Gemfile:
8
+
9
+ ```ruby
10
+ gem 'lex-dream'
11
+ ```
12
+
13
+ ## How It Works
14
+
15
+ Dream output feeds back into lex-memory as semantic traces with `dream:*` domain tags. These surface organically through normal retrieval ranking -- no explicit surfacing mechanism needed.
16
+
17
+ ### The Eight Dream Phases
18
+
19
+ | Phase | What It Does |
20
+ |-------|-------------|
21
+ | Memory Audit | Runs decay + tier migration, marks consolidation candidates, collects unresolved traces |
22
+ | Association Walk | BFS walks associations from highest-salience unresolved trace, materializes novel Hebbian links |
23
+ | Contradiction Resolution | Detects opposing-valence traces by domain; resolves via LLM if available, else mechanical fallback |
24
+ | Identity Entropy Check | Calls lex-identity entropy assessment, flags drift as corrective agenda item |
25
+ | Agenda Formation | Synthesizes phases 1-4 into typed, weighted agenda items (LLM synthesis when available) |
26
+ | Consolidation Commit | Converts agenda to semantic traces in lex-memory, clears dream state |
27
+ | Dream Reflection | Assesses cognitive health of the dream cycle via lex-reflection (skipped if not loaded) |
28
+ | Dream Narration | Produces a narrative summary via lex-narrator (skipped if not loaded) |
29
+
30
+ ### Agenda Item Types
31
+
32
+ - `:unresolved` -- traces that need attention
33
+ - `:surfacing` -- unresolvable contradictions
34
+ - `:curious` -- novel associations discovered during walk
35
+ - `:corrective` -- identity entropy drift detected
36
+
37
+ ## Dependencies
38
+
39
+ - `lex-memory` -- trace storage, decay, Hebbian linking, cache reload/flush
40
+ - `lex-identity` -- entropy assessment
41
+ - `lex-emotion` -- emotional intensity for salience ranking
42
+ - `lex-reflection` -- optional cognitive health assessment (dream_reflection phase)
43
+ - `lex-narrator` -- optional narrative generation (dream_narration phase)
44
+ - `legion-llm` -- optional LLM enhancement for contradiction resolution, agenda synthesis, and journal narration
45
+
46
+ ## Development
47
+
48
+ ```bash
49
+ bundle install
50
+ bundle exec rspec
51
+ bundle exec rubocop
52
+ ```
53
+
54
+ ## License
55
+
56
+ MIT
data/lex-dream.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/dream/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-dream'
7
+ spec.version = Legion::Extensions::Dream::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX Dream'
12
+ spec.description = 'Autonomous dream cycle for brain-modeled agentic AI — memory consolidation, ' \
13
+ 'association walking, contradiction resolution, and agenda formation'
14
+ spec.homepage = 'https://github.com/LegionIO/lex-dream'
15
+ spec.license = 'MIT'
16
+ spec.required_ruby_version = '>= 3.4'
17
+
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-dream'
20
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-dream'
21
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-dream'
22
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-dream/issues'
23
+ spec.metadata['rubygems_mfa_required'] = 'true'
24
+
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ end
28
+ spec.require_paths = ['lib']
29
+ spec.add_development_dependency 'legion-gaia'
30
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/actors/every'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Dream
8
+ module Actor
9
+ class DreamCycle < Legion::Extensions::Actors::Every
10
+ def runner_class
11
+ Legion::Extensions::Dream::Runners::DreamCycle
12
+ end
13
+
14
+ def runner_function
15
+ 'execute_dream_cycle'
16
+ end
17
+
18
+ def time
19
+ 300
20
+ end
21
+
22
+ def run_now?
23
+ false
24
+ end
25
+
26
+ def use_runner?
27
+ false
28
+ end
29
+
30
+ def check_subtask?
31
+ false
32
+ end
33
+
34
+ def generate_task?
35
+ false
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Dream
6
+ class Client
7
+ include Runners::DreamCycle
8
+
9
+ attr_reader :dream_store
10
+
11
+ def initialize(memory: nil, identity: nil, emotion: nil, **)
12
+ @memory = memory || (Legion::Extensions::Memory::Client.new if defined?(Legion::Extensions::Memory::Client))
13
+ @identity = identity || (Legion::Extensions::Identity::Client.new if defined?(Legion::Extensions::Identity::Client))
14
+ @emotion = emotion || (Legion::Extensions::Emotion::Client.new if defined?(Legion::Extensions::Emotion::Client))
15
+ @dream_store = Helpers::DreamStore.new
16
+ @phase_data = {}
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :memory, :identity, :emotion, :phase_data
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Dream
6
+ module Helpers
7
+ module Agenda
8
+ module_function
9
+
10
+ def build_from_phases(phase_outputs)
11
+ now = Time.now.utc
12
+
13
+ items = Array(phase_outputs[:unresolved_traces]).map do |trace|
14
+ {
15
+ type: :unresolved,
16
+ content: { trace_id: trace[:trace_id] },
17
+ weight: trace[:emotional_intensity],
18
+ created_at: now
19
+ }
20
+ end
21
+
22
+ Array(phase_outputs[:contradictions]).each do |contradiction|
23
+ next unless contradiction[:resolution] == :unresolvable
24
+
25
+ items << {
26
+ type: :surfacing,
27
+ content: { trace_ids: contradiction[:trace_ids], domain: contradiction[:domain] },
28
+ weight: 0.7,
29
+ created_at: now
30
+ }
31
+ end
32
+
33
+ Array(phase_outputs[:walk_results]).each do |result|
34
+ items << {
35
+ type: :curious,
36
+ content: { trace_id: result[:trace_id], path: result[:path] },
37
+ weight: result[:novelty_score],
38
+ created_at: now
39
+ }
40
+ end
41
+
42
+ entropy = phase_outputs[:entropy] || {}
43
+ if entropy[:classification] == :high_entropy && entropy[:trend] == :rising
44
+ items << {
45
+ type: :corrective,
46
+ content: { classification: entropy[:classification], trend: entropy[:trend] },
47
+ weight: entropy[:entropy],
48
+ created_at: now
49
+ }
50
+ end
51
+
52
+ items
53
+ end
54
+
55
+ def to_semantic_traces(agenda_items)
56
+ agenda_items.map do |item|
57
+ Legion::Extensions::Memory::Helpers::Trace.new_trace(
58
+ type: :semantic,
59
+ content_payload: { dream_agenda: item[:type], **item[:content] },
60
+ emotional_intensity: item[:weight],
61
+ domain_tags: ["dream:#{item[:type]}"],
62
+ origin: :direct_experience
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Dream
6
+ module Helpers
7
+ module AssociationWalker
8
+ module_function
9
+
10
+ def walk(store:, start_id:, max_hops: Constants::ASSOCIATION_WALK_HOPS, # rubocop:disable Metrics/ParameterLists
11
+ novelty_threshold: Constants::ASSOCIATION_NOVELTY_THRESHOLD,
12
+ known_paths: Set.new, **)
13
+ raw = store.walk_associations(start_id: start_id, max_hops: max_hops)
14
+
15
+ raw.filter_map do |result|
16
+ score = compute_novelty(
17
+ path: result[:path],
18
+ depth: result[:depth],
19
+ store: store,
20
+ known_paths: known_paths
21
+ )
22
+ next if score < novelty_threshold
23
+
24
+ result.merge(novelty_score: score)
25
+ end
26
+ end
27
+
28
+ def select_start_trace(store:)
29
+ # Prefer episodic unresolved traces, fall back to any high-intensity unresolved
30
+ candidates = store.all_traces.select do |t|
31
+ t[:unresolved] == true ||
32
+ (t[:trace_type] == :episodic && t[:reinforcement_count].zero? && t[:emotional_intensity] >= 0.5) ||
33
+ (t[:confidence].is_a?(Numeric) && t[:confidence] < 0.4 && t[:reinforcement_count].zero?)
34
+ end
35
+ candidates.max_by { |t| t[:emotional_intensity] }
36
+ end
37
+
38
+ def compute_novelty(path:, depth:, store:, known_paths:)
39
+ path_key = path.join('->')
40
+ return 0.0 if known_paths.include?(path_key)
41
+
42
+ depth_factor = depth / Constants::ASSOCIATION_WALK_HOPS.to_f
43
+
44
+ type_diversity = path.map { |id| store.get(id)&.dig(:trace_type) }.compact.uniq.size
45
+ type_factor = type_diversity / path.size.to_f
46
+
47
+ ((depth_factor * 0.4) + (type_factor * 0.6)).clamp(0.0, 1.0)
48
+ end
49
+
50
+ private :compute_novelty
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Dream
6
+ module Helpers
7
+ module Constants
8
+ ASSOCIATION_WALK_HOPS = 12
9
+ ASSOCIATION_NOVELTY_THRESHOLD = 0.65
10
+ CONTRADICTION_RESOLUTION_STRATEGY = :recency_weighted
11
+ ENTROPY_WINDOW = 7
12
+ AGENDA_MAX_ITEMS = 5
13
+ DREAM_PARTITION_TTL = 604_800
14
+ AGENDA_ITEM_TYPES = %i[unresolved surfacing curious corrective].freeze
15
+
16
+ DREAM_CYCLE_PHASES = %i[
17
+ memory_audit
18
+ association_walk
19
+ contradiction_resolution
20
+ identity_entropy_check
21
+ agenda_formation
22
+ consolidation_commit
23
+ dream_reflection
24
+ dream_narration
25
+ ].freeze
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end