rails_console_ai 0.30.0 → 0.32.0

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -26
  3. data/README.md +33 -11
  4. data/app/controllers/rails_console_ai/agent_versions_controller.rb +2 -9
  5. data/app/controllers/rails_console_ai/agents_controller.rb +25 -48
  6. data/app/controllers/rails_console_ai/memories_controller.rb +35 -38
  7. data/app/controllers/rails_console_ai/memory_versions_controller.rb +2 -6
  8. data/app/controllers/rails_console_ai/sessions_controller.rb +1 -1
  9. data/app/controllers/rails_console_ai/skill_versions_controller.rb +2 -8
  10. data/app/controllers/rails_console_ai/skills_controller.rb +19 -48
  11. data/app/models/rails_console_ai/agent.rb +33 -65
  12. data/app/models/rails_console_ai/agent_version.rb +8 -20
  13. data/app/models/rails_console_ai/memory.rb +67 -23
  14. data/app/models/rails_console_ai/memory_version.rb +5 -20
  15. data/app/models/rails_console_ai/skill.rb +55 -105
  16. data/app/models/rails_console_ai/skill_version.rb +7 -28
  17. data/app/views/rails_console_ai/agents/_form.html.erb +9 -34
  18. data/app/views/rails_console_ai/agents/diff.html.erb +1 -5
  19. data/app/views/rails_console_ai/agents/new.html.erb +0 -16
  20. data/app/views/rails_console_ai/memories/_form.html.erb +6 -13
  21. data/app/views/rails_console_ai/memories/diff.html.erb +1 -5
  22. data/app/views/rails_console_ai/memories/index.html.erb +13 -1
  23. data/app/views/rails_console_ai/memories/new.html.erb +0 -15
  24. data/app/views/rails_console_ai/memories/show.html.erb +23 -0
  25. data/app/views/rails_console_ai/skills/_form.html.erb +7 -29
  26. data/app/views/rails_console_ai/skills/diff.html.erb +1 -8
  27. data/app/views/rails_console_ai/skills/new.html.erb +0 -17
  28. data/config/routes.rb +3 -3
  29. data/lib/generators/rails_console_ai/templates/initializer.rb +5 -5
  30. data/lib/rails_console_ai/agent_loader.rb +18 -10
  31. data/lib/rails_console_ai/agent_runner.rb +66 -4
  32. data/lib/rails_console_ai/channel/console.rb +1 -1
  33. data/lib/rails_console_ai/configuration.rb +51 -33
  34. data/lib/rails_console_ai/conversation_engine.rb +3 -3
  35. data/lib/rails_console_ai/safety_guards.rb +36 -0
  36. data/lib/rails_console_ai/session_logger.rb +4 -0
  37. data/lib/rails_console_ai/skill_loader.rb +18 -9
  38. data/lib/rails_console_ai/slack_bot.rb +2 -2
  39. data/lib/rails_console_ai/storage/database_storage.rb +14 -20
  40. data/lib/rails_console_ai/sub_agent.rb +1 -1
  41. data/lib/rails_console_ai/tools/memory_tools.rb +37 -6
  42. data/lib/rails_console_ai/tools/registry.rb +18 -6
  43. data/lib/rails_console_ai/version.rb +1 -1
  44. data/lib/rails_console_ai.rb +92 -71
  45. metadata +1 -1
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  require 'rails_console_ai/version'
2
4
  require 'rails_console_ai/configuration'
3
5
 
@@ -57,8 +59,17 @@ module RailsConsoleAi
57
59
 
58
60
  # Enqueue an agent run. Returns the Integer session id immediately;
59
61
  # the actual work is picked up by `rake rails_console_ai:agents`.
60
- def run_agent(query, name: nil, user_name: nil)
62
+ #
63
+ # use_thinking_model: run on the configured thinking-tier model
64
+ # max_wall_clock_seconds: hard kill the run after N seconds (nil = no cap)
65
+ def run_agent(query, name: nil, user_name: nil,
66
+ use_thinking_model: false,
67
+ max_wall_clock_seconds: 600)
61
68
  require 'rails_console_ai/session_logger'
69
+ options = {
70
+ 'use_thinking_model' => !!use_thinking_model,
71
+ 'max_wall_clock_seconds' => max_wall_clock_seconds
72
+ }
62
73
  id = SessionLogger.log(
63
74
  query: query,
64
75
  conversation: [],
@@ -66,7 +77,8 @@ module RailsConsoleAi
66
77
  name: name,
67
78
  user_name: user_name,
68
79
  status: 'queued',
69
- executed: false
80
+ executed: false,
81
+ options: options
70
82
  )
