solid_agents 0.1.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 (40) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +18 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +111 -0
  5. data/Rakefile +12 -0
  6. data/app/controllers/solid_agents/agents_controller.rb +33 -0
  7. data/app/controllers/solid_agents/application_controller.rb +8 -0
  8. data/app/controllers/solid_agents/runs_controller.rb +38 -0
  9. data/app/helpers/solid_agents/runs_helper.rb +14 -0
  10. data/app/jobs/solid_agents/execute_run_job.rb +12 -0
  11. data/app/models/solid_agents/agent.rb +26 -0
  12. data/app/models/solid_agents/artifact.rb +11 -0
  13. data/app/models/solid_agents/config.rb +14 -0
  14. data/app/models/solid_agents/record.rb +11 -0
  15. data/app/models/solid_agents/run.rb +41 -0
  16. data/app/models/solid_agents/run_event.rb +12 -0
  17. data/app/services/solid_agents/runs/context_builder.rb +15 -0
  18. data/app/services/solid_agents/runs/dispatch.rb +31 -0
  19. data/app/services/solid_agents/runs/executor.rb +29 -0
  20. data/app/services/solid_agents/runs/prompt_builder.rb +33 -0
  21. data/app/views/layouts/solid_agents/_style.html.erb +128 -0
  22. data/app/views/layouts/solid_agents/application.html.erb +26 -0
  23. data/app/views/solid_agents/agents/edit.html.erb +17 -0
  24. data/app/views/solid_agents/agents/index.html.erb +27 -0
  25. data/app/views/solid_agents/agents/show.html.erb +11 -0
  26. data/app/views/solid_agents/runs/index.html.erb +27 -0
  27. data/app/views/solid_agents/runs/show.html.erb +48 -0
  28. data/config/locales/en.yml +6 -0
  29. data/config/routes.rb +9 -0
  30. data/lib/generators/solid_agents/install/USAGE +4 -0
  31. data/lib/generators/solid_agents/install/install_generator.rb +34 -0
  32. data/lib/generators/solid_agents/install/templates/config/initializers/solid_agents.rb +11 -0
  33. data/lib/generators/solid_agents/install/templates/db/agent_schema.rb +89 -0
  34. data/lib/solid_agents/engine.rb +16 -0
  35. data/lib/solid_agents/runtime/adapter.rb +22 -0
  36. data/lib/solid_agents/runtime/openclaw_adapter.rb +12 -0
  37. data/lib/solid_agents/runtime/tinyclaw_adapter.rb +12 -0
  38. data/lib/solid_agents/version.rb +5 -0
  39. data/lib/solid_agents.rb +32 -0
  40. metadata +179 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f47eb3e38b46a127496f51a08a4a9613becb2abc109922b1ad4f5f2b203c89a2
