activematrix 0.0.4 → 0.0.7

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.
@@ -0,0 +1,318 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveMatrix
4
+ module Protocols
5
+ module CS
6
+ # Handles message relationships (replies, edits, reactions, threads)
7
+ # @see https://spec.matrix.org/latest/client-server-api/#relationships
8
+ module MessageRelationships
9
+ # Send a message as a reply to another event
10
+ #
11
+ # @param room_id [String] The room ID
12
+ # @param event_id [String] The event ID to reply to
13
+ # @param content [Hash,String] The message content
14
+ # @param msgtype [String] The message type (defaults to 'm.text')
15
+ # @param params [Hash] Additional parameters for send_message_event
16
+ # @return [Response] The response from the server
17
+ #
18
+ # @example Reply to a message
19
+ # api.reply_to(room_id, event_id, "I agree!")
20
+ #
21
+ # @example Reply with custom msgtype
22
+ # api.reply_to(room_id, event_id, { url: "mxc://..." }, msgtype: 'm.image')
23
+ def reply_to(room_id, event_id, content, msgtype: 'm.text', **params)
24
+ content = { body: content } if content.is_a?(String)
25
+ content[:msgtype] ||= msgtype
26
+
27
+ # Add the reply relationship
28
+ content[:'m.relates_to'] = {
29
+ 'm.in_reply_to' => {
30
+ event_id: event_id
31
+ }
32
+ }
33
+
34
+ send_message_event(room_id, 'm.room.message', content, **params)
35
+ end
36
+
37
+ # Send a threaded message
38
+ #
39
+ # @param room_id [String] The room ID
40
+ # @param thread_root_id [String] The root event ID of the thread
41
+ # @param content [Hash,String] The message content
42
+ # @param msgtype [String] The message type (defaults to 'm.text')
43
+ # @param latest_event_id [String,nil] The latest event in the thread (for fallback)
44
+ # @param include_fallback [Boolean] Include reply fallback for older clients
45
+ # @param params [Hash] Additional parameters for send_message_event
46
+ # @return [Response] The response from the server
47
+ #
48
+ # @example Send a threaded message
49
+ # api.send_threaded_message(room_id, root_event_id, "This is a thread reply")
50
+ #
51
+ # @example Send a threaded message with fallback
52
+ # api.send_threaded_message(room_id, root_id, "Reply", latest_event_id: last_id, include_fallback: true)
53
+ def send_threaded_message(room_id, thread_root_id, content, msgtype: 'm.text',
54
+ latest_event_id: nil, include_fallback: false, **params)
55
+ content = { body: content } if content.is_a?(String)
56
+ content[:msgtype] ||= msgtype
57
+
58
+ # Build the thread relationship
59
+ relates_to = {
60
+ rel_type: 'm.thread',
61
+ event_id: thread_root_id
62
+ }
63
+
64
+ # Add fallback for older clients if requested
65
+ if include_fallback && latest_event_id
66
+ relates_to[:'m.in_reply_to'] = {
67
+ event_id: latest_event_id
68
+ }
69
+ relates_to[:is_falling_back] = true
70
+ end
71
+
72
+ content[:'m.relates_to'] = relates_to
73
+ send_message_event(room_id, 'm.room.message', content, **params)
74
+ end
75
+
76
+ # Edit an existing message
77
+ #
78
+ # @param room_id [String] The room ID
79
+ # @param event_id [String] The event ID to edit
80
+ # @param new_content [Hash,String] The new message content
81
+ # @param msgtype [String] The message type (defaults to 'm.text')
82
+ # @param params [Hash] Additional parameters for send_message_event
83
+ # @return [Response] The response from the server
84
+ #
85
+ # @example Edit a text message
86
+ # api.edit_message(room_id, event_id, "Updated message content")
87
+ #
88
+ # @example Edit with formatted content
89
+ # api.edit_message(room_id, event_id, {
90
+ # body: "Updated *formatted* message",
91
+ # format: "org.matrix.custom.html",
92
+ # formatted_body: "Updated <em>formatted</em> message"
93
+ # })
94
+ def edit_message(room_id, event_id, new_content, msgtype: 'm.text', **params)
95
+ new_content = { body: new_content } if new_content.is_a?(String)
96
+ new_content[:msgtype] ||= msgtype
97
+
98
+ # Build the edit event content
99
+ content = {
100
+ body: "* #{new_content[:body]}", # Fallback with asterisk prefix
101
+ msgtype: msgtype,
102
+ 'm.new_content' => new_content,
103
+ 'm.relates_to' => {
104
+ rel_type: 'm.replace',
105
+ event_id: event_id
106
+ }
107
+ }
108
+
109
+ # Copy format fields to top level if present
110
+ if new_content[:format]
111
+ content[:format] = new_content[:format]
112
+ content[:formatted_body] = "* #{new_content[:formatted_body]}"
113
+ end
114
+
115
+ send_message_event(room_id, 'm.room.message', content, **params)
116
+ end
117
+
118
+ # Send a reaction to an event
119
+ #
120
+ # @param room_id [String] The room ID
121
+ # @param event_id [String] The event ID to react to
122
+ # @param key [String] The reaction key (usually an emoji)
123
+ # @param params [Hash] Additional parameters for send_message_event
124
+ # @return [Response] The response from the server
125
+ #
126
+ # @example React with an emoji
127
+ # api.send_reaction(room_id, event_id, "👍")
128
+ #
129
+ # @example React with custom text
130
+ # api.send_reaction(room_id, event_id, "agree")
131
+ def send_reaction(room_id, event_id, key, **params)
132
+ content = {
133
+ 'm.relates_to' => {
134
+ rel_type: 'm.annotation',
135
+ event_id: event_id,
136
+ key: key
137
+ }
138
+ }
139
+
140
+ send_message_event(room_id, 'm.reaction', content, **params)
141
+ end
142
+
143
+ # Remove a reaction from an event
144
+ #
145
+ # @param room_id [String] The room ID
146
+ # @param reaction_event_id [String] The reaction event ID to redact
147
+ # @param reason [String,nil] Optional reason for removing the reaction
148
+ # @param params [Hash] Additional parameters for redact_event
149
+ # @return [Response] The response from the server
150
+ #
151
+ # @example Remove a reaction
152
+ # api.remove_reaction(room_id, reaction_event_id)
153
+ def remove_reaction(room_id, reaction_event_id, reason: nil, **params)
154
+ redact_event(room_id, reaction_event_id, reason: reason, **params)
155
+ end
156
+
157
+ # Send a reference to another event
158
+ #
159
+ # @param room_id [String] The room ID
160
+ # @param event_id [String] The event ID to reference
161
+ # @param content [Hash,String] The message content
162
+ # @param msgtype [String] The message type (defaults to 'm.text')
163
+ # @param params [Hash] Additional parameters for send_message_event
164
+ # @return [Response] The response from the server
165
+ #
166
+ # @example Send a message referencing another event
167
+ # api.send_reference(room_id, event_id, "See the above message")
168
+ def send_reference(room_id, event_id, content, msgtype: 'm.text', **params)
169
+ content = { body: content } if content.is_a?(String)
170
+ content[:msgtype] ||= msgtype
171
+
172
+ content[:'m.relates_to'] = {
173
+ rel_type: 'm.reference',
174
+ event_id: event_id
175
+ }
176
+
177
+ send_message_event(room_id, 'm.room.message', content, **params)
178
+ end
179
+
180
+ # Get related events for a given event
181
+ #
182
+ # @param room_id [String] The room ID
183
+ # @param event_id [String] The event ID to get relations for
184
+ # @param rel_type [String,nil] Filter by specific relationship type
185
+ # @param event_type [String,nil] Filter by specific event type
186
+ # @param from [String,nil] Pagination token
187
+ # @param to [String,nil] Pagination token
188
+ # @param limit [Integer] Maximum number of events to return
189
+ # @param direction [String] Direction of pagination ('b' for backwards, 'f' for forwards)
190
+ # @param params [Hash] Additional query parameters
191
+ # @return [Response] The related events
192
+ #
193
+ # @example Get all relations
194
+ # api.get_relations(room_id, event_id)
195
+ #
196
+ # @example Get only reactions
197
+ # api.get_relations(room_id, event_id, rel_type: 'm.annotation', event_type: 'm.reaction')
198
+ #
199
+ # @example Get thread replies
200
+ # api.get_relations(room_id, event_id, rel_type: 'm.thread')
201
+ def get_relations(room_id, event_id, rel_type: nil, event_type: nil,
202
+ from: nil, to: nil, limit: nil, direction: 'b', **params)
203
+ query = {
204
+ from: from,
205
+ to: to,
206
+ limit: limit,
207
+ dir: direction
208
+ }.merge(params).compact
209
+
210
+ # Build the appropriate endpoint based on filters
211
+ endpoint = "/rooms/#{room_id}/relations/#{event_id}"
212
+ endpoint += "/#{rel_type}" if rel_type
213
+ endpoint += "/#{event_type}" if rel_type && event_type
214
+
215
+ request(:get, client_api_latest, endpoint, query: query)
216
+ end
217
+
218
+ # Get aggregated relations for multiple events
219
+ #
220
+ # @param room_id [String] The room ID
221
+ # @param event_ids [Array<String>] The event IDs to get relations for
222
+ # @param rel_type [String,nil] Filter by specific relationship type
223
+ # @param event_type [String,nil] Filter by specific event type
224
+ # @param params [Hash] Additional body parameters
225
+ # @return [Response] The aggregated relations
226
+ #
227
+ # @example Get aggregated reactions for multiple events
228
+ # api.get_aggregated_relations(room_id, event_ids, rel_type: 'm.annotation')
229
+ def get_aggregated_relations(room_id, event_ids, rel_type: nil, event_type: nil, **params)
230
+ body = {
231
+ event_ids: event_ids,
232
+ rel_type: rel_type,
233
+ event_type: event_type
234
+ }.merge(params).compact
235
+
236
+ request(:post, client_api_latest, "/rooms/#{room_id}/aggregations", body: body)
237
+ end
238
+
239
+ # Get the edit history of an event
240
+ #
241
+ # @param room_id [String] The room ID
242
+ # @param event_id [String] The event ID to get edit history for
243
+ # @param params [Hash] Additional query parameters
244
+ # @return [Response] The edit history
245
+ #
246
+ # @example Get edit history
247
+ # api.get_edit_history(room_id, event_id)
248
+ def get_edit_history(room_id, event_id, **params)
249
+ get_relations(room_id, event_id, rel_type: 'm.replace', event_type: 'm.room.message', **params)
250
+ end
251
+
252
+ # Get all reactions for an event
253
+ #
254
+ # @param room_id [String] The room ID
255
+ # @param event_id [String] The event ID to get reactions for
256
+ # @param params [Hash] Additional query parameters
257
+ # @return [Response] The reactions
258
+ #
259
+ # @example Get all reactions
260
+ # api.get_reactions(room_id, event_id)
261
+ def get_reactions(room_id, event_id, **params)
262
+ get_relations(room_id, event_id, rel_type: 'm.annotation', event_type: 'm.reaction', **params)
263
+ end
264
+
265
+ # Get thread messages for a root event
266
+ #
267
+ # @param room_id [String] The room ID
268
+ # @param thread_root_id [String] The thread root event ID
269
+ # @param params [Hash] Additional query parameters
270
+ # @return [Response] The thread messages
271
+ #
272
+ # @example Get thread messages
273
+ # api.get_thread_messages(room_id, thread_root_id)
274
+ def get_thread_messages(room_id, thread_root_id, **params)
275
+ get_relations(room_id, thread_root_id, rel_type: 'm.thread', **params)
276
+ end
277
+
278
+ # Check if an event has been edited
279
+ #
280
+ # @param event [Hash] The event to check
281
+ # @return [Boolean] True if the event has been edited
282
+ def event_edited?(event)
283
+ event.dig(:unsigned, :'m.relations', :'m.replace').present?
284
+ end
285
+
286
+ # Get the latest edit content for an event
287
+ #
288
+ # @param event [Hash] The event to get latest content for
289
+ # @return [Hash,nil] The latest content or nil if not edited
290
+ def get_latest_edit_content(event)
291
+ edit_event = event.dig(:unsigned, :'m.relations', :'m.replace')
292
+ return nil unless edit_event
293
+
294
+ # Return the m.new_content if available, otherwise the content
295
+ edit_event.dig(:content, :'m.new_content') || edit_event[:content]
296
+ end
297
+
298
+ # Check if an event is part of a thread
299
+ #
300
+ # @param event [Hash] The event to check
301
+ # @return [Boolean] True if the event is part of a thread
302
+ def in_thread?(event)
303
+ rel_type = event.dig(:content, :'m.relates_to', :rel_type)
304
+ rel_type == 'm.thread'
305
+ end
306
+
307
+ # Get the thread root ID for an event
308
+ #
309
+ # @param event [Hash] The event to get thread root for
310
+ # @return [String,nil] The thread root event ID or nil if not in a thread
311
+ def get_thread_root_id(event)
312
+ return nil unless in_thread?(event)
313
+ event.dig(:content, :'m.relates_to', :event_id)
314
+ end
315
+ end
316
+ end
317
+ end
318
+ end
@@ -4,9 +4,17 @@ require 'rails/railtie'
4
4
 