71
83
  raise 'Failed to enqueue agent run (session logging disabled or table missing)' unless id
72
84
  id
@@ -74,7 +86,7 @@ module RailsConsoleAi
74
86
 
75
87
  # Returns the current status string for an enqueued agent run, or nil
76
88
  # if the session id is not found. Status is one of:
77
- # 'queued' | 'running' | 'ready' | 'failed'.
89
+ # 'queued' | 'running' | 'ready' | 'failed' | 'aborted'.
78
90
  def check_agent(session_id)
79
91
  Session.where(id: session_id).pluck(:status).first
80
92
  end
@@ -88,6 +100,16 @@ module RailsConsoleAi
88
100
  { status: row.status, result: row.result, error: row.error_message }
89
101
  end
90
102
 
103
+ # Abort a queued or running agent run. Returns true if the run was
104
+ # aborted, false if it had already finished (or doesn't exist).
105
+ # Queued runs are never picked up; a run already executing keeps
106
+ # going but its result is discarded when it completes.
107
+ def abort_agent(session_id)
108
+ n = Session.where(id: session_id, status: %w[queued running])
109
+ .update_all(status: 'aborted', error_message: 'Aborted')
110
+ n == 1
111
+ end
112
+
91
113
  def status
92
114
  c = configuration
93
115
  key = c.resolved_api_key
@@ -145,6 +167,7 @@ module RailsConsoleAi
145
167
  t.string :slack_thread_ts, limit: 255
146
168
  t.string :slack_channel_name, limit: 255
147
169
  t.integer :duration_ms
170
+ t.text :options
148
171
  t.datetime :created_at, null: false
149
172
  end
150
173
 
@@ -169,13 +192,22 @@ module RailsConsoleAi
169
192
  skills_table = 'rails_console_ai_skills'
170
193
  versions_table = 'rails_console_ai_skill_versions'
171
194
 
195
+ # Old shape had per-field columns (body, tags, bypass_guards_for_methods,
196
+ # description). New shape stores the raw .md in `content`. Pre-production,
197
+ # so we drop and recreate when the old shape is detected.
198
+ if conn.table_exists?(skills_table) && !conn.column_exists?(skills_table, :content)
199
+ conn.drop_table(skills_table)
200
+ $stdout.puts "\e[33mRailsConsoleAi: dropped legacy #{skills_table} (replaced with single-content schema).\e[0m"
201
+ end
202
+ if conn.table_exists?(versions_table) && !conn.column_exists?(versions_table, :content)
203
+ conn.drop_table(versions_table)
204
+ $stdout.puts "\e[33mRailsConsoleAi: dropped legacy #{versions_table}.\e[0m"
205
+ end
206
+
172
207
  unless conn.table_exists?(skills_table)
173
208
  conn.create_table(skills_table) do |t|
174
209
  t.string :name, limit: 255, null: false
175
- t.text :description
176
- t.text :body
177
- t.text :tags
178
- t.text :bypass_guards_for_methods
210
+ t.text :content, null: false
179
211
  t.string :status, limit: 20, default: 'proposed', null: false
180
212
  t.string :approved_by, limit: 255
181
213
  t.datetime :approved_at
@@ -187,34 +219,13 @@ module RailsConsoleAi
187
219
  conn.add_index(skills_table, :name, unique: true)
188
220
  conn.add_index(skills_table, :status)
189
221
  $stdout.puts "\e[32mRailsConsoleAi: created #{skills_table} table.\e[0m"
190
- else
191
- # Idempotent column-add probes for existing installs.
192
- unless conn.column_exists?(skills_table, :status)
193
- conn.add_column(skills_table, :status, :string, limit: 20, default: 'proposed', null: false)
194
- conn.add_index(skills_table, :status) unless conn.index_exists?(skills_table, :status)
195
- end
196
- unless conn.column_exists?(skills_table, :approved_by)
197
- conn.add_column(skills_table, :approved_by, :string, limit: 255)
198
- end
199
- unless conn.column_exists?(skills_table, :approved_at)
200
- conn.add_column(skills_table, :approved_at, :datetime)
201
- end
202
- unless conn.column_exists?(skills_table, :use_count)
203
- conn.add_column(skills_table, :use_count, :integer, default: 0, null: false)
204
- end
205
- unless conn.column_exists?(skills_table, :last_used_at)
206
- conn.add_column(skills_table, :last_used_at, :datetime)
207
- end
208
222
  end