4
+ data.tar.gz: ad064cee60b057a8eb9dc3baccf5f06c48411550f91f984a35ff8a32fca51d5e
5
+ SHA512:
6
+ metadata.gz: fd4fe012fb44f5f2707152fae422cbc0a4a63b2342e3fac2e485231808d46c460c8465642fa747f37e7aa14820763f8a1bc9a722c65e1734b76db14c1153892f
7
+ data.tar.gz: 31a7f4a1ecc53a08710c656b7c7009d118e01de305f273d112984d460e1a826a0f5b4c14edbe5833bcb4589ffd1b6fa620346b8b23f61b52f265c3afd49eaf4b
data/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ All notable changes to `solid_agents` will be documented in this file.
4
+
5
+ ## [v0.1.0] - 2026-03-22
6
+
7
+ Initial public release (WIP).
8
+
9
+ - Introduced `solid_agents` Rails engine for database-backed agent run orchestration.
10
+ - Added run lifecycle models and persistence for runs, events, artifacts, agents, and config.
11
+ - Added dispatch and execution flow with runtime adapters for TinyClaw and OpenClaw.
12
+ - Added built-in UI/controllers for managing agents and inspecting runs.
13
+ - Added installer generator, schema template, and base configuration defaults.
14
+
15
+ Status notes:
16
+
17
+ - This release is still WIP and not production-ready.
18
+ - Breaking changes are expected before `1.0`.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Solid Agent
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # SolidAgents
2
+
3
+ **Automation workflows for Rails apps, powered by agent runtimes.**
4
+
5
+ > [!WARNING]
6
+ > `solid_agents` is an early release and still a work in progress.
7
+ > APIs, schema, runtime behavior, and configuration may change between minor releases.
8
+ > Expect breaking changes before `1.0`.
9
+ > Not production-ready yet.
10
+
11
+ `solid_agents` is a Rails engine that consumes observability/incident APIs (for example from `solid_events`), stores automation run state, dispatches work to runtime executors (TinyClaw in development, OpenClaw in production), and provides a built-in dashboard for operations.
12
+
13
+ ## Scope
14
+
15
+ `solid_agents` is strictly for automation and execution:
16
+
17
+ - Consume incident/trace APIs and operator instructions
18
+ - Plan/execute workflows (triage, patch, test, PR, QA, review loops)
19
+ - Track run lifecycle, artifacts, and runtime outputs
20
+ - Route tasks to runtime adapters (TinyClaw/OpenClaw)
21
+
22
+ It does **not** own observability storage or incident detection/state as a source of truth. That belongs in `solid_events`.
23
+
24
+ ## Features
25
+
26
+ - DB-backed run lifecycle (`queued`, `running`, `succeeded`, `failed`)
27
+ - Separate agent profiles per environment
28
+ - Runtime adapters for TinyClaw and OpenClaw
29
+ - Built-in UI for runs, events, artifacts, and agent configuration
30
+ - Install generator and schema template aligned with Solid gem conventions
31
+
32
+ ## Installation
33
+
34
+ Add this to your Gemfile:
35
+
36
+ ```ruby
37
+ gem "solid_agents"
38
+ ```
39
+
40
+ Run installer:
41
+
42
+ ```bash
43
+ rails generate solid_agents:install
44
+ ```
45
+
46
+ Add a dedicated database in `config/database.yml` (recommended):
47
+
48
+ ```yaml
49
+ production:
50
+ primary: &primary
51
+ <<: *default
52
+ database: app_production
53
+ solid_agents:
54
+ <<: *primary
55
+ database: app_production_solid_agents
56
+ migrations_paths: db/agent_migrate
57
+ ```
58
+
59
+ Configure engine DB connection:
60
+
61
+ ```ruby
62
+ # config/environments/production.rb
63
+ config.solid_agents.connects_to = { database: { writing: :solid_agents } }
64
+ config.solid_agents.default_runtime = :openclaw
65
+ ```
66
+
67
+ Mount the UI:
68
+
69
+ ```ruby
70
+ # config/routes.rb
71
+ authenticate :user, ->(u) { u.admin? } do
72
+ mount SolidAgents::Engine, at: "/solid_agents"
73
+ end
74
+ ```
75
+
76
+ ## Usage
77
+
78
+ Create an agent profile:
79
+
80
+ ```ruby
81
+ SolidAgents::Agent.create!(
82
+ key: "fixer",
83
+ name: "Bug Fixer",
84
+ role: "fixer",
85
+ runtime: Rails.env.production? ? "openclaw" : "tinyclaw",
86
+ working_directory: Rails.root.to_s,
87
+ capabilities_json: {"allow_pr" => Rails.env.production?}
88
+ )
89
+ ```
90
+
91
+ Dispatch from a job:
92
+
93
+ ```ruby
94
+ SolidAgents.dispatch_error(source: solid_error_record, agent_key: "fixer")
95
+ ```
96
+
97
+ ## Configuration
98
+
99
+ ```ruby
100
+ config.solid_agents.tinyclaw_command = "tinyclaw"
101
+ config.solid_agents.openclaw_command = "openclaw"
102
+ config.solid_agents.default_test_command = "bin/rails test"
103
+ config.solid_agents.max_iterations = 8
104
+ ```
105
+
106
+ ## Development
107
+
108
+ ```bash
109
+ bundle install
110
+ bundle exec rake test
111
+ ```
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.pattern = "test/**/*_test.rb"
9
+ t.verbose = true
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class AgentsController < ApplicationController
5
+ before_action :set_agent, only: %i[show edit update]
6
+
7
+ def index
8
+ @agents = SolidAgents::Agent.order(:key)
9
+ end
10
+
11
+ def show; end
12
+
13
+ def edit; end
14
+
15
+ def update
16
+ if @agent.update(agent_params)
17
+ redirect_to agent_path(@agent), notice: "Agent updated."
18
+ else
19
+ render :edit, status: :unprocessable_entity
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def set_agent
26
+ @agent = SolidAgents::Agent.find(params[:id])
27
+ end
28
+
29
+ def agent_params
30
+ params.require(:agent).permit(:name, :role, :runtime, :enabled, :model, :working_directory, :timeout_seconds, :max_iterations, :environment, :system_prompt)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class ApplicationController < SolidAgents.base_controller_class.constantize
5
+ layout "solid_agents/application"
6
+ protect_from_forgery with: :exception
7
+ end
8
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class RunsController < ApplicationController
5
+ helper SolidAgents::RunsHelper
6
+ before_action :set_run, only: %i[show retry]
7
+
8
+ def index
9
+ @runs = SolidAgents::Run.includes(:agent).order(created_at: :desc).limit(100)
10
+ end
11
+
12
+ def show
13
+ @events = @run.events.order(sequence: :asc)
14
+ @artifacts = @run.artifacts.order(created_at: :desc)
15
+ end
16
+
17
+ def retry
18
+ duplicated = @run.dup
19
+ duplicated.status = :queued
20
+ duplicated.started_at = nil
21
+ duplicated.finished_at = nil
22
+ duplicated.error_payload = nil
23
+ duplicated.result_payload = nil
24
+ duplicated.external_key = "retry-#{@run.id}-#{Time.current.to_i}"
25
+ duplicated.save!
26
+ duplicated.append_event!("retried", message: "Run created from retry", payload: {original_run_id: @run.id})
27
+ SolidAgents::ExecuteRunJob.perform_later(duplicated.id)
28
+
29
+ redirect_to run_path(duplicated), notice: "Run retried."
30
+ end
31
+
32
+ private
33
+
34
+ def set_run
35
+ @run = SolidAgents::Run.find(params[:id])
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ module RunsHelper
5
+ def status_badge_class(status)
6
+ case status
7
+ when "succeeded" then "ok"
8
+ when "failed" then "err"
9
+ when "running" then "run"
10
+ else "muted"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class ExecuteRunJob < ActiveJob::Base
5
+ queue_as :default
6
+
7
+ def perform(run_id)
8
+ run = SolidAgents::Run.find(run_id)
9
+ SolidAgents::Runs::Executor.call(run)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class Agent < Record
5
+ self.table_name = "solid_agents_agents"
6
+
7
+ ROLES = %w[fixer reviewer qa custom].freeze
8
+
9
+ has_many :runs, class_name: "SolidAgents::Run", dependent: :nullify
10
+
11
+ validates :key, :name, :runtime, presence: true
12
+ validates :key, uniqueness: {scope: :environment}
13
+ validates :role, inclusion: {in: ROLES}
14
+
15
+ scope :enabled, -> { where(enabled: true) }
16
+ scope :for_environment, ->(env) { where(environment: [nil, env.to_s]) }
17
+
18
+ def runtime_sym
19
+ runtime.to_sym
20
+ end
21
+
22
+ def capability?(name)
23
+ capabilities_json.to_h.fetch(name.to_s, false)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class Artifact < Record
5
+ self.table_name = "solid_agents_artifacts"
6
+
7
+ belongs_to :run, class_name: "SolidAgents::Run"
8
+
9
+ validates :kind, :storage_type, presence: true
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class Config < Record
5
+ self.table_name = "solid_agents_configs"
6
+
7
+ validates :key, presence: true, uniqueness: {scope: :environment}
8
+
9
+ def self.get(key, environment: Rails.env)
10
+ row = where(key: key, environment: [environment, nil]).order(Arel.sql("environment IS NULL")).first
11
+ row&.value_json
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class Record < ActiveRecord::Base
5
+ self.abstract_class = true
6
+
7
+ connects_to(**SolidAgents.connects_to) if SolidAgents.connects_to
8
+ end
9
+ end
10
+
11
+ ActiveSupport.run_load_hooks :solid_agents_record, SolidAgents::Record
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class Run < Record
5
+ self.table_name = "solid_agents_runs"
6
+
7
+ STATUSES = {
8
+ queued: "queued",
9
+ running: "running",
10
+ pr_opened: "pr_opened",
11
+ succeeded: "succeeded",
12
+ failed: "failed",
13
+ canceled: "canceled"
14
+ }.freeze
15
+
16
+ belongs_to :agent, class_name: "SolidAgents::Agent"
17
+ has_many :events, class_name: "SolidAgents::RunEvent", foreign_key: :run_id, dependent: :delete_all
18
+ has_many :artifacts, class_name: "SolidAgents::Artifact", foreign_key: :run_id, dependent: :delete_all
19
+
20
+ enum :status, STATUSES
21
+
22
+ validates :runtime, :environment, :status, :source_type, presence: true
23
+ validates :external_key, uniqueness: true, allow_nil: true
24
+
25
+ before_validation :set_defaults, on: :create
26
+
27
+ def append_event!(event_type, message:, payload: nil)
28
+ next_sequence = (events.maximum(:sequence) || 0) + 1
29
+ events.create!(event_type: event_type, message: message, payload: payload || {}, event_time: Time.current, sequence: next_sequence)
30
+ end
31
+
32
+ private
33
+
34
+ def set_defaults
35
+ self.status ||= :queued
36
+ self.runtime ||= agent&.runtime || SolidAgents.default_runtime.to_s
37
+ self.environment ||= Rails.env
38
+ self.test_command ||= SolidAgents.default_test_command
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class RunEvent < Record
5
+ self.table_name = "solid_agents_run_events"
6
+
7
+ belongs_to :run, class_name: "SolidAgents::Run"
8
+
9
+ validates :event_type, :message, :sequence, :event_time, presence: true
10
+ validates :sequence, uniqueness: {scope: :run_id}
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ module Runs
5
+ class ContextBuilder
6
+ def self.call(source:)
7
+ if source.respond_to?(:attributes)
8
+ source.attributes.slice("id", "message", "class_name", "backtrace", "context")
9
+ else
10
+ {value: source.to_s}
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ module Runs
5
+ class Dispatch
6
+ def self.call(source:, agent_key:)
7
+ environment = Rails.env
8
+ agent = SolidAgents::Agent.enabled.for_environment(environment).find_by!(key: agent_key)
9
+
10
+ run = SolidAgents::Run.create!(
11
+ agent: agent,
12
+ source_type: source.class.name,
13
+ source_id: source.respond_to?(:id) ? source.id : nil,
14
+ runtime: agent.runtime,
15
+ environment: environment,
16
+ external_key: "#{source.class.name}-#{source.respond_to?(:id) ? source.id : source.object_id}-#{Time.current.to_i}",
17
+ repo_path: agent.working_directory,
18
+ base_branch: "main",
19
+ max_iterations: agent.max_iterations
20
+ )
21
+
22
+ context = ContextBuilder.call(source: source)
23
+ run.update!(prompt_payload: context)
24
+ run.append_event!("dispatched", message: "Run dispatched from source", payload: {agent_key: agent.key})
25
+
26
+ SolidAgents::ExecuteRunJob.perform_later(run.id)
27
+ run
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ module Runs
5
+ class Executor
6
+ def self.call(run)
7
+ run.update!(status: :running, started_at: Time.current)
8
+ run.append_event!("runtime_started", message: "Runtime execution started")
9
+
10
+ prompt = PromptBuilder.call(run: run, context: run.prompt_payload || {})
11
+ adapter = SolidAgents.runtime_adapter(run.runtime)
12
+ result = adapter.execute(run: run, prompt: prompt)
13
+
14
+ run.artifacts.create!(kind: "log", label: "runtime_output", storage_type: "inline", content_text: result.output.to_s)
15
+
16
+ if result.ok
17
+ metadata = result.metadata.to_h
18
+ run.update!(status: :succeeded, finished_at: Time.current, result_payload: {output: result.output, metadata: metadata})
19
+ run.append_event!("runtime_succeeded", message: "Runtime execution finished", payload: metadata)
20
+ else
21
+ run.update!(status: :failed, finished_at: Time.current, error_payload: {stderr: result.error, metadata: result.metadata})
22
+ run.append_event!("runtime_failed", message: "Runtime execution failed", payload: result.metadata.to_h.merge("stderr" => result.error.to_s))
23
+ end
24
+
25
+ run
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module SolidAgents
6
+ module Runs
7
+ class PromptBuilder
8
+ def self.call(run:, context:)
9
+ <<~PROMPT
10
+ You are #{run.agent.name} (#{run.agent.role}).
11
+ Goal: fix the reported Rails issue end-to-end.
12
+
13
+ Constraints:
14
+ - Work in repository: #{run.repo_path}
15
+ - Base branch: #{run.base_branch}
16
+ - Test command: #{run.test_command}
17
+ - Max iterations: #{run.max_iterations || SolidAgents.max_iterations}
18
+ - Open PR allowed: #{run.agent.capability?("allow_pr")}
19
+
20
+ Context JSON:
21
+ #{JSON.pretty_generate(context)}
22
+
23
+ Definition of done:
24
+ 1) Root cause identified.
25
+ 2) Code fix applied.
26
+ 3) Tests added or updated.
27
+ 4) Tests executed and passing.
28
+ 5) Return final JSON with keys: status, branch_name, test_summary, pr_url, notes.
29
+ PROMPT
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,128 @@
1
+ <style>
2
+ :root {
3
+ --bg: #f4f7fb;
4
+ --surface: #ffffff;
5
+ --ink: #17202a;
6
+ --muted: #5e6b78;
7
+ --line: #dbe4ee;
8
+ --accent: #0f766e;
9
+ --good: #166534;
10
+ --bad: #b91c1c;
11
+ --run: #7c3aed;
12
+ }
13
+
14
+ * { box-sizing: border-box; }
15
+
16
+ body {
17
+ margin: 0;
18
+ font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
19
+ background: radial-gradient(circle at 5% 0%, #d7f7f0 0%, transparent 35%), var(--bg);
20
+ color: var(--ink);
21
+ }
22
+
23
+ .wrap { width: min(1100px, 94vw); margin: 0 auto; }
24
+
25
+ .topbar {
26
+ background: var(--surface);
27
+ border-bottom: 1px solid var(--line);
28
+ margin-bottom: 1.25rem;
29
+ }
30
+
31
+ .topbar .wrap {
32
+ display: flex;
33
+ align-items: center;
34
+ justify-content: space-between;
35
+ padding: .8rem 0;
36
+ }
37
+
38
+ .topbar h1 {
39
+ font-size: 1.1rem;
40
+ margin: 0;
41
+ letter-spacing: .02em;
42
+ }
43
+
44
+ .topbar nav { display: flex; gap: .75rem; }
45
+
46
+ .topbar a {
47
+ color: var(--ink);
48
+ text-decoration: none;
49
+ padding: .35rem .65rem;
50
+ border-radius: .5rem;
51
+ border: 1px solid transparent;
52
+ }
53
+
54
+ .topbar a:hover { border-color: var(--line); background: #f8fbff; }
55
+
56
+ .card {
57
+ background: var(--surface);
58
+ border: 1px solid var(--line);
59
+ border-radius: .9rem;
60
+ padding: 1rem;
61
+ margin-bottom: 1rem;
62
+ box-shadow: 0 1px 0 rgba(16,24,40,.02);
63
+ }
64
+
65
+ table { width: 100%; border-collapse: collapse; }
66
+
67
+ th, td {
68
+ text-align: left;
69
+ padding: .65rem .5rem;
70
+ border-bottom: 1px solid var(--line);
71
+ font-size: .93rem;
72
+ }
73
+
74
+ th { color: var(--muted); font-size: .8rem; text-transform: uppercase; letter-spacing: .05em; }
75
+
76
+ .badge {
77
+ display: inline-block;
78
+ padding: .2rem .55rem;
79
+ border-radius: .55rem;
80
+ font-size: .75rem;
81
+ font-weight: 700;
82
+ text-transform: uppercase;
83
+ letter-spacing: .04em;
84
+ }
85
+
86
+ .badge.ok { background: #dcfce7; color: var(--good); }
87
+ .badge.err { background: #fee2e2; color: var(--bad); }
88
+ .badge.run { background: #ede9fe; color: var(--run); }
89
+ .badge.muted { background: #eef2f7; color: var(--muted); }
90
+
91
+ .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
92
+
93
+ .flash {
94
+ padding: .6rem .8rem;
95
+ border-radius: .65rem;
96
+ border: 1px solid var(--line);
97
+ }
98
+
99
+ .notice { background: #ecfeff; }
100
+ .alert { background: #fff7ed; }
101
+
102
+ .btn {
103
+ display: inline-block;
104
+ border: 1px solid var(--line);
105
+ background: #fff;
106
+ color: var(--ink);
107
+ border-radius: .55rem;
108
+ padding: .45rem .7rem;
109
+ text-decoration: none;
110
+ cursor: pointer;
111
+ }
112
+
113
+ .btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
114
+
115
+ pre {
116
+ background: #0b1220;
117
+ color: #d1e0ff;
118
+ border-radius: .8rem;
119
+ padding: .85rem;
120
+ overflow: auto;
121
+ font-size: .82rem;
122
+ }
123
+
124
+ @media (max-width: 800px) {
125
+ .grid { grid-template-columns: 1fr; }
126
+ th:nth-child(4), td:nth-child(4) { display: none; }
127
+ }
128
+ </style>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
5
+ <title>SolidAgents</title>
6
+ <%= csrf_meta_tags %>
7
+ <%= csp_meta_tag %>
8
+ <%= render "layouts/solid_agents/style" %>
9
+ </head>
10
+ <body>
11
+ <header class="topbar">
12
+ <div class="wrap">
13
+ <h1>SolidAgents</h1>
14
+ <nav>
15
+ <%= link_to "Runs", runs_path %>
16
+ <%= link_to "Agents", agents_path %>
17
+ </nav>
18
+ </div>
19
+ </header>
20
+ <main class="wrap">
21
+ <% if notice.present? %><p class="flash notice"><%= notice %></p><% end %>
22
+ <% if alert.present? %><p class="flash alert"><%= alert %></p><% end %>
23
+ <%= yield %>
24
+ </main>
25
+ </body>
26
+ </html>
@@ -0,0 +1,17 @@
1
+ <div class="card">
2
+ <h2>Edit Agent <%= @agent.key %></h2>
3
+
4
+ <%= form_with model: @agent, url: agent_path(@agent), method: :patch do |f| %>
5
+ <p><%= f.label :name %><br><%= f.text_field :name %></p>
6
+ <p><%= f.label :role %><br><%= f.select :role, SolidAgents::Agent::ROLES %></p>
7
+ <p><%= f.label :runtime %><br><%= f.select :runtime, %w[tinyclaw openclaw] %></p>
8
+ <p><%= f.label :environment %><br><%= f.text_field :environment %></p>
9
+ <p><%= f.label :model %><br><%= f.text_field :model %></p>
10
+ <p><%= f.label :working_directory %><br><%= f.text_field :working_directory %></p>
11
+ <p><%= f.label :timeout_seconds %><br><%= f.number_field :timeout_seconds %></p>
12
+ <p><%= f.label :max_iterations %><br><%= f.number_field :max_iterations %></p>
13
+ <p><%= f.label :system_prompt %><br><%= f.text_area :system_prompt, rows: 8, cols: 80 %></p>
14
+ <p><%= f.label :enabled %> <%= f.check_box :enabled %></p>
15
+ <p><%= f.submit "Save", class: "btn primary" %></p>
16
+ <% end %>
17
+ </div>
@@ -0,0 +1,27 @@
1
+ <div class="card">
2
+ <h2>Agents</h2>
3
+ <table>
4
+ <thead>
5
+ <tr>
6
+ <th>Key</th>
7
+ <th>Name</th>
8
+ <th>Role</th>
9
+ <th>Runtime</th>
10
+ <th>Env</th>
11
+ <th>Enabled</th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <% @agents.each do |agent| %>
16
+ <tr>
17
+ <td><%= link_to agent.key, agent_path(agent) %></td>
18
+ <td><%= agent.name %></td>
19
+ <td><%= agent.role %></td>
20
+ <td><%= agent.runtime %></td>
21
+ <td><%= agent.environment || "all" %></td>
22
+ <td><%= agent.enabled? ? "yes" : "no" %></td>
23
+ </tr>
24
+ <% end %>
25
+ </tbody>
26
+ </table>
27
+ </div>
@@ -0,0 +1,11 @@
1
+ <div class="card">
2
+ <h2>Agent: <%= @agent.key %></h2>
3
+ <p>Name: <strong><%= @agent.name %></strong></p>
4
+ <p>Role: <strong><%= @agent.role %></strong></p>
5
+ <p>Runtime: <strong><%= @agent.runtime %></strong></p>
6
+ <p>Environment: <strong><%= @agent.environment || "all" %></strong></p>
7
+ <p>Working directory: <code><%= @agent.working_directory %></code></p>
8
+ <p>Model: <code><%= @agent.model %></code></p>
9
+ <p>Enabled: <strong><%= @agent.enabled? ? "yes" : "no" %></strong></p>
10
+ <%= link_to "Edit", edit_agent_path(@agent), class: "btn" %>
11
+ </div>
@@ -0,0 +1,27 @@
1
+ <div class="card">
2
+ <h2>Runs</h2>
3
+ <table>
4
+ <thead>
5
+ <tr>
6
+ <th>ID</th>
7
+ <th>Agent</th>
8
+ <th>Status</th>
9
+ <th>Runtime</th>
10
+ <th>Source</th>
11
+ <th>Started</th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <% @runs.each do |run| %>
16
+ <tr>
17
+ <td><%= link_to "##{run.id}", run_path(run) %></td>
18
+ <td><%= run.agent&.key %></td>
19
+ <td><span class="badge <%= status_badge_class(run.status) %>"><%= run.status %></span></td>
20
+ <td><%= run.runtime %></td>
21
+ <td><%= [run.source_type, run.source_id].compact.join("#") %></td>
22
+ <td><%= run.started_at || run.created_at %></td>
23
+ </tr>
24
+ <% end %>
25
+ </tbody>
26
+ </table>
27
+ </div>
@@ -0,0 +1,48 @@
1
+ <div class="card">
2
+ <h2>Run #<%= @run.id %></h2>
3
+ <p>
4
+ Agent: <strong><%= @run.agent.key %></strong> |
5
+ Runtime: <strong><%= @run.runtime %></strong> |
6
+ Status: <span class="badge <%= status_badge_class(@run.status) %>"><%= @run.status %></span>
7
+ </p>
8
+ <p>
9
+ Source: <%= [@run.source_type, @run.source_id].compact.join("#") %>
10
+ </p>
11
+ <%= button_to "Retry", retry_run_path(@run), method: :post, class: "btn primary" %>
12
+ </div>
13
+
14
+ <div class="grid">
15
+ <div class="card">
16
+ <h3>Events</h3>
17
+ <table>
18
+ <thead>
19
+ <tr>
20
+ <th>#</th>
21
+ <th>Type</th>
22
+ <th>Message</th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ <% @events.each do |event| %>
27
+ <tr>
28
+ <td><%= event.sequence %></td>
29
+ <td><%= event.event_type %></td>
30
+ <td><%= event.message %></td>
31
+ </tr>
32
+ <% end %>
33
+ </tbody>
34
+ </table>
35
+ </div>
36
+
37
+ <div class="card">
38
+ <h3>Artifacts</h3>
39
+ <% @artifacts.each do |artifact| %>
40
+ <h4><%= artifact.kind %> - <%= artifact.label %></h4>
41
+ <% if artifact.content_text.present? %>
42
+ <pre><%= artifact.content_text %></pre>
43
+ <% else %>
44
+ <pre><%= artifact.content_json %></pre>
45
+ <% end %>
46
+ <% end %>
47
+ </div>
48
+ </div>
@@ -0,0 +1,6 @@
1
+ en:
2
+ solid_agents:
3
+ runs:
4
+ title: "Agent Runs"
5
+ agents:
6
+ title: "Agents"
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ SolidAgents::Engine.routes.draw do
4
+ resources :agents, only: %i[index show edit update]
5
+
6
+ resources :runs, only: %i[index show], path: "" do
7
+ member { post :retry }
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ Run the schema install and add a `solid_agents` database in your app's `config/database.yml`.
2
+ Then mount the UI in routes:
3
+
4
+ mount SolidAgents::Engine, at: "/solid_agents"
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path("templates", __dir__)
6
+
7
+ def add_schema
8
+ template "db/agent_schema.rb"
9
+ end
10
+
11
+ def add_initializer
12
+ template "config/initializers/solid_agents.rb"
13
+ end
14
+
15
+ def configure_environment
16
+ insert_into_file Pathname(destination_root).join("config/environments/production.rb"), after: /^([ \t]*).*?(?=\nend)$/ do
17
+ [
18
+ "",
19
+ '\\1# Configure Solid Agent',
20
+ '\\1config.solid_agents.connects_to = { database: { writing: :solid_agents } }',
21
+ '\\1config.solid_agents.default_runtime = :openclaw'
22
+ ].join("\n")
23
+ end
24
+ end
25
+
26
+ def ensure_migrations_directory
27
+ empty_directory "db/agent_migrate"
28
+ keep_path = Pathname(destination_root).join("db/agent_migrate/.gitkeep")
29
+ return if keep_path.exist?
30
+
31
+ create_file "db/agent_migrate/.gitkeep", "# Keep the SolidAgents migrations directory tracked by Git.\n"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.configure do
4
+ # Optional: override in specific environments.
5
+ # config.solid_agents.connects_to = { database: { writing: :solid_agents } }
6
+
7
+ config.solid_agents.tinyclaw_command = ENV.fetch("SOLID_AGENTS_TINYCLAW_COMMAND", "tinyclaw")
8
+ config.solid_agents.openclaw_command = ENV.fetch("SOLID_AGENTS_OPENCLAW_COMMAND", "openclaw")
9
+ config.solid_agents.default_test_command = ENV.fetch("SOLID_AGENTS_TEST_COMMAND", "bin/rails test")
10
+ config.solid_agents.max_iterations = ENV.fetch("SOLID_AGENTS_MAX_ITERATIONS", 8).to_i
11
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ ActiveRecord::Schema[6.1].define do
4
+ create_table :solid_agents_agents, force: :cascade do |t|
5
+ t.string :key, null: false
6
+ t.string :name, null: false
7
+ t.string :role, null: false, default: "fixer"
8
+ t.string :runtime, null: false, default: "tinyclaw"
9
+ t.boolean :enabled, null: false, default: true
10
+ t.string :environment
11
+ t.string :model
12
+ t.text :system_prompt
13
+ t.json :capabilities_json, default: {}
14
+ t.string :working_directory
15
+ t.integer :timeout_seconds, null: false, default: 900
16
+ t.integer :max_iterations, null: false, default: 8
17
+ t.timestamps
18
+ end
19
+
20
+ add_index :solid_agents_agents, [:key, :environment], unique: true
21
+
22
+ create_table :solid_agents_runs, force: :cascade do |t|
23
+ t.references :agent, null: false, foreign_key: {to_table: :solid_agents_agents}
24
+ t.string :external_key
25
+ t.string :source_type, null: false
26
+ t.bigint :source_id
27
+ t.string :error_fingerprint
28
+ t.string :status, null: false, default: "queued"
29
+ t.string :runtime, null: false
30
+ t.string :environment, null: false
31
+ t.string :repo_path
32
+ t.string :base_branch
33
+ t.string :work_branch
34
+ t.string :commit_sha
35
+ t.string :pr_url
36
+ t.integer :pr_number
37
+ t.string :test_command
38
+ t.integer :attempt_count, null: false, default: 0
39
+ t.integer :max_iterations
40
+ t.datetime :started_at
41
+ t.datetime :finished_at
42
+ t.json :prompt_payload, default: {}
43
+ t.json :result_payload, default: {}
44
+ t.json :error_payload, default: {}
45
+ t.timestamps
46
+ end
47
+
48
+ add_index :solid_agents_runs, :external_key, unique: true
49
+ add_index :solid_agents_runs, :status
50
+ add_index :solid_agents_runs, :error_fingerprint
51
+ add_index :solid_agents_runs, [:source_type, :source_id]
52
+
53
+ create_table :solid_agents_run_events, force: :cascade do |t|
54
+ t.references :run, null: false, foreign_key: {to_table: :solid_agents_runs}
55
+ t.string :event_type, null: false
56
+ t.datetime :event_time, null: false
57
+ t.text :message, null: false
58
+ t.json :payload, default: {}
59
+ t.integer :sequence, null: false
60
+ t.timestamps
61
+ end
62
+
63
+ add_index :solid_agents_run_events, [:run_id, :sequence], unique: true
64
+ add_index :solid_agents_run_events, :event_type
65
+
66
+ create_table :solid_agents_artifacts, force: :cascade do |t|
67
+ t.references :run, null: false, foreign_key: {to_table: :solid_agents_runs}
68
+ t.string :kind, null: false
69
+ t.string :label
70
+ t.string :storage_type, null: false
71
+ t.string :storage_ref
72
+ t.text :content_text
73
+ t.json :content_json, default: {}
74
+ t.string :sha256
75
+ t.bigint :byte_size
76
+ t.timestamps
77
+ end
78
+
79
+ add_index :solid_agents_artifacts, :kind
80
+
81
+ create_table :solid_agents_configs, force: :cascade do |t|
82
+ t.string :key, null: false
83
+ t.string :environment
84
+ t.json :value_json, default: {}
85
+ t.timestamps
86
+ end
87
+
88
+ add_index :solid_agents_configs, [:key, :environment], unique: true
89
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ class Engine < ::Rails::Engine
5
+ config.root = File.expand_path("../..", __dir__)
6
+ isolate_namespace SolidAgents
7
+
8
+ config.solid_agents = ActiveSupport::OrderedOptions.new
9
+
10
+ initializer "solid_agents.configure" do
11
+ config.solid_agents.each do |name, value|
12
+ SolidAgents.public_send(:"#{name}=", value)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module SolidAgents
6
+ module Runtime
7
+ class Adapter
8
+ Result = Struct.new(:ok, :output, :error, :metadata, keyword_init: true)
9
+
10
+ def execute(_run:, _prompt:)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ private
15
+
16
+ def run_command(*argv)
17
+ stdout, stderr, status = Open3.capture3(*argv)
18
+ Result.new(ok: status.success?, output: stdout, error: stderr, metadata: {exit_status: status.exitstatus, command: argv})
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ module Runtime
5
+ class OpenclawAdapter < Adapter
6
+ def execute(run:, prompt:)
7
+ command = [SolidAgents.openclaw_command.to_s, "agent", "--message", prompt, "--thinking", "high"]
8
+ run_command(*command)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ module Runtime
5
+ class TinyclawAdapter < Adapter
6
+ def execute(run:, prompt:)
7
+ command = [SolidAgents.tinyclaw_command.to_s, "send", "@#{run.agent.key} #{prompt}"]
8
+ run_command(*command)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidAgents
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "solid_agents/version"
4
+ require_relative "solid_agents/runtime/adapter"
5
+ require_relative "solid_agents/runtime/tinyclaw_adapter"
6
+ require_relative "solid_agents/runtime/openclaw_adapter"
7
+ require_relative "solid_agents/engine"
8
+
9
+ module SolidAgents
10
+ mattr_accessor :connects_to
11
+ mattr_accessor :base_controller_class, default: "::ActionController::Base"
12
+ mattr_accessor :default_runtime, default: :tinyclaw
13
+ mattr_accessor :tinyclaw_command, default: "tinyclaw"
14
+ mattr_accessor :openclaw_command, default: "openclaw"
15
+ mattr_accessor :default_test_command, default: "bin/rails test"
16
+ mattr_accessor :max_iterations, default: 8
17
+
18
+ class << self
19
+ def runtime_adapter(runtime)
20
+ case runtime.to_sym
21
+ when :tinyclaw then SolidAgents::Runtime::TinyclawAdapter.new
22
+ when :openclaw then SolidAgents::Runtime::OpenclawAdapter.new
23
+ else
24
+ raise ArgumentError, "Unsupported runtime: #{runtime.inspect}"
25
+ end
26
+ end
27
+
28
+ def dispatch_error(source:, agent_key: "fixer")
29
+ SolidAgents::Runs::Dispatch.call(source: source, agent_key: agent_key)
30
+ end
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,179 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: solid_agents
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kaka Ruto
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: actionpack
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '6.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '6.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: actionview
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '6.1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '6.1'
40
+ - !ruby/object:Gem::Dependency
41
+ name: activejob
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '6.1'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '6.1'
54
+ - !ruby/object:Gem::Dependency
55
+ name: activerecord
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '6.1'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '6.1'
68
+ - !ruby/object:Gem::Dependency
69
+ name: activesupport
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '6.1'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '6.1'
82
+ - !ruby/object:Gem::Dependency
83
+ name: railties
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '6.1'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '6.1'
96
+ - !ruby/object:Gem::Dependency
97
+ name: sqlite3
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '2.0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '2.0'
110
+ description: Solid Agent stores agent runs in its own database and dispatches work
111
+ to tinyclaw/openclaw runtimes with a built-in Rails UI.
112
+ email:
113
+ - kr@kakaruto.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - CHANGELOG.md
119
+ - LICENSE.txt
120
+ - README.md
121
+ - Rakefile
122
+ - app/controllers/solid_agents/agents_controller.rb
123
+ - app/controllers/solid_agents/application_controller.rb
124
+ - app/controllers/solid_agents/runs_controller.rb
125
+ - app/helpers/solid_agents/runs_helper.rb
126
+ - app/jobs/solid_agents/execute_run_job.rb
127
+ - app/models/solid_agents/agent.rb
128
+ - app/models/solid_agents/artifact.rb
129
+ - app/models/solid_agents/config.rb
130
+ - app/models/solid_agents/record.rb
131
+ - app/models/solid_agents/run.rb
132
+ - app/models/solid_agents/run_event.rb
133
+ - app/services/solid_agents/runs/context_builder.rb
134
+ - app/services/solid_agents/runs/dispatch.rb
135
+ - app/services/solid_agents/runs/executor.rb
136
+ - app/services/solid_agents/runs/prompt_builder.rb
137
+ - app/views/layouts/solid_agents/_style.html.erb
138
+ - app/views/layouts/solid_agents/application.html.erb
139
+ - app/views/solid_agents/agents/edit.html.erb
140
+ - app/views/solid_agents/agents/index.html.erb
141
+ - app/views/solid_agents/agents/show.html.erb
142
+ - app/views/solid_agents/runs/index.html.erb
143
+ - app/views/solid_agents/runs/show.html.erb
144
+ - config/locales/en.yml
145
+ - config/routes.rb
146
+ - lib/generators/solid_agents/install/USAGE
147
+ - lib/generators/solid_agents/install/install_generator.rb
148
+ - lib/generators/solid_agents/install/templates/config/initializers/solid_agents.rb
149
+ - lib/generators/solid_agents/install/templates/db/agent_schema.rb
150
+ - lib/solid_agents.rb
151
+ - lib/solid_agents/engine.rb
152
+ - lib/solid_agents/runtime/adapter.rb
153
+ - lib/solid_agents/runtime/openclaw_adapter.rb
154
+ - lib/solid_agents/runtime/tinyclaw_adapter.rb
155
+ - lib/solid_agents/version.rb
156
+ homepage: https://github.com/kaka-ruto/solid_agents
157
+ licenses:
158
+ - MIT
159
+ metadata:
160
+ homepage_uri: https://github.com/kaka-ruto/solid_agents
161
+ source_code_uri: https://github.com/kaka-ruto/solid_agents
162
+ rdoc_options: []
163
+ require_paths:
164
+ - lib
165
+ required_ruby_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: 2.6.0
170
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ requirements: []
176
+ rubygems_version: 4.0.3
177
+ specification_version: 4
178
+ summary: Database-backed Rails AI agent runtime bridge
179
+ test_files: []