5
5
  module ActiveMatrix
6
6
  class Railtie < Rails::Railtie
7
+ Rails.logger.debug 'ActiveMatrix::Railtie: Loading...'
8
+
7
9
  initializer 'activematrix.configure_rails_initialization' do
10
+ Rails.logger.debug 'ActiveMatrix::Railtie: Initializer running'
8
11
  # Configure Rails.logger as the default logger
9
12
  ActiveMatrix.logger = Rails.logger
13
+ Rails.logger.debug 'ActiveMatrix::Railtie: Logger configured'
14
+
15
+ # Debug autoload paths
16
+ Rails.logger.debug { "ActiveMatrix::Railtie: Autoload paths = #{Rails.application.config.autoload_paths}" }
17
+ Rails.logger.debug { "ActiveMatrix::Railtie: Eager load paths = #{Rails.application.config.eager_load_paths}" }
10
18
  end
11
19
  end
12
20
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveMatrix
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.7'
5
5
  end
data/lib/active_matrix.rb CHANGED
@@ -5,12 +5,9 @@ require_relative 'active_matrix/version'
5
5
 
6
6
  require 'json'
7
7
  require 'zeitwerk'
8
- require 'active_support'
9
- require 'active_support/core_ext/integer/time'
10
- require 'active_support/core_ext/time/calculations'
11
- require 'active_support/core_ext/time/zones'
12
- require 'active_support/core_ext/hash/keys'
13
- require 'active_support/core_ext/object/blank'
8
+ require 'active_record'
9
+ require 'active_job'
10
+ require 'state_machines-activerecord'
14
11
 