209
223
 
210
224
  unless conn.table_exists?(versions_table)
211
225
  conn.create_table(versions_table) do |t|
212
226
  t.integer :skill_id
213
227
  t.string :name, limit: 255
214
- t.text :description
215
- t.text :body
216
- t.text :tags
217
- t.text :bypass_guards_for_methods
228
+ t.text :content
218
229
  t.string :status, limit: 20
219
230
  t.string :edited_by, limit: 255
220
231
  t.text :change_note
@@ -223,10 +234,6 @@ module RailsConsoleAi
223
234
  conn.add_index(versions_table, :skill_id)
224
235
  conn.add_index(versions_table, :created_at)
225
236
  $stdout.puts "\e[32mRailsConsoleAi: created #{versions_table} table.\e[0m"
226
- else
227
- unless conn.column_exists?(versions_table, :status)
228
- conn.add_column(versions_table, :status, :string, limit: 20)
229
- end
230
237
  end
231
238
  end
232
239
 
@@ -234,33 +241,54 @@ module RailsConsoleAi
234
241
  memories_table = 'rails_console_ai_memories'
235
242
  versions_table = 'rails_console_ai_memory_versions'
236
243
 
244
+ if conn.table_exists?(memories_table) && !conn.column_exists?(memories_table, :content)
245
+ conn.drop_table(memories_table)
246
+ $stdout.puts "\e[33mRailsConsoleAi: dropped legacy #{memories_table}.\e[0m"
247
+ end
248
+ if conn.table_exists?(versions_table) && !conn.column_exists?(versions_table, :content)
249
+ conn.drop_table(versions_table)
250
+ $stdout.puts "\e[33mRailsConsoleAi: dropped legacy #{versions_table}.\e[0m"
251
+ end
252
+
237
253
  unless conn.table_exists?(memories_table)
238
254
  conn.create_table(memories_table) do |t|
239
255
  t.string :name, limit: 255, null: false
240
- t.text :description
241
- t.text :tags
256
+ t.text :content, null: false
257
+ t.string :status, limit: 20, default: 'proposed', null: false
258
+ t.string :approved_by, limit: 255
259
+ t.datetime :approved_at
242
260
  t.integer :use_count, default: 0, null: false
243
261
  t.datetime :last_used_at
244
262
  t.datetime :created_at, null: false
245
263
  t.datetime :updated_at, null: false
246
264
  end
247
265
  conn.add_index(memories_table, :name, unique: true)
266
+ conn.add_index(memories_table, :status)
248
267
  $stdout.puts "\e[32mRailsConsoleAi: created #{memories_table} table.\e[0m"
249
- else
250
- unless conn.column_exists?(memories_table, :use_count)
251
- conn.add_column(memories_table, :use_count, :integer, default: 0, null: false)
252
- end
253
- unless conn.column_exists?(memories_table, :last_used_at)
254
- conn.add_column(memories_table, :last_used_at, :datetime)
255
- end
268
+ end
269
+
270
+ # Existing installs have the content-based memories table but predate the
271
+ # approval columns — the drop-on-missing-content guard above won't fire for
272
+ # them, so add the columns in place. Memories created before approval existed
273
+ # were trusted under the old no-approval regime, so grandfather them to
274
+ # "approved" rather than yanking them out from under the AI; only memories
275
+ # created from now on start in "proposed".
276
+ if conn.table_exists?(memories_table) && !conn.column_exists?(memories_table, :status)
277
+ conn.add_column(memories_table, :status, :string, limit: 20, default: 'proposed', null: false)
278
+ conn.execute("UPDATE #{conn.quote_table_name(memories_table)} SET status = 'approved'")
279
+ end
280
+ if conn.table_exists?(memories_table)
281
+ conn.add_column(memories_table, :approved_by, :string, limit: 255) unless conn.column_exists?(memories_table, :approved_by)
282
+ conn.add_column(memories_table, :approved_at, :datetime) unless conn.column_exists?(memories_table, :approved_at)
283
+ conn.add_index(memories_table, :status) unless conn.index_exists?(memories_table, :status)
256
284
  end
257
285
 
258
286
  unless conn.table_exists?(versions_table)
259
287
  conn.create_table(versions_table) do |t|
