solid_agents 0.1.0 → 0.2.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 +18 -1
- data/README.md +32 -40
- data/app/controllers/solid_agents/runs_controller.rb +4 -1
- data/app/jobs/solid_agents/execute_run_job.rb +2 -0
- data/app/models/solid_agents/agent.rb +1 -1
- data/app/models/solid_agents/handoff.rb +11 -0
- data/app/models/solid_agents/run.rb +38 -3
- data/app/models/solid_agents/work_item.rb +11 -0
- data/app/services/solid_agents/runs/dispatch.rb +5 -2
- data/app/services/solid_agents/runs/executor.rb +41 -9
- data/app/services/solid_agents/runs/prompt_builder.rb +9 -11
- data/app/views/solid_agents/agents/edit.html.erb +4 -7
- data/app/views/solid_agents/runs/index.html.erb +4 -2
- data/app/views/solid_agents/runs/show.html.erb +7 -0
- data/lib/generators/solid_agents/install/install_generator.rb +1 -1
- data/lib/generators/solid_agents/install/templates/config/initializers/solid_agents.rb +1 -2
- data/lib/generators/solid_agents/install/templates/db/agent_schema.rb +30 -3
- data/lib/solid_agents/runtime/pi_adapter.rb +15 -0
- data/lib/solid_agents/version.rb +1 -1
- data/lib/solid_agents/workflow.rb +48 -0
- data/lib/solid_agents.rb +6 -8
- metadata +7 -5
- data/lib/solid_agents/runtime/openclaw_adapter.rb +0 -12
- data/lib/solid_agents/runtime/tinyclaw_adapter.rb +0 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5972db63aa2e5fb01908f0a70c788856b9812fce9961b21cf75c1d79b041406
|
|
4
|
+
data.tar.gz: 922aafdc00524dab9c490c56031af71c99ef539afbbf87acba80c53a9c1b73c3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1290f03a93a621b1bc78f94b8f7031134809b368c1e37c9542f5d22b58abd8d579b7774e93c6d0d6720838b3b5ea618a807c5fd6e2562737701ac7b0e5598c28
|
|
7
|
+
data.tar.gz: e6f7ceca7356d904fc0adf8187eea3ac4784e64cac04db5c3e944ceda661112c9e6729d9d1524f34ec2fb610925eafe10452fb79d2d611a088ca0e9835e145c5
|
data/CHANGELOG.md
CHANGED
|
@@ -2,13 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `solid_agents` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [v0.2.0] - 2026-03-23
|
|
6
|
+
|
|
7
|
+
Major architecture reset delivered as a minor release for rapid iteration.
|
|
8
|
+
|
|
9
|
+
- Replaced runtime adapters with a single pi-only execution path.
|
|
10
|
+
- Rebuilt the run model into an event-driven staged workflow.
|
|
11
|
+
- Added work-item board tracking and explicit inter-agent handoff records.
|
|
12
|
+
- Introduced stage ownership with alphabetical agents: alex, betty, chad, david, emma.
|
|
13
|
+
- Updated installer schema and initializer templates for the new workflow primitives.
|
|
14
|
+
- Reworked run UI to expose stage, owner, events, and artifacts in clean columns.
|
|
15
|
+
- Expanded Minitest coverage and moved to YAML fixtures for deterministic test data.
|
|
16
|
+
|
|
17
|
+
Status notes:
|
|
18
|
+
|
|
19
|
+
- This release is still WIP and not production-ready.
|
|
20
|
+
- Breaking changes are expected before `1.0`.
|
|
21
|
+
|
|
5
22
|
## [v0.1.0] - 2026-03-22
|
|
6
23
|
|
|
7
24
|
Initial public release (WIP).
|
|
8
25
|
|
|
9
26
|
- Introduced `solid_agents` Rails engine for database-backed agent run orchestration.
|
|
10
27
|
- Added run lifecycle models and persistence for runs, events, artifacts, agents, and config.
|
|
11
|
-
- Added dispatch and execution flow with runtime adapters for
|
|
28
|
+
- Added dispatch and execution flow with runtime adapters for OpenAI pi runtime and OpenAI pi runtime.
|
|
12
29
|
- Added built-in UI/controllers for managing agents and inspecting runs.
|
|
13
30
|
- Added installer generator, schema template, and base configuration defaults.
|
|
14
31
|
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SolidAgents
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Event-driven error fixing workflow for Rails apps, powered by the pi runtime.**
|
|
4
4
|
|
|
5
5
|
> [!WARNING]
|
|
6
6
|
> `solid_agents` is an early release and still a work in progress.
|
|
@@ -8,26 +8,30 @@
|
|
|
8
8
|
> Expect breaking changes before `1.0`.
|
|
9
9
|
> Not production-ready yet.
|
|
10
10
|
|
|
11
|
-
`solid_agents` is a Rails engine
|
|
11
|
+
`solid_agents` is a Rails engine focused on a single pipeline:
|
|
12
|
+
|
|
13
|
+
`error received -> staged agent workflow -> code fix attempt -> PR/CI stage tracking`
|
|
12
14
|
|
|
13
15
|
## Scope
|
|
14
16
|
|
|
15
|
-
`solid_agents` is
|
|
17
|
+
`solid_agents` is for automation orchestration and execution only:
|
|
16
18
|
|
|
17
|
-
- Consume
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
19
|
+
- Consume error-like events from source adapters (starting with `solid_errors` style payloads)
|
|
20
|
+
- Track runs in an event-driven stage machine
|
|
21
|
+
- Enforce non-overlapping stage ownership (`alex`, `betty`, `chad`, `david`, `emma`)
|
|
22
|
+
- Persist handoffs, notes, and artifacts for each stage
|
|
23
|
+
- Execute stage tasks through the pi runtime adapter
|
|
21
24
|
|
|
22
|
-
It does **not** own observability storage or incident detection
|
|
25
|
+
It does **not** own observability storage or incident detection as a source of truth.
|
|
23
26
|
|
|
24
27
|
## Features
|
|
25
28
|
|
|
26
|
-
- DB-backed run lifecycle
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
29
|
+
- DB-backed run lifecycle and stage workflow
|
|
30
|
+
- Append-only run events with actor attribution
|
|
31
|
+
- Work-item board columns driven by stage transitions
|
|
32
|
+
- Handoff records between stage owners
|
|
33
|
+
- Built-in UI for runs, events, and artifacts
|
|
34
|
+
- Installer generator and schema template
|
|
31
35
|
|
|
32
36
|
## Installation
|
|
33
37
|
|
|
@@ -43,25 +47,12 @@ Run installer:
|
|
|
43
47
|
rails generate solid_agents:install
|
|
44
48
|
```
|
|
45
49
|
|
|
46
|
-
|
|
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:
|
|
50
|
+
Configure engine DB connection if desired:
|
|
60
51
|
|
|
61
52
|
```ruby
|
|
62
53
|
# config/environments/production.rb
|
|
63
54
|
config.solid_agents.connects_to = { database: { writing: :solid_agents } }
|
|
64
|
-
config.solid_agents.default_runtime = :
|
|
55
|
+
config.solid_agents.default_runtime = :pi
|
|
65
56
|
```
|
|
66
57
|
|
|
67
58
|
Mount the UI:
|
|
@@ -75,30 +66,30 @@ end
|
|
|
75
66
|
|
|
76
67
|
## Usage
|
|
77
68
|
|
|
78
|
-
Create
|
|
69
|
+
Create pipeline agents:
|
|
79
70
|
|
|
80
71
|
```ruby
|
|
81
|
-
|
|
82
|
-
key:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
%w[alex betty chad david emma].each do |key|
|
|
73
|
+
SolidAgents::Agent.find_or_create_by!(key: key, environment: Rails.env) do |agent|
|
|
74
|
+
agent.name = key.capitalize
|
|
75
|
+
agent.role = key
|
|
76
|
+
agent.runtime = "pi"
|
|
77
|
+
agent.working_directory = Rails.root.to_s
|
|
78
|
+
agent.enabled = true
|
|
79
|
+
end
|
|
80
|
+
end
|
|
89
81
|
```
|
|
90
82
|
|
|
91
83
|
Dispatch from a job:
|
|
92
84
|
|
|
93
85
|
```ruby
|
|
94
|
-
SolidAgents.dispatch_error(source: solid_error_record, agent_key: "
|
|
86
|
+
SolidAgents.dispatch_error(source: solid_error_record, agent_key: "alex")
|
|
95
87
|
```
|
|
96
88
|
|
|
97
89
|
## Configuration
|
|
98
90
|
|
|
99
91
|
```ruby
|
|
100
|
-
config.solid_agents.
|
|
101
|
-
config.solid_agents.openclaw_command = "openclaw"
|
|
92
|
+
config.solid_agents.pi_command = "pi"
|
|
102
93
|
config.solid_agents.default_test_command = "bin/rails test"
|
|
103
94
|
config.solid_agents.max_iterations = 8
|
|
104
95
|
```
|
|
@@ -108,4 +99,5 @@ config.solid_agents.max_iterations = 8
|
|
|
108
99
|
```bash
|
|
109
100
|
bundle install
|
|
110
101
|
bundle exec rake test
|
|
102
|
+
gem build solid_agents.gemspec
|
|
111
103
|
```
|
|
@@ -17,13 +17,16 @@ module SolidAgents
|
|
|
17
17
|
def retry
|
|
18
18
|
duplicated = @run.dup
|
|
19
19
|
duplicated.status = :queued
|
|
20
|
+
duplicated.stage = "received"
|
|
21
|
+
duplicated.stage_owner = "alex"
|
|
20
22
|
duplicated.started_at = nil
|
|
21
23
|
duplicated.finished_at = nil
|
|
22
24
|
duplicated.error_payload = nil
|
|
23
25
|
duplicated.result_payload = nil
|
|
24
26
|
duplicated.external_key = "retry-#{@run.id}-#{Time.current.to_i}"
|
|
25
27
|
duplicated.save!
|
|
26
|
-
duplicated.
|
|
28
|
+
duplicated.create_work_item!(column_key: duplicated.stage, title: @run.work_item&.title || "Retry ##{@run.id}", summary: "Retry workflow run", metadata_json: {"original_run_id" => @run.id})
|
|
29
|
+
duplicated.append_event!("retried", message: "Run created from retry", payload: {"original_run_id" => @run.id}, actor: "alex")
|
|
27
30
|
SolidAgents::ExecuteRunJob.perform_later(duplicated.id)
|
|
28
31
|
|
|
29
32
|
redirect_to run_path(duplicated), notice: "Run retried."
|
|
@@ -7,6 +7,7 @@ module SolidAgents
|
|
|
7
7
|
STATUSES = {
|
|
8
8
|
queued: "queued",
|
|
9
9
|
running: "running",
|
|
10
|
+
blocked: "blocked",
|
|
10
11
|
pr_opened: "pr_opened",
|
|
11
12
|
succeeded: "succeeded",
|
|
12
13
|
failed: "failed",
|
|
@@ -16,23 +17,57 @@ module SolidAgents
|
|
|
16
17
|
belongs_to :agent, class_name: "SolidAgents::Agent"
|
|
17
18
|
has_many :events, class_name: "SolidAgents::RunEvent", foreign_key: :run_id, dependent: :delete_all
|
|
18
19
|
has_many :artifacts, class_name: "SolidAgents::Artifact", foreign_key: :run_id, dependent: :delete_all
|
|
20
|
+
has_many :handoffs, class_name: "SolidAgents::Handoff", foreign_key: :run_id, dependent: :delete_all
|
|
21
|
+
has_one :work_item, class_name: "SolidAgents::WorkItem", foreign_key: :run_id, dependent: :delete
|
|
19
22
|
|
|
20
23
|
enum :status, STATUSES
|
|
21
24
|
|
|
22
|
-
validates :runtime, :environment, :status, :source_type, presence: true
|
|
25
|
+
validates :runtime, :environment, :status, :source_type, :stage, presence: true
|
|
23
26
|
validates :external_key, uniqueness: true, allow_nil: true
|
|
24
27
|
|
|
25
28
|
before_validation :set_defaults, on: :create
|
|
26
29
|
|
|
27
|
-
def append_event!(event_type, message:, payload: nil)
|
|
30
|
+
def append_event!(event_type, message:, payload: nil, actor: nil)
|
|
28
31
|
next_sequence = (events.maximum(:sequence) || 0) + 1
|
|
29
|
-
events.create!(
|
|
32
|
+
events.create!(
|
|
33
|
+
event_type: event_type,
|
|
34
|
+
message: message,
|
|
35
|
+
payload: payload || {},
|
|
36
|
+
actor: actor || stage_owner,
|
|
37
|
+
event_time: Time.current,
|
|
38
|
+
sequence: next_sequence
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def transition_to!(next_stage, actor:, message:, payload: {})
|
|
43
|
+
update!(stage: next_stage, stage_owner: SolidAgents::Workflow.stage_agent(next_stage))
|
|
44
|
+
work_item&.update!(column_key: next_stage)
|
|
45
|
+
append_event!("stage_transitioned", message: message, payload: payload.merge("next_stage" => next_stage), actor: actor)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def start_stage!(actor:)
|
|
49
|
+
update!(status: :running, started_at: (started_at || Time.current))
|
|
50
|
+
append_event!("stage_started", message: "Stage #{stage} started", payload: {"stage" => stage}, actor: actor)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def complete!(result_payload:, actor:)
|
|
54
|
+
update!(status: :succeeded, stage: "done", stage_owner: SolidAgents::Workflow.stage_agent("done"), finished_at: Time.current, result_payload: result_payload)
|
|
55
|
+
work_item&.update!(column_key: "done")
|
|
56
|
+
append_event!("run_completed", message: "Run completed successfully", payload: result_payload, actor: actor)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def fail!(error_payload:, actor:)
|
|
60
|
+
update!(status: :failed, finished_at: Time.current, error_payload: error_payload)
|
|
61
|
+
work_item&.update!(column_key: "failed")
|
|
62
|
+
append_event!("run_failed", message: "Run failed", payload: error_payload, actor: actor)
|
|
30
63
|
end
|
|
31
64
|
|
|
32
65
|
private
|
|
33
66
|
|
|
34
67
|
def set_defaults
|
|
35
68
|
self.status ||= :queued
|
|
69
|
+
self.stage ||= "received"
|
|
70
|
+
self.stage_owner ||= SolidAgents::Workflow.stage_agent(stage)
|
|
36
71
|
self.runtime ||= agent&.runtime || SolidAgents.default_runtime.to_s
|
|
37
72
|
self.environment ||= Rails.env
|
|
38
73
|
self.test_command ||= SolidAgents.default_test_command
|
|
@@ -16,12 +16,15 @@ module SolidAgents
|
|
|
16
16
|
external_key: "#{source.class.name}-#{source.respond_to?(:id) ? source.id : source.object_id}-#{Time.current.to_i}",
|
|
17
17
|
repo_path: agent.working_directory,
|
|
18
18
|
base_branch: "main",
|
|
19
|
-
max_iterations: agent.max_iterations
|
|
19
|
+
max_iterations: agent.max_iterations,
|
|
20
|
+
stage: "received",
|
|
21
|
+
stage_owner: "alex"
|
|
20
22
|
)
|
|
21
23
|
|
|
22
24
|
context = ContextBuilder.call(source: source)
|
|
23
25
|
run.update!(prompt_payload: context)
|
|
24
|
-
run.append_event!("
|
|
26
|
+
run.append_event!("run_received", message: "Run received from source", payload: {"agent_key" => agent.key, "source_type" => run.source_type}, actor: "alex")
|
|
27
|
+
run.create_work_item!(column_key: run.stage, title: "Fix #{run.source_type}##{run.source_id || "n/a"}", summary: "Automated error-fixing pipeline", metadata_json: {"agent_key" => agent.key})
|
|
25
28
|
|
|
26
29
|
SolidAgents::ExecuteRunJob.perform_later(run.id)
|
|
27
30
|
run
|
|
@@ -4,24 +4,56 @@ module SolidAgents
|
|
|
4
4
|
module Runs
|
|
5
5
|
class Executor
|
|
6
6
|
def self.call(run)
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
stage = run.stage
|
|
8
|
+
owner = SolidAgents::Workflow.stage_agent(stage)
|
|
9
|
+
|
|
10
|
+
run.start_stage!(actor: owner)
|
|
9
11
|
|
|
10
12
|
prompt = PromptBuilder.call(run: run, context: run.prompt_payload || {})
|
|
11
13
|
adapter = SolidAgents.runtime_adapter(run.runtime)
|
|
12
14
|
result = adapter.execute(run: run, prompt: prompt)
|
|
13
15
|
|
|
14
|
-
run.artifacts.create!(
|
|
16
|
+
run.artifacts.create!(
|
|
17
|
+
kind: "log",
|
|
18
|
+
label: "#{stage}_runtime_output",
|
|
19
|
+
storage_type: "inline",
|
|
20
|
+
content_text: result.output.to_s,
|
|
21
|
+
content_json: {"metadata" => result.metadata.to_h}
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
unless result.ok
|
|
25
|
+
run.fail!(error_payload: {"stage" => stage, "stderr" => result.error.to_s, "metadata" => result.metadata.to_h}, actor: owner)
|
|
26
|
+
return run
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
next_stage = SolidAgents::Workflow.next_stage(stage)
|
|
30
|
+
next_owner = SolidAgents::Workflow.stage_agent(next_stage)
|
|
15
31
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
32
|
+
run.handoffs.create!(
|
|
33
|
+
stage: stage,
|
|
34
|
+
from_agent: owner,
|
|
35
|
+
to_agent: next_owner,
|
|
36
|
+
note: "#{owner} completed #{stage} and handed off to #{next_owner}",
|
|
37
|
+
payload: {"output_excerpt" => result.output.to_s.slice(0, 500)}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
run.append_event!(
|
|
41
|
+
"stage_completed",
|
|
42
|
+
message: "#{owner} completed #{stage}",
|
|
43
|
+
payload: {"stage" => stage, "next_stage" => next_stage, "next_owner" => next_owner},
|
|
44
|
+
actor: owner
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if next_stage == "done"
|
|
48
|
+
run.complete!(result_payload: {"final_stage" => stage, "metadata" => result.metadata.to_h, "output" => result.output.to_s}, actor: next_owner)
|
|
20
49
|
else
|
|
21
|
-
run.
|
|
22
|
-
|
|
50
|
+
run.transition_to!(next_stage, actor: owner, message: "Transitioned from #{stage} to #{next_stage}", payload: {"next_owner" => next_owner})
|
|
51
|
+
SolidAgents::ExecuteRunJob.perform_later(run.id)
|
|
23
52
|
end
|
|
24
53
|
|
|
54
|
+
run
|
|
55
|
+
rescue StandardError => e
|
|
56
|
+
run.fail!(error_payload: {"stage" => run.stage, "exception" => e.class.name, "message" => e.message}, actor: SolidAgents::Workflow.stage_agent(run.stage))
|
|
25
57
|
run
|
|
26
58
|
end
|
|
27
59
|
end
|
|
@@ -7,25 +7,23 @@ module SolidAgents
|
|
|
7
7
|
class PromptBuilder
|
|
8
8
|
def self.call(run:, context:)
|
|
9
9
|
<<~PROMPT
|
|
10
|
-
You are #{run.
|
|
11
|
-
Goal:
|
|
10
|
+
You are #{run.stage_owner} executing stage #{run.stage} for run #{run.id}.
|
|
11
|
+
Goal: move the workflow to the next stage with clear evidence.
|
|
12
12
|
|
|
13
13
|
Constraints:
|
|
14
|
-
-
|
|
14
|
+
- Repository path: #{run.repo_path}
|
|
15
15
|
- Base branch: #{run.base_branch}
|
|
16
16
|
- Test command: #{run.test_command}
|
|
17
|
+
- Runtime: #{run.runtime}
|
|
17
18
|
- Max iterations: #{run.max_iterations || SolidAgents.max_iterations}
|
|
18
|
-
|
|
19
|
+
|
|
20
|
+
Stage handoff contract:
|
|
21
|
+
- Produce concise notes for the next stage owner.
|
|
22
|
+
- Include reproducible evidence and command output.
|
|
23
|
+
- Respect repository rules from AGENTS.md.
|
|
19
24
|
|
|
20
25
|
Context JSON:
|
|
21
26
|
#{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
27
|
PROMPT
|
|
30
28
|
end
|
|
31
29
|
end
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
<div class="card">
|
|
2
|
-
<h2>Edit Agent
|
|
3
|
-
|
|
2
|
+
<h2>Edit Agent</h2>
|
|
4
3
|
<%= form_with model: @agent, url: agent_path(@agent), method: :patch do |f| %>
|
|
5
4
|
<p><%= f.label :name %><br><%= f.text_field :name %></p>
|
|
6
5
|
<p><%= f.label :role %><br><%= f.select :role, SolidAgents::Agent::ROLES %></p>
|
|
7
|
-
<p><%= f.label :runtime %><br><%= f.select :runtime, %w[
|
|
6
|
+
<p><%= f.label :runtime %><br><%= f.select :runtime, %w[pi] %></p>
|
|
8
7
|
<p><%= f.label :environment %><br><%= f.text_field :environment %></p>
|
|
9
|
-
<p><%= f.label :model %><br><%= f.text_field :model %></p>
|
|
10
8
|
<p><%= f.label :working_directory %><br><%= f.text_field :working_directory %></p>
|
|
11
9
|
<p><%= f.label :timeout_seconds %><br><%= f.number_field :timeout_seconds %></p>
|
|
12
10
|
<p><%= f.label :max_iterations %><br><%= f.number_field :max_iterations %></p>
|
|
13
|
-
<p><%= f.label :
|
|
14
|
-
|
|
15
|
-
<p><%= f.submit "Save", class: "btn primary" %></p>
|
|
11
|
+
<p><%= f.label :enabled %><br><%= f.check_box :enabled %></p>
|
|
12
|
+
<%= f.submit "Save", class: "btn primary" %>
|
|
16
13
|
<% end %>
|
|
17
14
|
</div>
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
<th>ID</th>
|
|
7
7
|
<th>Agent</th>
|
|
8
8
|
<th>Status</th>
|
|
9
|
+
<th>Stage</th>
|
|
10
|
+
<th>Owner</th>
|
|
9
11
|
<th>Runtime</th>
|
|
10
12
|
<th>Source</th>
|
|
11
|
-
<th>Started</th>
|
|
12
13
|
</tr>
|
|
13
14
|
</thead>
|
|
14
15
|
<tbody>
|
|
@@ -17,9 +18,10 @@
|
|
|
17
18
|
<td><%= link_to "##{run.id}", run_path(run) %></td>
|
|
18
19
|
<td><%= run.agent&.key %></td>
|
|
19
20
|
<td><span class="badge <%= status_badge_class(run.status) %>"><%= run.status %></span></td>
|
|
21
|
+
<td><%= run.stage %></td>
|
|
22
|
+
<td><%= run.stage_owner %></td>
|
|
20
23
|
<td><%= run.runtime %></td>
|
|
21
24
|
<td><%= [run.source_type, run.source_id].compact.join("#") %></td>
|
|
22
|
-
<td><%= run.started_at || run.created_at %></td>
|
|
23
25
|
</tr>
|
|
24
26
|
<% end %>
|
|
25
27
|
</tbody>
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
Runtime: <strong><%= @run.runtime %></strong> |
|
|
6
6
|
Status: <span class="badge <%= status_badge_class(@run.status) %>"><%= @run.status %></span>
|
|
7
7
|
</p>
|
|
8
|
+
<p>
|
|
9
|
+
Stage: <strong><%= @run.stage %></strong> |
|
|
10
|
+
Owner: <strong><%= @run.stage_owner %></strong> |
|
|
11
|
+
Board Column: <strong><%= @run.work_item&.column_key || @run.stage %></strong>
|
|
12
|
+
</p>
|
|
8
13
|
<p>
|
|
9
14
|
Source: <%= [@run.source_type, @run.source_id].compact.join("#") %>
|
|
10
15
|
</p>
|
|
@@ -19,6 +24,7 @@
|
|
|
19
24
|
<tr>
|
|
20
25
|
<th>#</th>
|
|
21
26
|
<th>Type</th>
|
|
27
|
+
<th>Actor</th>
|
|
22
28
|
<th>Message</th>
|
|
23
29
|
</tr>
|
|
24
30
|
</thead>
|
|
@@ -27,6 +33,7 @@
|
|
|
27
33
|
<tr>
|
|
28
34
|
<td><%= event.sequence %></td>
|
|
29
35
|
<td><%= event.event_type %></td>
|
|
36
|
+
<td><%= event.actor %></td>
|
|
30
37
|
<td><%= event.message %></td>
|
|
31
38
|
</tr>
|
|
32
39
|
<% end %>
|
|
@@ -18,7 +18,7 @@ module SolidAgents
|
|
|
18
18
|
"",
|
|
19
19
|
'\\1# Configure Solid Agent',
|
|
20
20
|
'\\1config.solid_agents.connects_to = { database: { writing: :solid_agents } }',
|
|
21
|
-
'\\1config.solid_agents.default_runtime = :
|
|
21
|
+
'\\1config.solid_agents.default_runtime = :pi'
|
|
22
22
|
].join("\n")
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -4,8 +4,7 @@ Rails.application.configure do
|
|
|
4
4
|
# Optional: override in specific environments.
|
|
5
5
|
# config.solid_agents.connects_to = { database: { writing: :solid_agents } }
|
|
6
6
|
|
|
7
|
-
config.solid_agents.
|
|
8
|
-
config.solid_agents.openclaw_command = ENV.fetch("SOLID_AGENTS_OPENCLAW_COMMAND", "openclaw")
|
|
7
|
+
config.solid_agents.pi_command = ENV.fetch("SOLID_AGENTS_PI_COMMAND", "pi")
|
|
9
8
|
config.solid_agents.default_test_command = ENV.fetch("SOLID_AGENTS_TEST_COMMAND", "bin/rails test")
|
|
10
9
|
config.solid_agents.max_iterations = ENV.fetch("SOLID_AGENTS_MAX_ITERATIONS", 8).to_i
|
|
11
10
|
end
|
|
@@ -4,8 +4,8 @@ ActiveRecord::Schema[6.1].define do
|
|
|
4
4
|
create_table :solid_agents_agents, force: :cascade do |t|
|
|
5
5
|
t.string :key, null: false
|
|
6
6
|
t.string :name, null: false
|
|
7
|
-
t.string :role, null: false, default: "
|
|
8
|
-
t.string :runtime, null: false, default: "
|
|
7
|
+
t.string :role, null: false, default: "alex"
|
|
8
|
+
t.string :runtime, null: false, default: "pi"
|
|
9
9
|
t.boolean :enabled, null: false, default: true
|
|
10
10
|
t.string :environment
|
|
11
11
|
t.string :model
|
|
@@ -26,7 +26,9 @@ ActiveRecord::Schema[6.1].define do
|
|
|
26
26
|
t.bigint :source_id
|
|
27
27
|
t.string :error_fingerprint
|
|
28
28
|
t.string :status, null: false, default: "queued"
|
|
29
|
-
t.string :
|
|
29
|
+
t.string :stage, null: false, default: "received"
|
|
30
|
+
t.string :stage_owner, null: false, default: "alex"
|
|
31
|
+
t.string :runtime, null: false, default: "pi"
|
|
30
32
|
t.string :environment, null: false
|
|
31
33
|
t.string :repo_path
|
|
32
34
|
t.string :base_branch
|
|
@@ -47,14 +49,39 @@ ActiveRecord::Schema[6.1].define do
|
|
|
47
49
|
|
|
48
50
|
add_index :solid_agents_runs, :external_key, unique: true
|
|
49
51
|
add_index :solid_agents_runs, :status
|
|
52
|
+
add_index :solid_agents_runs, :stage
|
|
50
53
|
add_index :solid_agents_runs, :error_fingerprint
|
|
51
54
|
add_index :solid_agents_runs, [:source_type, :source_id]
|
|
52
55
|
|
|
56
|
+
create_table :solid_agents_work_items, force: :cascade do |t|
|
|
57
|
+
t.references :run, null: false, foreign_key: {to_table: :solid_agents_runs}
|
|
58
|
+
t.string :column_key, null: false, default: "received"
|
|
59
|
+
t.string :title, null: false
|
|
60
|
+
t.text :summary
|
|
61
|
+
t.json :metadata_json, default: {}
|
|
62
|
+
t.timestamps
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
add_index :solid_agents_work_items, :column_key
|
|
66
|
+
|
|
67
|
+
create_table :solid_agents_handoffs, force: :cascade do |t|
|
|
68
|
+
t.references :run, null: false, foreign_key: {to_table: :solid_agents_runs}
|
|
69
|
+
t.string :stage, null: false
|
|
70
|
+
t.string :from_agent, null: false
|
|
71
|
+
t.string :to_agent, null: false
|
|
72
|
+
t.text :note
|
|
73
|
+
t.json :payload, default: {}
|
|
74
|
+
t.timestamps
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
add_index :solid_agents_handoffs, [:run_id, :stage]
|
|
78
|
+
|
|
53
79
|
create_table :solid_agents_run_events, force: :cascade do |t|
|
|
54
80
|
t.references :run, null: false, foreign_key: {to_table: :solid_agents_runs}
|
|
55
81
|
t.string :event_type, null: false
|
|
56
82
|
t.datetime :event_time, null: false
|
|
57
83
|
t.text :message, null: false
|
|
84
|
+
t.string :actor
|
|
58
85
|
t.json :payload, default: {}
|
|
59
86
|
t.integer :sequence, null: false
|
|
60
87
|
t.timestamps
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidAgents
|
|
4
|
+
module Runtime
|
|
5
|
+
class PiAdapter < Adapter
|
|
6
|
+
def execute(run:, prompt:)
|
|
7
|
+
command = [SolidAgents.pi_command.to_s, "agent", "run", "--json", "--message", prompt]
|
|
8
|
+
run_command(*command).tap do |result|
|
|
9
|
+
result.metadata[:stage] = run.stage
|
|
10
|
+
result.metadata[:owner] = run.stage_owner
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/solid_agents/version.rb
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidAgents
|
|
4
|
+
module Workflow
|
|
5
|
+
STAGES = [
|
|
6
|
+
"received",
|
|
7
|
+
"triaged",
|
|
8
|
+
"repro_test",
|
|
9
|
+
"repro_manual",
|
|
10
|
+
"fixing",
|
|
11
|
+
"verifying",
|
|
12
|
+
"pr_opened",
|
|
13
|
+
"ci_wait",
|
|
14
|
+
"done"
|
|
15
|
+
].freeze
|
|
16
|
+
|
|
17
|
+
STAGE_TO_AGENT = {
|
|
18
|
+
"received" => "alex",
|
|
19
|
+
"triaged" => "alex",
|
|
20
|
+
"repro_test" => "betty",
|
|
21
|
+
"repro_manual" => "betty",
|
|
22
|
+
"fixing" => "chad",
|
|
23
|
+
"verifying" => "david",
|
|
24
|
+
"pr_opened" => "emma",
|
|
25
|
+
"ci_wait" => "emma",
|
|
26
|
+
"done" => "emma"
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
FINAL_STAGES = ["done", "failed"].freeze
|
|
30
|
+
|
|
31
|
+
module_function
|
|
32
|
+
|
|
33
|
+
def next_stage(current_stage)
|
|
34
|
+
index = STAGES.index(current_stage)
|
|
35
|
+
return "done" if index.nil? || index == STAGES.length - 1
|
|
36
|
+
|
|
37
|
+
STAGES[index + 1]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def stage_agent(stage)
|
|
41
|
+
STAGE_TO_AGENT.fetch(stage.to_s, "alex")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def final_stage?(stage)
|
|
45
|
+
FINAL_STAGES.include?(stage.to_s)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/solid_agents.rb
CHANGED
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "solid_agents/version"
|
|
4
|
+
require_relative "solid_agents/workflow"
|
|
4
5
|
require_relative "solid_agents/runtime/adapter"
|
|
5
|
-
require_relative "solid_agents/runtime/
|
|
6
|
-
require_relative "solid_agents/runtime/openclaw_adapter"
|
|
6
|
+
require_relative "solid_agents/runtime/pi_adapter"
|
|
7
7
|
require_relative "solid_agents/engine"
|
|
8
8
|
|
|
9
9
|
module SolidAgents
|
|
10
10
|
mattr_accessor :connects_to
|
|
11
11
|
mattr_accessor :base_controller_class, default: "::ActionController::Base"
|
|
12
|
-
mattr_accessor :default_runtime, default: :
|
|
13
|
-
mattr_accessor :
|
|
14
|
-
mattr_accessor :openclaw_command, default: "openclaw"
|
|
12
|
+
mattr_accessor :default_runtime, default: :pi
|
|
13
|
+
mattr_accessor :pi_command, default: "pi"
|
|
15
14
|
mattr_accessor :default_test_command, default: "bin/rails test"
|
|
16
15
|
mattr_accessor :max_iterations, default: 8
|
|
17
16
|
|
|
18
17
|
class << self
|
|
19
18
|
def runtime_adapter(runtime)
|
|
20
19
|
case runtime.to_sym
|
|
21
|
-
when :
|
|
22
|
-
when :openclaw then SolidAgents::Runtime::OpenclawAdapter.new
|
|
20
|
+
when :pi then SolidAgents::Runtime::PiAdapter.new
|
|
23
21
|
else
|
|
24
22
|
raise ArgumentError, "Unsupported runtime: #{runtime.inspect}"
|
|
25
23
|
end
|
|
26
24
|
end
|
|
27
25
|
|
|
28
|
-
def dispatch_error(source:, agent_key: "
|
|
26
|
+
def dispatch_error(source:, agent_key: "alex")
|
|
29
27
|
SolidAgents::Runs::Dispatch.call(source: source, agent_key: agent_key)
|
|
30
28
|
end
|
|
31
29
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solid_agents
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kaka Ruto
|
|
@@ -107,8 +107,8 @@ dependencies:
|
|
|
107
107
|
- - ">="
|
|
108
108
|
- !ruby/object:Gem::Version
|
|
109
109
|
version: '2.0'
|
|
110
|
-
description: Solid Agent stores agent runs in its own database and dispatches
|
|
111
|
-
to
|
|
110
|
+
description: Solid Agent stores agent runs in its own database and dispatches staged
|
|
111
|
+
workflow tasks to the pi runtime with a built-in Rails UI.
|
|
112
112
|
email:
|
|
113
113
|
- kr@kakaruto.com
|
|
114
114
|
executables: []
|
|
@@ -127,9 +127,11 @@ files:
|
|
|
127
127
|
- app/models/solid_agents/agent.rb
|
|
128
128
|
- app/models/solid_agents/artifact.rb
|
|
129
129
|
- app/models/solid_agents/config.rb
|
|
130
|
+
- app/models/solid_agents/handoff.rb
|
|
130
131
|
- app/models/solid_agents/record.rb
|
|
131
132
|
- app/models/solid_agents/run.rb
|
|
132
133
|
- app/models/solid_agents/run_event.rb
|
|
134
|
+
- app/models/solid_agents/work_item.rb
|
|
133
135
|
- app/services/solid_agents/runs/context_builder.rb
|
|
134
136
|
- app/services/solid_agents/runs/dispatch.rb
|
|
135
137
|
- app/services/solid_agents/runs/executor.rb
|
|
@@ -150,9 +152,9 @@ files:
|
|
|
150
152
|
- lib/solid_agents.rb
|
|
151
153
|
- lib/solid_agents/engine.rb
|
|
152
154
|
- lib/solid_agents/runtime/adapter.rb
|
|
153
|
-
- lib/solid_agents/runtime/
|
|
154
|
-
- lib/solid_agents/runtime/tinyclaw_adapter.rb
|
|
155
|
+
- lib/solid_agents/runtime/pi_adapter.rb
|
|
155
156
|
- lib/solid_agents/version.rb
|
|
157
|
+
- lib/solid_agents/workflow.rb
|
|
156
158
|
homepage: https://github.com/kaka-ruto/solid_agents
|
|
157
159
|
licenses:
|
|
158
160
|
- MIT
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|
|
@@ -1,12 +0,0 @@
|
|
|
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
|