rails_console_ai 0.31.0 → 0.33.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -26
- data/README.md +54 -10
- data/app/controllers/rails_console_ai/agent_versions_controller.rb +1 -1
- data/app/controllers/rails_console_ai/agents_controller.rb +1 -1
- data/app/controllers/rails_console_ai/memories_controller.rb +22 -2
- data/app/controllers/rails_console_ai/memory_versions_controller.rb +1 -1
- data/app/controllers/rails_console_ai/sessions_controller.rb +1 -1
- data/app/controllers/rails_console_ai/skill_versions_controller.rb +1 -1
- data/app/controllers/rails_console_ai/skills_controller.rb +1 -1
- data/app/models/rails_console_ai/memory.rb +40 -1
- data/app/views/rails_console_ai/memories/index.html.erb +13 -1
- data/app/views/rails_console_ai/memories/show.html.erb +23 -0
- data/config/routes.rb +3 -0
- data/lib/generators/rails_console_ai/templates/initializer.rb +5 -5
- data/lib/rails_console_ai/agent_runner.rb +12 -1
- data/lib/rails_console_ai/channel/console.rb +1 -1
- data/lib/rails_console_ai/configuration.rb +76 -33
- data/lib/rails_console_ai/conversation_engine.rb +74 -6
- data/lib/rails_console_ai/executor.rb +30 -1
- data/lib/rails_console_ai/safety_guards.rb +36 -0
- data/lib/rails_console_ai/slack_bot.rb +2 -2
- data/lib/rails_console_ai/sub_agent.rb +1 -1
- data/lib/rails_console_ai/tools/memory_tools.rb +29 -6
- data/lib/rails_console_ai/tools/registry.rb +23 -7
- data/lib/rails_console_ai/version.rb +1 -1
- data/lib/rails_console_ai.rb +38 -1
- metadata +1 -1
|
@@ -338,19 +338,27 @@ module RailsConsoleAi
|
|
|
338
338
|
'name' => 'output-explorer',
|
|
339
339
|
'tools' => ['execute_code'],
|
|
340
340
|
'max_rounds' => 8,
|
|
341
|
+
'skip_base_instructions' => true,
|
|
341
342
|
'body' => <<~PROMPT.freeze
|
|
342
|
-
You are exploring a single chunk of captured tool output on behalf of the
|
|
343
|
+
You are exploring a single chunk of already-captured tool output on behalf of the
|
|
344
|
+
main assistant. Your ONLY job is to answer a question about that captured text.
|
|
343
345
|
|
|
344
346
|
The full output is bound to the local variable `output` (a String). You do NOT see it
|
|
345
|
-
directly — it lives in Ruby memory. Use `execute_code` with Ruby to
|
|
347
|
+
directly — it lives in Ruby memory. Use `execute_code` with Ruby to examine it:
|
|
346
348
|
- `output.length`, `output.lines.count`
|
|
347
349
|
- `output[start, len]`, `output.lines[n]`
|
|
348
350
|
- `output.scan(/pattern/)`, `output.include?("...")`
|
|
349
351
|
- `JSON.parse(output)` if it looks like JSON, then drill in
|
|
350
352
|
- any other Ruby string/collection methods
|
|
351
353
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
+
Database / ActiveRecord access is DISABLED in this context — any DB query (e.g.
|
|
355
|
+
`SomeModel.where(...)`, `.find`, raw SQL) will be blocked and raise an error. The data
|
|
356
|
+
you need exists ONLY in the `output` String already in memory. Do not load models, do
|
|
357
|
+
not run queries, and do not try to "look up" anything — every fact must come from
|
|
358
|
+
examining `output`.
|
|
359
|
+
|
|
360
|
+
Print only the specific slice or summary the task requires — never dump the whole
|
|
361
|
+
`output`. Return a concise factual answer. No preamble.
|
|
354
362
|
PROMPT
|
|
355
363
|
}.freeze
|
|
356
364
|
|
|
@@ -368,7 +376,11 @@ module RailsConsoleAi
|
|
|
368
376
|
executor: @executor,
|
|
369
377
|
output_payload: payload.dup
|
|
370
378
|
)
|
|
371
|
-
|
|
379
|
+
# Hard sandbox: the output-explorer only examines the in-memory `output` string.
|
|
380
|
+
# Block all DB access for the duration of its run so a stray query can't escape to
|
|
381
|
+
# the live database. Scoped via thread-local (explore_output runs synchronously),
|
|
382
|
+
# so the parent session's guard state is untouched.
|
|
383
|
+
result = RailsConsoleAi.configuration.safety_guards.with_database_blocked { sub.run }
|
|
372
384
|
@last_sub_agent_usage = { input: sub.input_tokens, output: sub.output_tokens, model: sub.model_used }
|
|
373
385
|
"Exploration result (#{sub.input_tokens + sub.output_tokens} tokens used, #{payload.length} chars explored):\n#{result}"
|
|
374
386
|
end
|
|
@@ -418,7 +430,7 @@ module RailsConsoleAi
|
|
|
418
430
|
|
|
419
431
|
register(
|
|
420
432
|
name: 'save_memory',
|
|
421
|
-
description: 'Save a fact or pattern you learned about this codebase for future sessions. Use after discovering how something works (e.g. sharding, auth, custom business logic). Defaults to the versioned DB store; pass target: "file" to write to the on-disk .rails_console_ai/memories directory instead.',
|
|
433
|
+
description: 'Save a fact or pattern you learned about this codebase for future sessions. Use after discovering how something works (e.g. sharding, auth, custom business logic). Defaults to the versioned DB store; pass target: "file" to write to the on-disk .rails_console_ai/memories directory instead. IMPORTANT: memories saved to the DB start in "proposed" state and must be approved by a human in the web UI before you can recall them. Edits to an approved memory also revert it to proposed. Tell the user to visit /rails_console_ai/memories to approve.',
|
|
422
434
|
parameters: {
|
|
423
435
|
'type' => 'object',
|
|
424
436
|
'properties' => {
|
|
@@ -686,13 +698,16 @@ module RailsConsoleAi
|
|
|
686
698
|
end
|
|
687
699
|
|
|
688
700
|
if @executor.last_error
|
|
689
|
-
|
|
701
|
+
result = "ERROR: #{@executor.last_error}"
|
|
702
|
+
result += "\n\n#{@executor.last_error_hint}" if @executor.last_error_hint
|
|
703
|
+
return result
|
|
690
704
|
end
|
|
691
705
|
|
|
692
706
|
output = @executor.last_output
|
|
693
707
|
parts = []
|
|
694
708
|
parts << "Output:\n#{output.strip}" if output && !output.strip.empty?
|
|
695
709
|
parts << "Return value: #{exec_result.inspect}"
|
|
710
|
+
parts << @executor.last_error_hint if @executor.last_error_hint
|
|
696
711
|
parts.join("\n\n")
|
|
697
712
|
end
|
|
698
713
|
|
|
@@ -796,6 +811,7 @@ module RailsConsoleAi
|
|
|
796
811
|
step_report = "Step #{i + 1} (#{step['description']}):\n"
|
|
797
812
|
if error
|
|
798
813
|
step_report += "ERROR: #{error}\n"
|
|
814
|
+
step_report += "#{@executor.last_error_hint}\n" if @executor.last_error_hint
|
|
799
815
|
end
|
|
800
816
|
if output && !output.strip.empty?
|
|
801
817
|
step_report += "Output: #{output.strip}\n"
|
data/lib/rails_console_ai.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
1
3
|
require 'rails_console_ai/version'
|
|
2
4
|
require 'rails_console_ai/configuration'
|
|
3
5
|
|
|
@@ -84,7 +86,7 @@ module RailsConsoleAi
|
|
|
84
86
|
|
|
85
87
|
# Returns the current status string for an enqueued agent run, or nil
|
|
86
88
|
# if the session id is not found. Status is one of:
|
|
87
|
-
# 'queued' | 'running' | 'ready' | 'failed'.
|
|
89
|
+
# 'queued' | 'running' | 'ready' | 'failed' | 'aborted'.
|
|
88
90
|
def check_agent(session_id)
|
|
89
91
|
Session.where(id: session_id).pluck(:status).first
|
|
90
92
|
end
|
|
@@ -98,6 +100,16 @@ module RailsConsoleAi
|
|
|
98
100
|
{ status: row.status, result: row.result, error: row.error_message }
|
|
99
101
|
end
|
|
100
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
|
+
|
|
101
113
|
def status
|
|
102
114
|
c = configuration
|
|
103
115
|
key = c.resolved_api_key
|
|
@@ -242,20 +254,41 @@ module RailsConsoleAi
|
|
|
242
254
|
conn.create_table(memories_table) do |t|
|
|
243
255
|
t.string :name, limit: 255, null: false
|
|
244
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
|
|
245
260
|
t.integer :use_count, default: 0, null: false
|
|
246
261
|
t.datetime :last_used_at
|
|
247
262
|
t.datetime :created_at, null: false
|
|
248
263
|
t.datetime :updated_at, null: false
|
|
249
264
|
end
|
|
250
265
|
conn.add_index(memories_table, :name, unique: true)
|
|
266
|
+
conn.add_index(memories_table, :status)
|
|
251
267
|
$stdout.puts "\e[32mRailsConsoleAi: created #{memories_table} table.\e[0m"
|
|
252
268
|
end
|
|
253
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)
|
|
284
|
+
end
|
|
285
|
+
|
|
254
286
|
unless conn.table_exists?(versions_table)
|
|
255
287
|
conn.create_table(versions_table) do |t|
|
|
256
288
|
t.integer :memory_id
|
|
257
289
|
t.string :name, limit: 255
|
|
258
290
|
t.text :content
|
|
291
|
+
t.string :status, limit: 20
|
|
259
292
|
t.string :edited_by, limit: 255
|
|
260
293
|
t.text :change_note
|
|
261
294
|
t.datetime :created_at, null: false
|
|
@@ -264,6 +297,10 @@ module RailsConsoleAi
|
|
|
264
297
|
conn.add_index(versions_table, :created_at)
|
|
265
298
|
$stdout.puts "\e[32mRailsConsoleAi: created #{versions_table} table.\e[0m"
|
|
266
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
|
|
267
304
|
end
|
|
268
305
|
|
|
269
306
|
def setup_agents_tables!(conn)
|