swarm_sdk 2.7.14 → 3.0.0.alpha2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_sdk/ruby_llm_patches/chat_callbacks_patch.rb +16 -0
  3. data/lib/swarm_sdk/ruby_llm_patches/init.rb +4 -1
  4. data/lib/swarm_sdk/v3/agent.rb +1165 -0
  5. data/lib/swarm_sdk/v3/agent_builder.rb +533 -0
  6. data/lib/swarm_sdk/v3/agent_definition.rb +330 -0
  7. data/lib/swarm_sdk/v3/configuration.rb +490 -0
  8. data/lib/swarm_sdk/v3/debug_log.rb +86 -0
  9. data/lib/swarm_sdk/v3/event_stream.rb +130 -0
  10. data/lib/swarm_sdk/v3/hooks/context.rb +112 -0
  11. data/lib/swarm_sdk/v3/hooks/result.rb +115 -0
  12. data/lib/swarm_sdk/v3/hooks/runner.rb +128 -0
  13. data/lib/swarm_sdk/v3/mcp/connector.rb +183 -0
  14. data/lib/swarm_sdk/v3/mcp/mcp_error.rb +15 -0
  15. data/lib/swarm_sdk/v3/mcp/server_definition.rb +125 -0
  16. data/lib/swarm_sdk/v3/mcp/ssl_http_transport.rb +103 -0
  17. data/lib/swarm_sdk/v3/mcp/stdio_transport.rb +135 -0
  18. data/lib/swarm_sdk/v3/mcp/tool_proxy.rb +53 -0
  19. data/lib/swarm_sdk/v3/memory/adapters/base.rb +297 -0
  20. data/lib/swarm_sdk/v3/memory/adapters/faiss_support.rb +194 -0
  21. data/lib/swarm_sdk/v3/memory/adapters/filesystem_adapter.rb +212 -0
  22. data/lib/swarm_sdk/v3/memory/adapters/sqlite_adapter.rb +507 -0
  23. data/lib/swarm_sdk/v3/memory/adapters/vector_utils.rb +88 -0
  24. data/lib/swarm_sdk/v3/memory/card.rb +206 -0
  25. data/lib/swarm_sdk/v3/memory/cluster.rb +146 -0
  26. data/lib/swarm_sdk/v3/memory/compressor.rb +496 -0
  27. data/lib/swarm_sdk/v3/memory/consolidator.rb +427 -0
  28. data/lib/swarm_sdk/v3/memory/context_builder.rb +339 -0
  29. data/lib/swarm_sdk/v3/memory/edge.rb +105 -0
  30. data/lib/swarm_sdk/v3/memory/embedder.rb +185 -0
  31. data/lib/swarm_sdk/v3/memory/exposure_tracker.rb +104 -0
  32. data/lib/swarm_sdk/v3/memory/ingestion_pipeline.rb +394 -0
  33. data/lib/swarm_sdk/v3/memory/retriever.rb +289 -0
  34. data/lib/swarm_sdk/v3/memory/store.rb +489 -0
  35. data/lib/swarm_sdk/v3/skills/loader.rb +147 -0
  36. data/lib/swarm_sdk/v3/skills/manifest.rb +45 -0
  37. data/lib/swarm_sdk/v3/sub_task_agent.rb +248 -0
  38. data/lib/swarm_sdk/v3/tools/base.rb +80 -0
  39. data/lib/swarm_sdk/v3/tools/bash.rb +174 -0
  40. data/lib/swarm_sdk/v3/tools/clock.rb +32 -0
  41. data/lib/swarm_sdk/v3/tools/document_converters/base.rb +84 -0
  42. data/lib/swarm_sdk/v3/tools/document_converters/docx_converter.rb +120 -0
  43. data/lib/swarm_sdk/v3/tools/document_converters/pdf_converter.rb +111 -0
  44. data/lib/swarm_sdk/v3/tools/document_converters/xlsx_converter.rb +128 -0
  45. data/lib/swarm_sdk/v3/tools/edit.rb +111 -0
  46. data/lib/swarm_sdk/v3/tools/glob.rb +96 -0
  47. data/lib/swarm_sdk/v3/tools/grep.rb +200 -0
  48. data/lib/swarm_sdk/v3/tools/message_teammate.rb +15 -0
  49. data/lib/swarm_sdk/v3/tools/message_user.rb +15 -0
  50. data/lib/swarm_sdk/v3/tools/read.rb +213 -0
  51. data/lib/swarm_sdk/v3/tools/read_tracker.rb +40 -0
  52. data/lib/swarm_sdk/v3/tools/registry.rb +208 -0
  53. data/lib/swarm_sdk/v3/tools/sub_task.rb +183 -0
  54. data/lib/swarm_sdk/v3/tools/think.rb +88 -0
  55. data/lib/swarm_sdk/v3/tools/write.rb +87 -0
  56. data/lib/swarm_sdk/v3.rb +145 -0
  57. metadata +88 -149
  58. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -175
  59. data/lib/swarm_sdk/agent/builder.rb +0 -705
  60. data/lib/swarm_sdk/agent/chat.rb +0 -1438
  61. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -375
  62. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  63. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  64. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -85
  65. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -290
  66. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  67. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  68. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -134
  69. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  70. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -146
  71. data/lib/swarm_sdk/agent/context.rb +0 -115
  72. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  73. data/lib/swarm_sdk/agent/definition.rb +0 -588
  74. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -226
  75. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -173
  76. data/lib/swarm_sdk/agent/tool_registry.rb +0 -189
  77. data/lib/swarm_sdk/agent_registry.rb +0 -146
  78. data/lib/swarm_sdk/builders/base_builder.rb +0 -558
  79. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  80. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -42
  81. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  82. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  83. data/lib/swarm_sdk/config.rb +0 -368
  84. data/lib/swarm_sdk/configuration/parser.rb +0 -397
  85. data/lib/swarm_sdk/configuration/translator.rb +0 -285
  86. data/lib/swarm_sdk/configuration.rb +0 -165
  87. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  88. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -102
  89. data/lib/swarm_sdk/context_compactor.rb +0 -335
  90. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  91. data/lib/swarm_sdk/context_management/context.rb +0 -328
  92. data/lib/swarm_sdk/custom_tool_registry.rb +0 -226
  93. data/lib/swarm_sdk/defaults.rb +0 -251
  94. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  95. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  96. data/lib/swarm_sdk/hooks/context.rb +0 -197
  97. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  98. data/lib/swarm_sdk/hooks/error.rb +0 -29
  99. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  100. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  101. data/lib/swarm_sdk/hooks/result.rb +0 -150
  102. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -256
  103. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  104. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  105. data/lib/swarm_sdk/log_collector.rb +0 -227
  106. data/lib/swarm_sdk/log_stream.rb +0 -127
  107. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  108. data/lib/swarm_sdk/model_aliases.json +0 -8
  109. data/lib/swarm_sdk/models.json +0 -44002
  110. data/lib/swarm_sdk/models.rb +0 -161
  111. data/lib/swarm_sdk/node_context.rb +0 -245
  112. data/lib/swarm_sdk/observer/builder.rb +0 -81
  113. data/lib/swarm_sdk/observer/config.rb +0 -45
  114. data/lib/swarm_sdk/observer/manager.rb +0 -248
  115. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  116. data/lib/swarm_sdk/permissions/config.rb +0 -239
  117. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  118. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  119. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  120. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  121. data/lib/swarm_sdk/plugin.rb +0 -309
  122. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  123. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  124. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -119
  125. data/lib/swarm_sdk/restore_result.rb +0 -65
  126. data/lib/swarm_sdk/result.rb +0 -241
  127. data/lib/swarm_sdk/snapshot.rb +0 -156
  128. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  129. data/lib/swarm_sdk/state_restorer.rb +0 -476
  130. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  131. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -648
  132. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -204
  133. data/lib/swarm_sdk/swarm/builder.rb +0 -256
  134. data/lib/swarm_sdk/swarm/executor.rb +0 -446
  135. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -162
  136. data/lib/swarm_sdk/swarm/lazy_delegate_chat.rb +0 -372
  137. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -361
  138. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -290
  139. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  140. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -392
  141. data/lib/swarm_sdk/swarm.rb +0 -973
  142. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  143. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  144. data/lib/swarm_sdk/tools/base.rb +0 -63
  145. data/lib/swarm_sdk/tools/bash.rb +0 -280
  146. data/lib/swarm_sdk/tools/clock.rb +0 -46
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -389
  148. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  149. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  150. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  151. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  152. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  153. data/lib/swarm_sdk/tools/edit.rb +0 -145
  154. data/lib/swarm_sdk/tools/glob.rb +0 -166
  155. data/lib/swarm_sdk/tools/grep.rb +0 -235
  156. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  157. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -167
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/mcp_tool_stub.rb +0 -198
  160. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  161. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  162. data/lib/swarm_sdk/tools/read.rb +0 -261
  163. data/lib/swarm_sdk/tools/registry.rb +0 -205
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  166. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  167. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  168. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -273
  169. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  170. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  171. data/lib/swarm_sdk/tools/think.rb +0 -100
  172. data/lib/swarm_sdk/tools/todo_write.rb +0 -237
  173. data/lib/swarm_sdk/tools/web_fetch.rb +0 -264
  174. data/lib/swarm_sdk/tools/write.rb +0 -112
  175. data/lib/swarm_sdk/transcript_builder.rb +0 -278
  176. data/lib/swarm_sdk/utils.rb +0 -68
  177. data/lib/swarm_sdk/validation_result.rb +0 -33
  178. data/lib/swarm_sdk/version.rb +0 -5
  179. data/lib/swarm_sdk/workflow/agent_config.rb +0 -95
  180. data/lib/swarm_sdk/workflow/builder.rb +0 -227
  181. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  182. data/lib/swarm_sdk/workflow/node_builder.rb +0 -593
  183. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -250
  184. data/lib/swarm_sdk/workflow.rb +0 -589
  185. data/lib/swarm_sdk.rb +0 -721
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ module V3
5
+ module Memory
6
+ # A self-contained memory unit (<=250 words)
7
+ #
8
+ # Cards are the atomic unit of the memory system. Each card captures
9
+ # a single idea, fact, decision, or concept with enough context to
10
+ # be understood in isolation.
11
+ #
12
+ # @example Create a fact card
13
+ # card = Card.new(
14
+ # text: "The API uses JWT tokens for authentication with RS256 signing.",
15
+ # type: :fact,
16
+ # entities: ["API", "JWT", "RS256"],
17
+ # source_turn_ids: ["turn_001"],
18
+ # )
19
+ class Card
20
+ TYPES = [:fact, :concept, :decision, :constraint, :preference, :incident].freeze
21
+
22
+ # @return [String] Unique card identifier (card_<hex>)
23
+ attr_reader :id
24
+
25
+ # @return [String] Card content (<=250 words, self-contained)
26
+ attr_accessor :text
27
+
28
+ # @return [Symbol] Card type (fact, concept, decision, constraint, preference, incident)
29
+ attr_reader :type
30
+
31
+ # @return [Array<String>] Named entities mentioned in the card
32
+ attr_accessor :entities
33
+
34
+ # @return [Array<String>] Turn IDs that contributed to this card
35
+ attr_accessor :source_turn_ids
36
+
37
+ # @return [Array<Float>, nil] Embedding vector (384 dimensions for MiniLM)
38
+ attr_accessor :embedding
39
+
40
+ # @return [Float] Importance score (0.0-1.0)
41
+ attr_accessor :importance
42
+
43
+ # @return [Float] Confidence score (0.0-1.0, default 1.0)
44
+ attr_accessor :confidence
45
+
46
+ # @return [Integer] Number of times this card was retrieved
47
+ attr_accessor :access_count
48
+
49
+ # @return [Time, nil] Last time this card was accessed
50
+ attr_accessor :last_accessed
51
+
52
+ # @return [Float] Dwell time weight (how long card was in context)
53
+ attr_accessor :dwell
54
+
55
+ # @return [Integer] Compression level (0=raw, 1=summary, 2=bullets, 3=schema, 4=embedding-only)
56
+ attr_accessor :compression_level
57
+
58
+ # @return [String, nil] ID of canonical card if this was merged
59
+ attr_accessor :canonical_id
60
+
61
+ # @return [Time] Creation timestamp
62
+ attr_reader :created_at
63
+
64
+ # @return [Time] Last update timestamp
65
+ attr_accessor :updated_at
66
+
67
+ # Create a new memory card
68
+ #
69
+ # @param text [String] Card content
70
+ # @param type [Symbol] Card type
71
+ # @param entities [Array<String>] Named entities
72
+ # @param source_turn_ids [Array<String>] Contributing turn IDs
73
+ # @param id [String, nil] Card ID (auto-generated if nil)
74
+ # @param embedding [Array<Float>, nil] Embedding vector
75
+ # @param importance [Float] Importance score
76
+ # @param confidence [Float] Confidence score (0.0-1.0)
77
+ # @param access_count [Integer] Access count
78
+ # @param last_accessed [Time, nil] Last access time
79
+ # @param dwell [Float] Dwell time weight
80
+ # @param compression_level [Integer] Compression level (0-4)
81
+ # @param canonical_id [String, nil] Canonical card ID
82
+ # @param created_at [Time, nil] Creation time
83
+ # @param updated_at [Time, nil] Update time
84
+ #
85
+ # @raise [ArgumentError] If type is invalid
86
+ def initialize(
87
+ text:,
88
+ type: :fact,
89
+ entities: [],
90
+ source_turn_ids: [],
91
+ id: nil,
92
+ embedding: nil,
93
+ importance: 0.5,
94
+ confidence: 1.0,
95
+ access_count: 0,
96
+ last_accessed: nil,
97
+ dwell: 0.0,
98
+ compression_level: 0,
99
+ canonical_id: nil,
100
+ created_at: nil,
101
+ updated_at: nil
102
+ )
103
+ @id = id || "card_#{SecureRandom.hex(6)}"
104
+ @text = text
105
+ @type = type.to_sym
106
+ @entities = Array(entities)
107
+ @source_turn_ids = Array(source_turn_ids)
108
+ @embedding = embedding
109
+ @importance = importance.to_f
110
+ @confidence = confidence.to_f
111
+ @access_count = access_count.to_i
112
+ @last_accessed = last_accessed
113
+ @dwell = dwell.to_f
114
+ @compression_level = compression_level.to_i
115
+ @canonical_id = canonical_id
116
+ @created_at = created_at || Time.now
117
+ @updated_at = updated_at || Time.now
118
+
119
+ validate!
120
+ end
121
+
122
+ # Record an access to this card (included in working context)
123
+ #
124
+ # Increments access count, updates last accessed time, and
125
+ # increases dwell weight to track how often this card has
126
+ # been included in the working context.
127
+ #
128
+ # @param dwell_increment [Float] Amount to add to dwell (default: 1.0)
129
+ # @return [void]
130
+ def record_access!(dwell_increment: 1.0)
131
+ @access_count += 1
132
+ @last_accessed = Time.now
133
+ @dwell += dwell_increment
134
+ @updated_at = Time.now
135
+ end
136
+
137
+ # Whether this card has been merged into a canonical card
138
+ #
139
+ # @return [Boolean]
140
+ def merged?
141
+ !@canonical_id.nil?
142
+ end
143
+
144
+ # Serialize to a hash for JSON storage
145
+ #
146
+ # @return [Hash] Serializable representation
147
+ def to_h
148
+ {
149
+ id: @id,
150
+ text: @text,
151
+ type: @type.to_s,
152
+ entities: @entities,
153
+ source_turn_ids: @source_turn_ids,
154
+ embedding: @embedding,
155
+ importance: @importance,
156
+ confidence: @confidence,
157
+ access_count: @access_count,
158
+ last_accessed: @last_accessed&.iso8601,
159
+ dwell: @dwell,
160
+ compression_level: @compression_level,
161
+ canonical_id: @canonical_id,
162
+ created_at: @created_at.iso8601,
163
+ updated_at: @updated_at.iso8601,
164
+ }
165
+ end
166
+
167
+ class << self
168
+ # Deserialize from a hash
169
+ #
170
+ # @param hash [Hash] Serialized card data
171
+ # @return [Card]
172
+ def from_h(hash)
173
+ hash = hash.transform_keys(&:to_sym)
174
+ new(
175
+ id: hash[:id],
176
+ text: hash[:text],
177
+ type: hash[:type]&.to_sym || :fact,
178
+ entities: hash[:entities] || [],
179
+ source_turn_ids: hash[:source_turn_ids] || [],
180
+ embedding: hash[:embedding],
181
+ importance: hash[:importance] || 0.5,
182
+ confidence: hash[:confidence] || 1.0,
183
+ access_count: hash[:access_count] || 0,
184
+ last_accessed: hash[:last_accessed] ? Time.parse(hash[:last_accessed]) : nil,
185
+ dwell: hash[:dwell] || 0.0,
186
+ compression_level: hash[:compression_level] || 0,
187
+ canonical_id: hash[:canonical_id],
188
+ created_at: hash[:created_at] ? Time.parse(hash[:created_at]) : nil,
189
+ updated_at: hash[:updated_at] ? Time.parse(hash[:updated_at]) : nil,
190
+ )
191
+ end
192
+ end
193
+
194
+ private
195
+
196
+ # @raise [ArgumentError] If type is invalid
197
+ def validate!
198
+ raise ArgumentError, "Invalid card type: #{@type}. Must be one of: #{TYPES.join(", ")}" unless TYPES.include?(@type)
199
+ raise ArgumentError, "Card text is required" if @text.nil? || @text.strip.empty?
200
+ raise ArgumentError, "Confidence must be 0.0-1.0" unless (0.0..1.0).cover?(@confidence)
201
+ raise ArgumentError, "Compression level must be 0-4" unless (0..4).cover?(@compression_level)
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SwarmSDK
4
+ module V3
5
+ module Memory
6
+ # A topic cluster grouping related memory cards
7
+ #
8
+ # Clusters provide a higher-level organization of memory cards
9
+ # by topic. Each cluster maintains a rolling summary and tracks
10
+ # its member cards.
11
+ #
12
+ # @example Create a cluster
13
+ # cluster = Cluster.new(
14
+ # title: "Authentication System",
15
+ # rolling_summary: "The project uses JWT-based auth with RS256 signing...",
16
+ # card_ids: ["card_a1b2c3", "card_d4e5f6"],
17
+ # )
18
+ class Cluster
19
+ # @return [String] Unique cluster identifier
20
+ attr_reader :id
21
+
22
+ # @return [String] Cluster title/topic
23
+ attr_accessor :title
24
+
25
+ # @return [Array<Float>, nil] Cluster centroid embedding
26
+ attr_accessor :embedding
27
+
28
+ # @return [String] Rolling summary of cluster contents
29
+ attr_accessor :rolling_summary
30
+
31
+ # @return [Array<String>] Decision log entries
32
+ attr_accessor :decision_log
33
+
34
+ # @return [Array<String>] Key entities in this cluster
35
+ attr_accessor :key_entities
36
+
37
+ # @return [Array<String>] Member card IDs
38
+ attr_accessor :card_ids
39
+
40
+ # @return [Time] Creation timestamp
41
+ attr_reader :created_at
42
+
43
+ # @return [Time] Last update timestamp
44
+ attr_accessor :updated_at
45
+
46
+ # Create a new cluster
47
+ #
48
+ # @param title [String] Cluster title
49
+ # @param id [String, nil] Cluster ID (auto-generated if nil)
50
+ # @param embedding [Array<Float>, nil] Centroid embedding
51
+ # @param rolling_summary [String] Summary of contents
52
+ # @param decision_log [Array<String>] Decision entries
53
+ # @param key_entities [Array<String>] Key entities
54
+ # @param card_ids [Array<String>] Member card IDs
55
+ # @param created_at [Time, nil] Creation time
56
+ # @param updated_at [Time, nil] Update time
57
+ def initialize(
58
+ title:,
59
+ id: nil,
60
+ embedding: nil,
61
+ rolling_summary: "",
62
+ decision_log: [],
63
+ key_entities: [],
64
+ card_ids: [],
65
+ created_at: nil,
66
+ updated_at: nil
67
+ )
68
+ @id = id || "cluster_#{SecureRandom.hex(6)}"
69
+ @title = title
70
+ @embedding = embedding
71
+ @rolling_summary = rolling_summary
72
+ @decision_log = Array(decision_log)
73
+ @key_entities = Array(key_entities)
74
+ @card_ids = Array(card_ids)
75
+ @created_at = created_at || Time.now
76
+ @updated_at = updated_at || Time.now
77
+ end
78
+
79
+ # Add a card to this cluster
80
+ #
81
+ # @param card_id [String] Card ID to add
82
+ # @return [void]
83
+ def add_card(card_id)
84
+ return if @card_ids.include?(card_id)
85
+
86
+ @card_ids << card_id
87
+ @updated_at = Time.now
88
+ end
89
+
90
+ # Remove a card from this cluster
91
+ #
92
+ # @param card_id [String] Card ID to remove
93
+ # @return [void]
94
+ def remove_card(card_id)
95
+ @card_ids.delete(card_id)
96
+ @updated_at = Time.now
97
+ end
98
+
99
+ # Number of cards in this cluster
100
+ #
101
+ # @return [Integer]
102
+ def size
103
+ @card_ids.size
104
+ end
105
+
106
+ # Serialize to a hash
107
+ #
108
+ # @return [Hash]
109
+ def to_h
110
+ {
111
+ id: @id,
112
+ title: @title,
113
+ embedding: @embedding,
114
+ rolling_summary: @rolling_summary,
115
+ decision_log: @decision_log,
116
+ key_entities: @key_entities,
117
+ card_ids: @card_ids,
118
+ created_at: @created_at.iso8601,
119
+ updated_at: @updated_at.iso8601,
120
+ }
121
+ end
122
+
123
+ class << self
124
+ # Deserialize from a hash
125
+ #
126
+ # @param hash [Hash] Serialized cluster data
127
+ # @return [Cluster]
128
+ def from_h(hash)
129
+ hash = hash.transform_keys(&:to_sym)
130
+ new(
131
+ id: hash[:id],
132
+ title: hash[:title],
133
+ embedding: hash[:embedding],
134
+ rolling_summary: hash[:rolling_summary] || "",
135
+ decision_log: hash[:decision_log] || [],
136
+ key_entities: hash[:key_entities] || [],
137
+ card_ids: hash[:card_ids] || [],
138
+ created_at: hash[:created_at] ? Time.parse(hash[:created_at]) : nil,
139
+ updated_at: hash[:updated_at] ? Time.parse(hash[:updated_at]) : nil,
140
+ )
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end