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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -26
- data/README.md +33 -11
- data/app/controllers/rails_console_ai/agent_versions_controller.rb +2 -9
- data/app/controllers/rails_console_ai/agents_controller.rb +25 -48
- data/app/controllers/rails_console_ai/memories_controller.rb +35 -38
- data/app/controllers/rails_console_ai/memory_versions_controller.rb +2 -6
- data/app/controllers/rails_console_ai/sessions_controller.rb +1 -1
- data/app/controllers/rails_console_ai/skill_versions_controller.rb +2 -8
- data/app/controllers/rails_console_ai/skills_controller.rb +19 -48
- data/app/models/rails_console_ai/agent.rb +33 -65
- data/app/models/rails_console_ai/agent_version.rb +8 -20
- data/app/models/rails_console_ai/memory.rb +67 -23
- data/app/models/rails_console_ai/memory_version.rb +5 -20
- data/app/models/rails_console_ai/skill.rb +55 -105
- data/app/models/rails_console_ai/skill_version.rb +7 -28
- data/app/views/rails_console_ai/agents/_form.html.erb +9 -34
- data/app/views/rails_console_ai/agents/diff.html.erb +1 -5
- data/app/views/rails_console_ai/agents/new.html.erb +0 -16
- data/app/views/rails_console_ai/memories/_form.html.erb +6 -13
- data/app/views/rails_console_ai/memories/diff.html.erb +1 -5
- data/app/views/rails_console_ai/memories/index.html.erb +13 -1
- data/app/views/rails_console_ai/memories/new.html.erb +0 -15
- data/app/views/rails_console_ai/memories/show.html.erb +23 -0
- data/app/views/rails_console_ai/skills/_form.html.erb +7 -29
- data/app/views/rails_console_ai/skills/diff.html.erb +1 -8
- data/app/views/rails_console_ai/skills/new.html.erb +0 -17
- data/config/routes.rb +3 -3
- data/lib/generators/rails_console_ai/templates/initializer.rb +5 -5
- data/lib/rails_console_ai/agent_loader.rb +18 -10
- data/lib/rails_console_ai/agent_runner.rb +66 -4
- data/lib/rails_console_ai/channel/console.rb +1 -1
- data/lib/rails_console_ai/configuration.rb +51 -33
- data/lib/rails_console_ai/conversation_engine.rb +3 -3
- data/lib/rails_console_ai/safety_guards.rb +36 -0
- data/lib/rails_console_ai/session_logger.rb +4 -0
- data/lib/rails_console_ai/skill_loader.rb +18 -9
- data/lib/rails_console_ai/slack_bot.rb +2 -2
- data/lib/rails_console_ai/storage/database_storage.rb +14 -20
- data/lib/rails_console_ai/sub_agent.rb +1 -1
- data/lib/rails_console_ai/tools/memory_tools.rb +37 -6
- data/lib/rails_console_ai/tools/registry.rb +18 -6
- data/lib/rails_console_ai/version.rb +1 -1
- data/lib/rails_console_ai.rb +92 -71
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7eab1fd8d3a5c8c7a228206f0c723bfa845fa2d344471e0251659658c0c5e571
|
|
4
|
+
data.tar.gz: c607b6cfa9e6834612513f0c75dc1303386f79bcdd96bba203a4fb5d0549f32b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1acd49dcf3370eaab8a8fb8ef77c7dad4746fcb0e0eb2f78935b53c870228a9b53a2cef8dc82608ed94f5f2642de905070fe9e241e9a98d8f408653535ce30d7
|
|
7
|
+
data.tar.gz: bd7ec6fcf50788b45f47f6a7b1948a74fb83cf4ff7fba62687eb2d6e8c75743972f4e83525d87f57096c1a6cd114611a69c5b6e2299a88dea6b02063cdf09adc
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.32.0]
|
|
6
|
+
|
|
7
|
+
- Switch default models to Claude Sonnet 5 and Opus 4.8, and rework pricing to match models by family so dated snapshots and Bedrock IDs are covered
|
|
8
|
+
- Add an abort mechanism for background sub-agents
|
|
9
|
+
- Require human approval before AI-proposed memory changes take effect
|
|
10
|
+
- Keep `explore_output` focused on exploring a previous command's output instead of spinning off into database reads
|
|
11
|
+
- Fix loading `ApplicationController` under the classic autoloader
|
|
12
|
+
|
|
13
|
+
## [0.31.0]
|
|
14
|
+
|
|
15
|
+
- Simplify DB-backed skills, sub-agents, and memories to a single content field per record, replacing the separate frontmatter/body columns and streamlining the models, controllers, and forms
|
|
16
|
+
- Add options for running agents
|
|
17
|
+
- Fix the `/script/release` script
|
|
18
|
+
|
|
5
19
|
## [0.30.0]
|
|
6
20
|
|
|
7
21
|
- Add background agent support — new `run_agent` mechanism to launch agents asynchronously alongside an HTTP API channel
|
|
@@ -16,32 +30,6 @@ All notable changes to this project will be documented in this file.
|
|
|
16
30
|
- Harden `Skill` / `Agent` model accessors to return safe defaults when newly added columns are not yet present
|
|
17
31
|
- Fix built-in agent `.md` files with UTF-8 characters failing to load under US-ASCII locales
|
|
18
32
|
|
|
19
|
-
## [Unreleased]
|
|
20
|
-
|
|
21
|
-
- New skill / memory / agent pages now have a "Paste a .md file" textarea at the top. Paste the contents of a Markdown file with YAML frontmatter (same format used by `.rails_console_ai/{skills,memories,agents}/*.md` and the gem's built-in agents), click "Parse pasted content ↓", and the form fields below are prefilled from the parse. You still click Create to actually save, so the normal proposed-status + version-row flow applies — and you can tweak the parsed content before saving. Useful for moving an existing on-disk record into the versioned DB store
|
|
22
|
-
- `RailsConsoleAi::SkillLoader.parse`, `RailsConsoleAi::AgentLoader.parse`, and `RailsConsoleAi::Tools::MemoryTools.parse` are now public class methods. They return a parsed frontmatter+body hash, or `nil` on malformed input
|
|
23
|
-
|
|
24
|
-
- Usage tracking for DB-backed skills, memories, and agents. New `use_count` and `last_used_at` columns are bumped atomically (one SQL `UPDATE … SET use_count = use_count + 1, last_used_at = NOW()`, no callbacks, no `updated_at` change) when:
|
|
25
|
-
- `activate_skill` resolves a DB skill
|
|
26
|
-
- `recall_memory` resolves a DB memory, or `recall_memories` returns a DB memory in its result set
|
|
27
|
-
- `delegate_task` resolves a DB sub-agent
|
|
28
|
-
File / built-in records have no DB row so they're not tracked (the web UI shows `—` for them). System-prompt summary inclusion does **not** count — counters only move when the AI actively invokes/loads the record. Surfaced in the index tables (with a "Sort: most used" toggle) and on each show page (Times activated / Times recalled / Times invoked, plus Last used)
|
|
29
|
-
- Bugfix: `RailsConsoleAi.migrate!` now always re-runs the `setup_*_tables!` methods so column-add probes execute on upgrades. Previously, when the base table already existed, the outer `unless table_exists?` guard skipped column probes entirely, leaving new columns un-added and causing `NameError: undefined local variable or method 'status'` from `Skill#proposed?` on host apps that hadn't fully migrated
|
|
30
|
-
- Bugfix: `Skill` / `Agent` model accessors for `status` / `approved_by` / `approved_at` / `use_count` / `last_used_at` are now defensive — they return safe defaults via `has_attribute?` rather than raising NameError when the column hasn't been added yet. The `status` inclusion validation is also gated on the column being present
|
|
31
|
-
|
|
32
|
-
- Sub-agents can now be stored in the database, versioned, and approved through the web UI — same workflow as skills. DB-backed agents start as `proposed` and are invisible to `delegate_task` until a human clicks Approve at `/rails_console_ai/agents`; editing an approved agent reverts it to proposed. Built-in (gem-shipped) and file-based agents are pre-approved and continue working unchanged
|
|
33
|
-
- New AI tools `save_agent` and `delete_agent` — the AI can now draft a sub-agent definition (lands as proposed, awaits human approval) and request deletion. `delegate_task` now emits a specific "awaiting human approval" error when the AI references a proposed DB agent
|
|
34
|
-
- New web UI section at `/rails_console_ai/agents` with three-source listing (DB / FILE / BUILTIN badges) plus the full skills-style CRUD + version history + diff + restore + approve. Built-in agents are read-only with a "Create DB override" link that prefills the new-agent form
|
|
35
|
-
- `ai_db_setup` / `ai_db_migrate` now also create `rails_console_ai_agents` and `rails_console_ai_agent_versions` tables idempotently
|
|
36
|
-
- Fix: built-in agent .md files containing UTF-8 (em-dashes, smart quotes) now load correctly. `safe_load_builtin_agents` was using `File.read`'s locale-default encoding, which silently swallowed `Encoding::CompatibilityError` on US-ASCII locales
|
|
37
|
-
|
|
38
|
-
- Skills and memories can now be stored in the database (in addition to the existing on-disk `.rails_console_ai/skills` and `.rails_console_ai/memories` files). DB-backed records are versioned — every save creates a `SkillVersion` / `MemoryVersion` row with `edited_by` and an optional change note
|
|
39
|
-
- DB-backed skills start in a **proposed** state and cannot be activated by the AI until a human approves them in the web UI. Editing an approved skill reverts it to proposed. File-backed skills are unaffected (already git-tracked, considered pre-approved). Memories are not gated
|
|
40
|
-
- `SkillLoader#load_all_skills` and `MemoryTools#load_all_memories` now return the union of DB and file records (DB wins on name collision). `SkillLoader#find_skill` / `skill_summaries` filter out proposed skills so the AI never sees them
|
|
41
|
-
- `save_skill` / `save_memory` AI tools accept an optional `target` parameter (`"db"` default, `"file"` to write to disk) plus `change_note`. When the AI saves a DB skill, the tool response tells it the skill is awaiting human approval
|
|
42
|
-
- New web UI sections at `/rails_console_ai/skills` and `/rails_console_ai/memories` provide list, view, create, edit, delete, version history, side-by-side diff, and restore. Skills also have an Approve button and PROPOSED / APPROVED badges. File-sourced records are surfaced but read-only in the UI
|
|
43
|
-
- `ai_db_setup` / `ai_db_migrate` now create the new `rails_console_ai_skills`, `rails_console_ai_skill_versions`, `rails_console_ai_memories`, and `rails_console_ai_memory_versions` tables idempotently, plus the `status` / `approved_by` / `approved_at` columns on skills
|
|
44
|
-
|
|
45
33
|
## [0.29.0]
|
|
46
34
|
|
|
47
35
|
- Allow steering Slack conversations mid-run by sending follow-up messages that are folded in as user guidance at the next tool-loop boundary
|
data/README.md
CHANGED
|
@@ -291,7 +291,7 @@ RailsConsoleAi.configure do |config|
|
|
|
291
291
|
end
|
|
292
292
|
```
|
|
293
293
|
|
|
294
|
-
Default model: `claude-sonnet-
|
|
294
|
+
Default model: `claude-sonnet-5`. Thinking model: `claude-opus-4-8`. Prompt caching is enabled automatically.
|
|
295
295
|
|
|
296
296
|
### OpenAI
|
|
297
297
|
|
|
@@ -317,8 +317,8 @@ gem 'aws-sdk-bedrockruntime'
|
|
|
317
317
|
RailsConsoleAi.configure do |config|
|
|
318
318
|
config.provider = :bedrock
|
|
319
319
|
config.bedrock_region = 'us-east-1'
|
|
320
|
-
# config.model = 'us.anthropic.claude-sonnet-
|
|
321
|
-
# config.thinking_model = 'us.anthropic.claude-opus-4-
|
|
320
|
+
# config.model = 'us.anthropic.claude-sonnet-5' # default
|
|
321
|
+
# config.thinking_model = 'us.anthropic.claude-opus-4-8' # default
|
|
322
322
|
end
|
|
323
323
|
```
|
|
324
324
|
|
|
@@ -365,25 +365,25 @@ Before adopting a new Claude model, smoke-test it against the Anthropic or Bedro
|
|
|
365
365
|
|
|
366
366
|
```bash
|
|
367
367
|
# Anthropic — provider inferred from the `claude-` prefix
|
|
368
|
-
ANTHROPIC_API_KEY=sk-ant-... bin/smoke_model.rb --model claude-opus-4-
|
|
368
|
+
ANTHROPIC_API_KEY=sk-ant-... bin/smoke_model.rb --model claude-opus-4-8
|
|
369
369
|
|
|
370
370
|
# Bedrock — provider inferred from the regional `us.anthropic.` prefix.
|
|
371
371
|
# Requires the aws-sdk-bedrockruntime gem and AWS credentials in the environment.
|
|
372
|
-
bin/smoke_model.rb --model us.anthropic.claude-opus-4-
|
|
372
|
+
bin/smoke_model.rb --model us.anthropic.claude-opus-4-8
|
|
373
373
|
|
|
374
374
|
# Bedrock in another region
|
|
375
|
-
bin/smoke_model.rb --model eu.anthropic.claude-opus-4-
|
|
375
|
+
bin/smoke_model.rb --model eu.anthropic.claude-opus-4-8 --region eu-west-1
|
|
376
376
|
|
|
377
377
|
# Subset of checks, e.g. when iterating on cache behavior
|
|
378
|
-
bin/smoke_model.rb --model claude-sonnet-
|
|
378
|
+
bin/smoke_model.rb --model claude-sonnet-5 --checks cache
|
|
379
379
|
|
|
380
380
|
# Force a provider when the model ID is ambiguous
|
|
381
|
-
bin/smoke_model.rb --provider anthropic --model claude-opus-4-
|
|
381
|
+
bin/smoke_model.rb --provider anthropic --model claude-opus-4-8
|
|
382
382
|
```
|
|
383
383
|
|
|
384
384
|
`DEBUG=1` enables the providers' raw request/response logging.
|
|
385
385
|
|
|
386
|
-
|
|
386
|
+
Pricing, default max tokens, and parameter support (e.g. which families reject `temperature`) are keyed by model family in `Configuration::MODEL_FAMILIES` (`lib/rails_console_ai/configuration.rb`). Family matching is by substring, so one entry covers bare IDs, dated snapshots, and Bedrock inference profiles (`us.` / `global.` prefixes). When adopting a new model family, add an entry there and smoke-test it.
|
|
387
387
|
|
|
388
388
|
## Configuration
|
|
389
389
|
|
|
@@ -513,14 +513,36 @@ id = RailsConsoleAi.run_agent("How many users signed up yesterday?", name: 'dail
|
|
|
513
513
|
# => 4821 (Integer session id, returned immediately)
|
|
514
514
|
|
|
515
515
|
RailsConsoleAi.check_agent(id)
|
|
516
|
-
# => 'queued' | 'running' | 'ready' | 'failed' | nil
|
|
516
|
+
# => 'queued' | 'running' | 'ready' | 'failed' | 'aborted' | nil
|
|
517
517
|
|
|
518
518
|
RailsConsoleAi.get_agent_response(id)
|
|
519
519
|
# => { status: 'ready', result: "1,432 users signed up yesterday.\n", error: nil }
|
|
520
|
+
|
|
521
|
+
RailsConsoleAi.abort_agent(id)
|
|
522
|
+
# => true (aborted) | false (already finished, or unknown id)
|
|
520
523
|
```
|
|
521
524
|
|
|
525
|
+
`abort_agent` cancels a run: a queued run is never picked up, and a run already executing keeps going but its result is discarded when it completes -- the session stays `status='aborted'`.
|
|
526
|
+
|
|
522
527
|
`run_agent` enqueues a row in the sessions table with `mode='agent_api'` and `status='queued'`. A separate long-running rake task picks them up and runs each in its own thread using the same engine that powers `ai "..."` in the console.
|
|
523
528
|
|
|
529
|
+
### Per-run options
|
|
530
|
+
|
|
531
|
+
`run_agent` accepts two extra keyword arguments to tune individual runs:
|
|
532
|
+
|
|
533
|
+
```ruby
|
|
534
|
+
RailsConsoleAi.run_agent(
|
|
535
|
+
"Trace why nightly billing is double-charging some accounts",
|
|
536
|
+
use_thinking_model: true, # run on the thinking-tier model (e.g. Opus)
|
|
537
|
+
max_wall_clock_seconds: 1800 # hard kill after 30 minutes; pass nil for no cap
|
|
538
|
+
)
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
- `use_thinking_model:` (default `false`) — switches the run to `config.thinking_model` (or the provider default thinking model) for the duration of the agent. Useful for harder, multi-step problems.
|
|
542
|
+
- `max_wall_clock_seconds:` (default `600`) — hard ceiling on wall-clock time. If the run exceeds the cap, the worker thread is killed and the session is marked `status='failed'` with `error_message: "exceeded max_wall_clock_seconds (Ns)"`. Pass `nil` to opt out of any cap.
|
|
543
|
+
|
|
544
|
+
These (along with any future per-run options) are stored in a JSON `options` column on the session row, so they survive the handoff to the background runner.
|
|
545
|
+
|
|
524
546
|
### Running the background runner
|
|
525
547
|
|
|
526
548
|
```bash
|
|
@@ -535,7 +557,7 @@ Run it separately from your web server, alongside (or instead of) the Slack bot.
|
|
|
535
557
|
|
|
536
558
|
### Requirements
|
|
537
559
|
|
|
538
|
-
- `RailsConsoleAi.setup!` must have been run so the sessions table has the `status`, `result`, and `
|
|
560
|
+
- `RailsConsoleAi.setup!` must have been run so the sessions table has the `status`, `result`, `error_message`, and `options` columns. `ai_db_setup` (or `ai_db_migrate` on existing installs) handles this.
|
|
539
561
|
- `session_logging` must be enabled (it is by default).
|
|
540
562
|
|
|
541
563
|
### Behavior notes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module RailsConsoleAi
|
|
2
|
-
class AgentVersionsController < ApplicationController
|
|
2
|
+
class AgentVersionsController < RailsConsoleAi::ApplicationController
|
|
3
3
|
before_action :load_agent
|
|
4
4
|
|
|
5
5
|
def index
|
|
@@ -13,14 +13,7 @@ module RailsConsoleAi
|
|
|
13
13
|
def restore
|
|
14
14
|
version = @agent.versions.find(params[:id])
|
|
15
15
|
@agent.update_with_version!(
|
|
16
|
-
{
|
|
17
|
-
name: version.name,
|
|
18
|
-
description: version.description,
|
|
19
|
-
body: version.body,
|
|
20
|
-
max_rounds: version.max_rounds,
|
|
21
|
-
model: version.model,
|
|
22
|
-
tools: Array(version.tools)
|
|
23
|
-
},
|
|
16
|
+
{ content: version.content },
|
|
24
17
|
edited_by: params[:edited_by].presence || 'web',
|
|
25
18
|
change_note: "Restored from version ##{version.id}"
|
|
26
19
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'rails_console_ai/agent_loader'
|
|
2
2
|
|
|
3
3
|
module RailsConsoleAi
|
|
4
|
-
class AgentsController < ApplicationController
|
|
4
|
+
class AgentsController < RailsConsoleAi::ApplicationController
|
|
5
5
|
before_action :load_agent, only: [:show, :edit, :update, :destroy, :approve]
|
|
6
6
|
|
|
7
7
|
def index
|
|
@@ -32,14 +32,17 @@ module RailsConsoleAi
|
|
|
32
32
|
a['source'] == :builtin && a['name'].to_s.downcase == params[:from_builtin].to_s.downcase
|
|
33
33
|
}
|
|
34
34
|
if builtin
|
|
35
|
-
@agent.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
@agent.content = AgentLoader.dump(
|
|
36
|
+
name: builtin['name'],
|
|
37
|
+
description: builtin['description'],
|
|
38
|
+
body: builtin['body'],
|
|
39
|
+
max_rounds: builtin['max_rounds'],
|
|
40
|
+
model: builtin['model'],
|
|
41
|
+
tools: Array(builtin['tools'])
|
|
42
|
+
)
|
|
41
43
|
end
|
|
42
44
|
end
|
|
45
|
+
@agent.content ||= new_agent_template
|
|
43
46
|
end
|
|
44
47
|
|
|
45
48
|
def create
|
|
@@ -57,34 +60,6 @@ module RailsConsoleAi
|
|
|
57
60
|
end
|
|
58
61
|
end
|
|
59
62
|
|
|
60
|
-
# POST /agents/import — parse a pasted .md blob and re-render `new` with fields prefilled.
|
|
61
|
-
def import
|
|
62
|
-
content = params[:content].to_s
|
|
63
|
-
if content.strip.empty?
|
|
64
|
-
redirect_to new_agent_path, alert: 'Nothing to parse — paste the .md content into the box first.'
|
|
65
|
-
return
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
parsed = AgentLoader.parse(content)
|
|
69
|
-
if parsed.nil? || parsed['name'].to_s.strip.empty?
|
|
70
|
-
redirect_to new_agent_path,
|
|
71
|
-
alert: 'Could not parse. Expected YAML frontmatter (between `---` lines) with at least a `name` field, followed by the agent body.'
|
|
72
|
-
return
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
@agent = Agent.new(
|
|
76
|
-
name: parsed['name'],
|
|
77
|
-
description: parsed['description'],
|
|
78
|
-
body: parsed['body'],
|
|
79
|
-
max_rounds: parsed['max_rounds'],
|
|
80
|
-
model: parsed['model']
|
|
81
|
-
)
|
|
82
|
-
@agent.tools = Array(parsed['tools'])
|
|
83
|
-
|
|
84
|
-
flash.now[:notice] = "Parsed \"#{parsed['name']}\" from pasted content. Review the fields below and click Create agent to save to the DB."
|
|
85
|
-
render :new
|
|
86
|
-
end
|
|
87
|
-
|
|
88
63
|
def edit
|
|
89
64
|
redirect_to agents_path, alert: read_only_message and return unless @agent.is_a?(RailsConsoleAi::Agent)
|
|
90
65
|
end
|
|
@@ -136,9 +111,8 @@ module RailsConsoleAi
|
|
|
136
111
|
@agent = Agent.find(params[:agent_id])
|
|
137
112
|
@from = @agent.versions.find(params[:from])
|
|
138
113
|
@to = params[:to].present? ? @agent.versions.find(params[:to]) : nil
|
|
139
|
-
@to_label
|
|
140
|
-
@
|
|
141
|
-
@to_tools = @to ? Array(@to.tools) : Array(@agent.tools)
|
|
114
|
+
@to_label = @to ? "Version ##{@to.id}" : 'Current'
|
|
115
|
+
@to_content = @to ? @to.content : @agent.content
|
|
142
116
|
end
|
|
143
117
|
|
|
144
118
|
private
|
|
@@ -163,22 +137,25 @@ module RailsConsoleAi
|
|
|
163
137
|
end
|
|
164
138
|
|
|
165
139
|
def agent_params
|
|
166
|
-
{
|
|
167
|
-
name: params.require(:agent)[:name],
|
|
168
|
-
description: params[:agent][:description],
|
|
169
|
-
body: params[:agent][:body],
|
|
170
|
-
max_rounds: params[:agent][:max_rounds].presence&.to_i,
|
|
171
|
-
model: params[:agent][:model].presence,
|
|
172
|
-
tools: split_lines(params[:agent][:tools])
|
|
173
|
-
}
|
|
140
|
+
{ content: params.require(:agent)[:content].to_s }
|
|
174
141
|
end
|
|
175
142
|
|
|
176
143
|
def edited_by_param
|
|
177
144
|
params[:edited_by].presence || 'web'
|
|
178
145
|
end
|
|
179
146
|
|
|
180
|
-
def
|
|
181
|
-
|
|
147
|
+
def new_agent_template
|
|
148
|
+
<<~MD
|
|
149
|
+
---
|
|
150
|
+
name:
|
|
151
|
+
description:
|
|
152
|
+
max_rounds:
|
|
153
|
+
model:
|
|
154
|
+
tools: []
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
Persona, strategy, rules…
|
|
158
|
+
MD
|
|
182
159
|
end
|
|
183
160
|
|
|
184
161
|
def slugify(name)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require 'rails_console_ai/tools/memory_tools'
|
|
2
2
|
|
|
3
3
|
module RailsConsoleAi
|
|
4
|
-
class MemoriesController < ApplicationController
|
|
5
|
-
before_action :load_memory, only: [:show, :edit, :update, :destroy]
|
|
4
|
+
class MemoriesController < RailsConsoleAi::ApplicationController
|
|
5
|
+
before_action :load_memory, only: [:show, :edit, :update, :destroy, :approve]
|
|
6
6
|
|
|
7
7
|
def index
|
|
8
8
|
@memories = Tools::MemoryTools.new.load_all_memories
|
|
@@ -25,32 +25,7 @@ module RailsConsoleAi
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def new
|
|
28
|
-
@memory = Memory.new
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# POST /memories/import — parse a pasted .md blob and re-render `new` with fields prefilled.
|
|
32
|
-
def import
|
|
33
|
-
content = params[:content].to_s
|
|
34
|
-
if content.strip.empty?
|
|
35
|
-
redirect_to new_memory_path, alert: 'Nothing to parse — paste the .md content into the box first.'
|
|
36
|
-
return
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
parsed = Tools::MemoryTools.parse(content)
|
|
40
|
-
if parsed.nil? || parsed['name'].to_s.strip.empty?
|
|
41
|
-
redirect_to new_memory_path,
|
|
42
|
-
alert: 'Could not parse. Expected YAML frontmatter (between `---` lines) with at least a `name` field, followed by the memory body.'
|
|
43
|
-
return
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
@memory = Memory.new(
|
|
47
|
-
name: parsed['name'],
|
|
48
|
-
description: parsed['description']
|
|
49
|
-
)
|
|
50
|
-
@memory.tags = Array(parsed['tags'])
|
|
51
|
-
|
|
52
|
-
flash.now[:notice] = "Parsed \"#{parsed['name']}\" from pasted content. Review the fields below and click Create memory to save to the DB."
|
|
53
|
-
render :new
|
|
28
|
+
@memory = Memory.new(content: new_memory_template)
|
|
54
29
|
end
|
|
55
30
|
|
|
56
31
|
def create
|
|
@@ -98,13 +73,32 @@ module RailsConsoleAi
|
|
|
98
73
|
end
|
|
99
74
|
end
|
|
100
75
|
|
|
76
|
+
def approve
|
|
77
|
+
redirect_to memories_path, alert: file_memory_message and return unless @memory.is_a?(RailsConsoleAi::Memory)
|
|
78
|
+
|
|
79
|
+
approver = params[:approved_by].presence ||
|
|
80
|
+
(request.respond_to?(:remote_user) && request.remote_user.presence) ||
|
|
81
|
+
'web'
|
|
82
|
+
|
|
83
|
+
if @memory.approved?
|
|
84
|
+
redirect_to memory_path(@memory), notice: 'Memory is already approved.'
|
|
85
|
+
return
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
begin
|
|
89
|
+
@memory.approve!(approved_by: approver)
|
|
90
|
+
redirect_to memory_path(@memory), notice: "Approved by #{approver}. The AI can now recall this memory."
|
|
91
|
+
rescue ArgumentError, ActiveRecord::RecordInvalid => e
|
|
92
|
+
redirect_to memory_path(@memory), alert: "Could not approve: #{e.message}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
101
96
|
def diff
|
|
102
97
|
@memory = Memory.find(params[:memory_id])
|
|
103
98
|
@from = @memory.versions.find(params[:from])
|
|
104
99
|
@to = params[:to].present? ? @memory.versions.find(params[:to]) : nil
|
|
105
|
-
@to_label
|
|
106
|
-
@
|
|
107
|
-
@to_tags = @to ? Array(@to.tags) : Array(@memory.tags)
|
|
100
|
+
@to_label = @to ? "Version ##{@to.id}" : 'Current'
|
|
101
|
+
@to_content = @to ? @to.content : @memory.content
|
|
108
102
|
end
|
|
109
103
|
|
|
110
104
|
private
|
|
@@ -130,19 +124,22 @@ module RailsConsoleAi
|
|
|
130
124
|
end
|
|
131
125
|
|
|
132
126
|
def memory_params
|
|
133
|
-
{
|
|
134
|
-
name: params.require(:memory)[:name],
|
|
135
|
-
description: params[:memory][:description],
|
|
136
|
-
tags: split_csv(params[:memory][:tags])
|
|
137
|
-
}
|
|
127
|
+
{ content: params.require(:memory)[:content].to_s }
|
|
138
128
|
end
|
|
139
129
|
|
|
140
130
|
def edited_by_param
|
|
141
131
|
params[:edited_by].presence || 'web'
|
|
142
132
|
end
|
|
143
133
|
|
|
144
|
-
def
|
|
145
|
-
|
|
134
|
+
def new_memory_template
|
|
135
|
+
<<~MD
|
|
136
|
+
---
|
|
137
|
+
name:
|
|
138
|
+
tags: []
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
The fact or pattern you're persisting.
|
|
142
|
+
MD
|
|
146
143
|
end
|
|
147
144
|
|
|
148
145
|
def slugify(name)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module RailsConsoleAi
|
|
2
|
-
class MemoryVersionsController < ApplicationController
|
|
2
|
+
class MemoryVersionsController < RailsConsoleAi::ApplicationController
|
|
3
3
|
before_action :load_memory
|
|
4
4
|
|
|
5
5
|
def index
|
|
@@ -13,11 +13,7 @@ module RailsConsoleAi
|
|
|
13
13
|
def restore
|
|
14
14
|
version = @memory.versions.find(params[:id])
|
|
15
15
|
@memory.update_with_version!(
|
|
16
|
-
{
|
|
17
|
-
name: version.name,
|
|
18
|
-
description: version.description,
|
|
19
|
-
tags: Array(version.tags)
|
|
20
|
-
},
|
|
16
|
+
{ content: version.content },
|
|
21
17
|
edited_by: params[:edited_by].presence || 'web',
|
|
22
18
|
change_note: "Restored from version ##{version.id}"
|
|
23
19
|
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module RailsConsoleAi
|
|
2
|
-
class SkillVersionsController < ApplicationController
|
|
2
|
+
class SkillVersionsController < RailsConsoleAi::ApplicationController
|
|
3
3
|
before_action :load_skill
|
|
4
4
|
|
|
5
5
|
def index
|
|
@@ -13,13 +13,7 @@ module RailsConsoleAi
|
|
|
13
13
|
def restore
|
|
14
14
|
version = @skill.versions.find(params[:id])
|
|
15
15
|
@skill.update_with_version!(
|
|
16
|
-
{
|
|
17
|
-
name: version.name,
|
|
18
|
-
description: version.description,
|
|
19
|
-
body: version.body,
|
|
20
|
-
tags: Array(version.tags),
|
|
21
|
-
bypass_guards_for_methods: Array(version.bypass_guards_for_methods)
|
|
22
|
-
},
|
|
16
|
+
{ content: version.content },
|
|
23
17
|
edited_by: params[:edited_by].presence || 'web',
|
|
24
18
|
change_note: "Restored from version ##{version.id}"
|
|
25
19
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'rails_console_ai/skill_loader'
|
|
2
2
|
|
|
3
3
|
module RailsConsoleAi
|
|
4
|
-
class SkillsController < ApplicationController
|
|
4
|
+
class SkillsController < RailsConsoleAi::ApplicationController
|
|
5
5
|
before_action :load_skill, only: [:show, :edit, :update, :destroy, :approve]
|
|
6
6
|
|
|
7
7
|
def index
|
|
@@ -26,37 +26,7 @@ module RailsConsoleAi
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def new
|
|
29
|
-
@skill = Skill.new
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# POST /skills/import — accepts a pasted .md blob in params[:content], parses
|
|
33
|
-
# YAML frontmatter + body, and re-renders `new` with the fields pre-populated.
|
|
34
|
-
# The user reviews + clicks Create skill to actually persist (normal proposed-
|
|
35
|
-
# status + version-row flow applies).
|
|
36
|
-
def import
|
|
37
|
-
content = params[:content].to_s
|
|
38
|
-
if content.strip.empty?
|
|
39
|
-
redirect_to new_skill_path, alert: 'Nothing to parse — paste the .md content into the box first.'
|
|
40
|
-
return
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
parsed = SkillLoader.parse(content)
|
|
44
|
-
if parsed.nil? || parsed['name'].to_s.strip.empty?
|
|
45
|
-
redirect_to new_skill_path,
|
|
46
|
-
alert: 'Could not parse. Expected YAML frontmatter (between `---` lines) with at least a `name` field, followed by a markdown body.'
|
|
47
|
-
return
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
@skill = Skill.new(
|
|
51
|
-
name: parsed['name'],
|
|
52
|
-
description: parsed['description'],
|
|
53
|
-
body: parsed['body']
|
|
54
|
-
)
|
|
55
|
-
@skill.tags = Array(parsed['tags'])
|
|
56
|
-
@skill.bypass_guards_for_methods = Array(parsed['bypass_guards_for_methods'])
|
|
57
|
-
|
|
58
|
-
flash.now[:notice] = "Parsed \"#{parsed['name']}\" from pasted content. Review the fields below and click Create skill to save to the DB."
|
|
59
|
-
render :new
|
|
29
|
+
@skill = Skill.new(content: new_skill_template)
|
|
60
30
|
end
|
|
61
31
|
|
|
62
32
|
def create
|
|
@@ -130,10 +100,8 @@ module RailsConsoleAi
|
|
|
130
100
|
@from = @skill.versions.find(params[:from])
|
|
131
101
|
@to = params[:to].present? ? @skill.versions.find(params[:to]) : nil
|
|
132
102
|
# If `to` is omitted, diff against the current skill.
|
|
133
|
-
@to_label
|
|
134
|
-
@
|
|
135
|
-
@to_tags = @to ? Array(@to.tags) : Array(@skill.tags)
|
|
136
|
-
@to_bypass = @to ? Array(@to.bypass_guards_for_methods) : Array(@skill.bypass_guards_for_methods)
|
|
103
|
+
@to_label = @to ? "Version ##{@to.id}" : 'Current'
|
|
104
|
+
@to_content = @to ? @to.content : @skill.content
|
|
137
105
|
end
|
|
138
106
|
|
|
139
107
|
private
|
|
@@ -165,25 +133,28 @@ module RailsConsoleAi
|
|
|
165
133
|
end
|
|
166
134
|
|
|
167
135
|
def skill_params
|
|
168
|
-
{
|
|
169
|
-
name: params.require(:skill)[:name],
|
|
170
|
-
description: params[:skill][:description],
|
|
171
|
-
body: params[:skill][:body],
|
|
172
|
-
tags: split_csv(params[:skill][:tags]),
|
|
173
|
-
bypass_guards_for_methods: split_lines(params[:skill][:bypass_guards_for_methods])
|
|
174
|
-
}
|
|
136
|
+
{ content: params.require(:skill)[:content].to_s }
|
|
175
137
|
end
|
|
176
138
|
|
|
177
139
|
def edited_by_param
|
|
178
140
|
params[:edited_by].presence || 'web'
|
|
179
141
|
end
|
|
180
142
|
|
|
181
|
-
def
|
|
182
|
-
|
|
183
|
-
|
|
143
|
+
def new_skill_template
|
|
144
|
+
<<~MD
|
|
145
|
+
---
|
|
146
|
+
name:
|
|
147
|
+
description:
|
|
148
|
+
tags: []
|
|
149
|
+
bypass_guards_for_methods: []
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## When to use
|
|
153
|
+
|
|
154
|
+
## Recipe
|
|
184
155
|
|
|
185
|
-
|
|
186
|
-
|
|
156
|
+
## Notes
|
|
157
|
+
MD
|
|
187
158
|
end
|
|
188
159
|
|
|
189
160
|
def slugify(name)
|