260
288
  t.integer :memory_id
261
289
  t.string :name, limit: 255
262
- t.text :description
263
- t.text :tags
290
+ t.text :content
291
+ t.string :status, limit: 20
264
292
  t.string :edited_by, limit: 255
265
293
  t.text :change_note
266
294
  t.datetime :created_at, null: false
@@ -269,20 +297,29 @@ module RailsConsoleAi
269
297
  conn.add_index(versions_table, :created_at)
270
298
  $stdout.puts "\e[32mRailsConsoleAi: created #{versions_table} table.\e[0m"
271
299
  end
300
+
301
+ if conn.table_exists?(versions_table) && !conn.column_exists?(versions_table, :status)
302
+ conn.add_column(versions_table, :status, :string, limit: 20)
303
+ end
272
304
  end
273
305
 
274
306
  def setup_agents_tables!(conn)
275
307
  agents_table = 'rails_console_ai_agents'
276
308
  versions_table = 'rails_console_ai_agent_versions'
277
309
 
310
+ if conn.table_exists?(agents_table) && !conn.column_exists?(agents_table, :content)
311
+ conn.drop_table(agents_table)
312
+ $stdout.puts "\e[33mRailsConsoleAi: dropped legacy #{agents_table}.\e[0m"
313
+ end
314
+ if conn.table_exists?(versions_table) && !conn.column_exists?(versions_table, :content)
315
+ conn.drop_table(versions_table)
316
+ $stdout.puts "\e[33mRailsConsoleAi: dropped legacy #{versions_table}.\e[0m"
317
+ end
318
+
278
319
  unless conn.table_exists?(agents_table)
279
320
  conn.create_table(agents_table) do |t|
280
321
  t.string :name, limit: 255, null: false
281
- t.text :description
282
- t.text :body
283
- t.integer :max_rounds
284
- t.string :model, limit: 100
285
- t.text :tools
322
+ t.text :content, null: false
286
323
  t.string :status, limit: 20, default: 'proposed', null: false
287
324
  t.string :approved_by, limit: 255
288
325
  t.datetime :approved_at
@@ -294,34 +331,13 @@ module RailsConsoleAi
294
331
  conn.add_index(agents_table, :name, unique: true)
295
332
  conn.add_index(agents_table, :status)
296
333
  $stdout.puts "\e[32mRailsConsoleAi: created #{agents_table} table.\e[0m"
297
- else
298
- unless conn.column_exists?(agents_table, :status)
299
- conn.add_column(agents_table, :status, :string, limit: 20, default: 'proposed', null: false)
300
- conn.add_index(agents_table, :status) unless conn.index_exists?(agents_table, :status)
301
- end
302
- unless conn.column_exists?(agents_table, :approved_by)
303
- conn.add_column(agents_table, :approved_by, :string, limit: 255)
304
- end
305
- unless conn.column_exists?(agents_table, :approved_at)
306
- conn.add_column(agents_table, :approved_at, :datetime)
307
- end
308
- unless conn.column_exists?(agents_table, :use_count)
309
- conn.add_column(agents_table, :use_count, :integer, default: 0, null: false)
310
- end
311
- unless conn.column_exists?(agents_table, :last_used_at)
312
- conn.add_column(agents_table, :last_used_at, :datetime)
313
- end
314
334
  end
315
335
 
316
336
  unless conn.table_exists?(versions_table)
317
337
  conn.create_table(versions_table) do |t|
318
338
  t.integer :agent_id
319
339
  t.string :name, limit: 255
320
- t.text :description
321
- t.text :body
322
- t.integer :max_rounds
323
- t.string :model, limit: 100
324
- t.text :tools
340
+ t.text :content
325
341
  t.string :status, limit: 20
326
342
  t.string :edited_by, limit: 255
327
343
  t.text :change_note
@@ -376,6 +392,11 @@ module RailsConsoleAi
376
392
  migrations << 'error_message'
377
393
  end
378
394
 
395
+ unless conn.column_exists?(table, :options)
396
+ conn.add_column(table, :options, :text)
397
+ migrations << 'options'
398
+ end
399
+
379
400
  unless conn.index_exists?(table, [:mode, :status], name: 'idx_rca_sessions_mode_status')
380
401
  conn.add_index(table, [:mode, :status], name: 'idx_rca_sessions_mode_status')
381
402
  migrations << 'idx_rca_sessions_mode_status'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_console_ai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
4
+ version: 0.32.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cortfr