15
12
  module ActiveMatrix
16
13
  # Configuration
@@ -83,6 +80,11 @@ module ActiveMatrix
83
80
  Loader.ignore("#{__dir__}/generators")
84
81
  Loader.ignore("#{__dir__}/activematrix.rb")
85
82
 
83
+ # Ignore files that don't follow Zeitwerk naming conventions
84
+ Loader.ignore("#{__dir__}/active_matrix/errors.rb")
85
+ Loader.ignore("#{__dir__}/active_matrix/events.rb")
86
+ Loader.ignore("#{__dir__}/active_matrix/uri_module.rb")
87
+
86
88
  # Configure inflections for special cases
87
89
  Loader.inflector.inflect(
88
90
  'mxid' => 'MXID',
@@ -97,11 +99,11 @@ module ActiveMatrix
97
99
  # Setup Zeitwerk autoloading
98
100
  Loader.setup
99
101
 
100
- # Load core classes that are used in metaprogramming
102
+ # Load classes that don't follow Zeitwerk naming conventions
101
103
  require_relative 'active_matrix/errors'
102
104
  require_relative 'active_matrix/events'
103
105
  require_relative 'active_matrix/uri_module'
104
106
 
105
- # Load Railtie for Rails integration
106
- require 'active_matrix/railtie' if defined?(Rails::Railtie)
107
+ # Load Engine for Rails integration
108
+ require 'active_matrix/engine'
107
109
  end
@@ -23,13 +23,6 @@ module ActiveMatrix
23
23
  template 'active_matrix.rb', 'config/initializers/active_matrix.rb'
24
24
  end
25
25
 
26
- def create_models
27
- template 'matrix_agent.rb', 'app/models/matrix_agent.rb'
28
- template 'agent_memory.rb', 'app/models/agent_memory.rb'
29
- template 'conversation_context.rb', 'app/models/conversation_context.rb'
30
- template 'global_memory.rb', 'app/models/global_memory.rb'
31
- end
32
-
33
26
  def display_post_install
34
27
  readme 'README' if behavior == :invoke
35
28
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activematrix
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -10,6 +10,20 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: maxitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: mocha
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +136,20 @@ dependencies:
122
136
  - - ">="
123
137
  - !ruby/object:Gem::Version
124
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: sqlite3
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '2.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '2.0'
125
153
  - !ruby/object:Gem::Dependency
126
154
  name: syslog
127
155
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +206,20 @@ dependencies:
178
206
  - - "~>"
179
207
  - !ruby/object:Gem::Version
180
208
  version: '3.19'
209
+ - !ruby/object:Gem::Dependency
210
+ name: activejob
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: '8.0'
216
+ type: :runtime
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: '8.0'
181
223
  - !ruby/object:Gem::Dependency
182
224
  name: activerecord
183
225
  requirement: !ruby/object:Gem::Requirement
@@ -238,16 +280,16 @@ dependencies:
238
280
  name: state_machines-activerecord
239
281
  requirement: !ruby/object:Gem::Requirement
240
282
  requirements:
241
- - - "~>"
283
+ - - "<="
242
284
  - !ruby/object:Gem::Version
243
- version: 0.40.0
285
+ version: 0.100.0
244
286
  type: :runtime
245
287
  prerelease: false
246
288
  version_requirements: !ruby/object:Gem::Requirement
247
289
  requirements:
248
- - - "~>"
290
+ - - "<="
249
291
  - !ruby/object:Gem::Version
250
- version: 0.40.0
292
+ version: 0.100.0
251
293
  - !ruby/object:Gem::Dependency
252
294
  name: zeitwerk
253
295
  requirement: !ruby/object:Gem::Requirement
@@ -262,8 +304,15 @@ dependencies:
262
304
  - - "~>"
263
305
  - !ruby/object:Gem::Version
264
306
  version: '2.6'
265
- description: A Ruby on Rails gem that provides seamless integration with the Matrix
266
- protocol, enabling Rails applications to connect and communicate with Matrix servers.
307
+ description: "ActiveMatrix is a comprehensive Rails-native Matrix SDK that enables
308
+ developers to build sophisticated multi-agent bot systems \nand real-time communication
309
+ features. This gem provides deep Rails integration with ActiveRecord models, state
310
+ machines for \nbot lifecycle management, multi-tiered memory systems, intelligent
311
+ event routing, connection pooling, and built-in \ninter-agent communication. Perfect
312
+ for building chatbots, automation systems, monitoring agents, and collaborative
313
+ AI \nsystems within Rails applications. Features include command handling, room
314
+ management, media support, end-to-end encryption \ncapabilities, and extensive protocol
315
+ support (CS, AS, IS, SS).\n"
267
316
  email:
268
317
  - terminale@gmail.com
269
318
  - ace@haxalot.com
@@ -277,6 +326,13 @@ files:
277
326
  - CHANGELOG.md
278
327
  - LICENSE.txt
279
328
  - README.md
329
+ - app/jobs/active_matrix/application_job.rb
330
+ - app/models/active_matrix/agent.rb
331
+ - app/models/active_matrix/agent/jobs/memory_reaper.rb
332
+ - app/models/active_matrix/agent_store.rb
333
+ - app/models/active_matrix/application_record.rb
334
+ - app/models/active_matrix/chat_session.rb
335
+ - app/models/active_matrix/knowledge_base.rb
280
336
  - lib/active_matrix.rb
281
337
  - lib/active_matrix/account_data_cache.rb
282
338
  - lib/active_matrix/agent_manager.rb
@@ -288,6 +344,7 @@ files:
288
344
  - lib/active_matrix/cacheable.rb
289
345
  - lib/active_matrix/client.rb
290
346
  - lib/active_matrix/client_pool.rb
347
+ - lib/active_matrix/engine.rb
291
348
  - lib/active_matrix/errors.rb
292
349
  - lib/active_matrix/event_router.rb
293
350
  - lib/active_matrix/events.rb
@@ -301,6 +358,7 @@ files:
301
358
  - lib/active_matrix/mxid.rb
302
359
  - lib/active_matrix/protocols/as.rb
303
360
  - lib/active_matrix/protocols/cs.rb
361
+ - lib/active_matrix/protocols/cs/message_relationships.rb
304
362
  - lib/active_matrix/protocols/is.rb
305
363
  - lib/active_matrix/protocols/msc.rb
306
364
  - lib/active_matrix/protocols/ss.rb
@@ -319,19 +377,24 @@ files:
319
377
  - lib/generators/active_matrix/install/install_generator.rb
320
378
  - lib/generators/active_matrix/install/templates/README
321
379
  - lib/generators/active_matrix/install/templates/active_matrix.rb
322
- - lib/generators/active_matrix/install/templates/agent_memory.rb
323
- - lib/generators/active_matrix/install/templates/conversation_context.rb
324
380
  - lib/generators/active_matrix/install/templates/create_agent_memories.rb
325
381
  - lib/generators/active_matrix/install/templates/create_conversation_contexts.rb
326
382
  - lib/generators/active_matrix/install/templates/create_global_memories.rb
327
383
  - lib/generators/active_matrix/install/templates/create_matrix_agents.rb
328
- - lib/generators/active_matrix/install/templates/global_memory.rb
329
- - lib/generators/active_matrix/install/templates/matrix_agent.rb
330
384
  homepage: https://github.com/seuros/activematrix
331
385
  licenses:
332
386
  - MIT
333
387
  metadata:
334
388
  rubygems_mfa_required: 'true'
389
+ homepage_uri: https://github.com/seuros/activematrix
390
+ source_code_uri: https://github.com/seuros/activematrix
391
+ changelog_uri: https://github.com/seuros/activematrix/blob/master/CHANGELOG.md
392
+ documentation_uri: https://rubydoc.info/gems/activematrix
393
+ bug_tracker_uri: https://github.com/seuros/activematrix/issues
394
+ wiki_uri: https://github.com/seuros/activematrix/wiki
395
+ tags: matrix, matrix-protocol, matrix-sdk, matrix-api, matrix-client, rails, rails-engine,
396
+ activerecord, activejob, rails-integration, bot, chatbot, multi-agent, agent-system,
397
+ bot-framework, real-time, messaging, communication, chat, state-machine
335
398
  rdoc_options: []
336
399
  require_paths:
337
400
  - lib
@@ -346,7 +409,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
346
409
  - !ruby/object:Gem::Version
347
410
  version: '0'
348
411
  requirements: []
349
- rubygems_version: 3.6.9
412
+ rubygems_version: 3.6.7
350
413
  specification_version: 4
351
- summary: Rails gem for connecting to Matrix protocol
414
+ summary: Rails-native Matrix SDK for building multi-agent bot systems and real-time
415
+ communication
352
416
  test_files: []
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class AgentMemory < ApplicationRecord
4
- belongs_to :matrix_agent
5
-
6
- validates :key, presence: true, uniqueness: { scope: :matrix_agent_id }
7
-
8
- scope :active, -> { where('expires_at IS NULL OR expires_at > ?', Time.current) }
9
- scope :expired, -> { where('expires_at <= ?', Time.current) }
10
-
11
- # Automatically clean up expired memories
12
- after_commit :schedule_cleanup, if: :expires_at?
13
-
14
- def expired?
15
- expires_at.present? && expires_at <= Time.current
16
- end
17
-
18
- def ttl=(seconds)
19
- self.expires_at = seconds.present? ? Time.current + seconds : nil
20
- end
21
-
22
- def ttl
23
- return nil unless expires_at.present?
24
-
25
- remaining = expires_at - Time.current
26
- [remaining, 0].max
27
- end
28
-
29
- # Cache integration
30
- def cache_key
31
- "agent_memory/#{matrix_agent_id}/#{key}"
32
- end
33
-
34
- def write_to_cache
35
- Rails.cache.write(cache_key, value, expires_in: ttl)
36
- end
37
-
38
- def self.cleanup_expired!
39
- expired.destroy_all
40
- end
41
-
42
- private
43
-
44
- def schedule_cleanup
45
- AgentMemoryCleanupJob.set(wait_until: expires_at).perform_later if defined?(AgentMemoryCleanupJob)
46
- end
47
- end