silas 0.1.2 → 0.1.4
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 +41 -0
- data/README.md +17 -0
- data/lib/generators/silas/install/install_generator.rb +21 -9
- data/lib/generators/silas/install/templates/example_schedule.md +1 -1
- data/lib/generators/silas/install/templates/initializer.rb +4 -1
- data/lib/generators/silas/install/templates/ruby_llm.rb +9 -0
- data/lib/silas/chat.rb +137 -0
- data/lib/silas/configuration.rb +7 -3
- data/lib/silas/engines/ruby_llm.rb +8 -1
- data/lib/silas/ledger.rb +10 -3
- data/lib/silas/schedule.rb +3 -1
- data/lib/silas/version.rb +1 -1
- data/lib/silas.rb +1 -0
- data/lib/tasks/silas_chat.rake +16 -0
- metadata +10 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: be0574c8a9c71366220b9be69d9f3d679a6c0a469d907bec1fb2a7d53436f6b3
|
|
4
|
+
data.tar.gz: 5ac2adcf62f854ec3a4b8206c0d9a19bd33bb3b631615e36bffed425757126ca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3a0be6b63aec608abe5cf2c72f0533c79b7150f8d389344a36e68c90122055fb1f304a4a251ade503467c875c21d99ddd9d136fa37f4d4734d1d5bda9c158bd6
|
|
7
|
+
data.tar.gz: 1340782f9cb00620e4e1ed5e9c5b9dc9fc8d96a9310d3d9e67e55004363352e4d17e98f62d1cb6054738b2fed1052c0c7e92f576f5afe587a33770db5294cc48
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.4
|
|
4
|
+
|
|
5
|
+
- **Fresh-app quickstart actually works.** A from-scratch install previously
|
|
6
|
+
failed at its own post-install steps: the generated default model
|
|
7
|
+
("claude-sonnet-5") wasn't in ruby_llm 1.16's bundled registry, and no
|
|
8
|
+
provider-key initializer was generated, so the first turn raised. The
|
|
9
|
+
generator now emits `config/initializers/ruby_llm.rb` (maps
|
|
10
|
+
`ANTHROPIC_API_KEY`), defaults to the registry-known `claude-opus-4-8`, and a
|
|
11
|
+
missing model raises an actionable error suggesting
|
|
12
|
+
`RubyLLM.models.refresh!`.
|
|
13
|
+
- **`silas:schedules` no longer crashes on the generator's own template.** The
|
|
14
|
+
example schedule's ERB comment rendered a leading blank line the frontmatter
|
|
15
|
+
parser rejected; template fixed and the parser now tolerates leading
|
|
16
|
+
whitespace.
|
|
17
|
+
- **`bin/ci` is never clobbered.** Rails 8.1 ships a real bin/ci; the installer
|
|
18
|
+
now leaves an existing one untouched and tells you to add
|
|
19
|
+
`bin/rails silas:eval` to it.
|
|
20
|
+
- **Correct Opus 4.8 pricing.** Default `model_prices` had Opus 4.8 at $15/$75
|
|
21
|
+
per MTok; it is $5/$25. Added Sonnet 4.6 and the `claude-haiku-4-5` alias.
|
|
22
|
+
- **ruby_llm dependency bounded `< 2`** (2.0 removes APIs the engine relies on).
|
|
23
|
+
- **`bin/rails silas:chat` — a terminal REPL for your agent.** The dev-loop a
|
|
24
|
+
hosted platform gives you from its CLI, except there is no platform: it runs
|
|
25
|
+
inside your app, tools hit your real dev database, and parked approvals prompt
|
|
26
|
+
inline (`approve? [y]es / [d]ecline / [s]kip`) calling the same
|
|
27
|
+
`approve!`/`decline!` as the inbox and Slack. `SESSION=id` resumes a session;
|
|
28
|
+
a new message while a turn is parked steers you to the pending approvals. The
|
|
29
|
+
task forces the synchronous `:inline` adapter for its own process (a REPL
|
|
30
|
+
wants each turn settled before the next prompt; production still runs Solid
|
|
31
|
+
Queue).
|
|
32
|
+
|
|
33
|
+
## 0.1.3
|
|
34
|
+
|
|
35
|
+
- **Ledger: parallel graded gates.** When the model emits several tool calls in
|
|
36
|
+
one step, `settle!` now settles every invocation instead of stopping at the
|
|
37
|
+
first one that needs approval. An ungated call (e.g. a low-risk write) runs
|
|
38
|
+
immediately even when a gated sibling (e.g. a money move) parks for a human —
|
|
39
|
+
it is no longer stranded behind the approval. This changes only *timing*, not
|
|
40
|
+
safety: an unsettled sibling already executed on resume regardless of the
|
|
41
|
+
human's approve/decline, so stopping early only delayed independent work.
|
|
42
|
+
Regression test in `spec/silas/parallel_tool_calls_spec.rb`.
|
|
43
|
+
|
|
3
44
|
## 0.1.2
|
|
4
45
|
|
|
5
46
|
- **Security (email channel scaffold): approvals no longer go to the session
|
data/README.md
CHANGED
|
@@ -53,6 +53,23 @@ session.pending_approvals.first.approve!(by: "daniel")
|
|
|
53
53
|
session.continue(input: "Now email the customer.")
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
Or talk to it from the terminal — the REPL runs *inside your app*, so tools hit
|
|
57
|
+
your real dev database, and parked approvals prompt inline (the same
|
|
58
|
+
`approve!`/`decline!` as the inbox and Slack):
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
$ bin/rails silas:chat
|
|
62
|
+
you> Refund order 42, £12.50
|
|
63
|
+
✓ lookup_order(order_id: 42)
|
|
64
|
+
⏸ issue_refund(order_id: 42, amount: 1250) — awaiting approval
|
|
65
|
+
|
|
66
|
+
approval needed — issue_refund(order_id: 42, amount: 1250)
|
|
67
|
+
approve? [y]es / [d]ecline / [s]kip> y
|
|
68
|
+
agent> Done — £12.50 refunded on order 42.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`SESSION=id` resumes an existing session.
|
|
72
|
+
|
|
56
73
|
## The durability contract (what's actually guaranteed)
|
|
57
74
|
|
|
58
75
|
Verified by `chaos_host/bin/chaos` — the harness that kill -9s a live agent
|
|
@@ -7,6 +7,9 @@ module Silas
|
|
|
7
7
|
|
|
8
8
|
def create_initializer
|
|
9
9
|
template "initializer.rb", "config/initializers/silas.rb"
|
|
10
|
+
# ruby_llm reads no provider keys from ENV on its own; without this the
|
|
11
|
+
# first model call raises ConfigurationError.
|
|
12
|
+
template "ruby_llm.rb", "config/initializers/ruby_llm.rb"
|
|
10
13
|
end
|
|
11
14
|
|
|
12
15
|
def create_agent_directory
|
|
@@ -28,11 +31,19 @@ module Silas
|
|
|
28
31
|
route 'mount Silas::Engine => "/silas"'
|
|
29
32
|
end
|
|
30
33
|
|
|
31
|
-
# Evals as a deploy gate: an example scenario + a bin/ci wrapper.
|
|
34
|
+
# Evals as a deploy gate: an example scenario + a bin/ci wrapper. Rails
|
|
35
|
+
# 8.1 apps ship their own bin/ci (rubocop, brakeman, tests) — never
|
|
36
|
+
# clobber it; tell the user to add the eval gate to theirs instead.
|
|
32
37
|
def create_eval_gate
|
|
33
38
|
template "example_eval.rb", "test/agent_evals/example_eval.rb"
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
|
|
40
|
+
if File.exist?(File.expand_path("bin/ci", destination_root))
|
|
41
|
+
say "bin/ci already exists — left untouched. Add `bin/rails silas:eval` " \
|
|
42
|
+
"to it to gate deploys on agent evals.", :yellow
|
|
43
|
+
else
|
|
44
|
+
copy_file "bin_ci", "bin/ci"
|
|
45
|
+
chmod "bin/ci", 0o755
|
|
46
|
+
end
|
|
36
47
|
end
|
|
37
48
|
|
|
38
49
|
def add_rescuer_recurring_task
|
|
@@ -65,14 +76,15 @@ module Silas
|
|
|
65
76
|
|
|
66
77
|
Silas installed. Next:
|
|
67
78
|
1. bin/rails db:migrate
|
|
68
|
-
2.
|
|
69
|
-
3.
|
|
70
|
-
4.
|
|
71
|
-
5.
|
|
72
|
-
6.
|
|
79
|
+
2. export ANTHROPIC_API_KEY=sk-ant-... (config/initializers/ruby_llm.rb reads it)
|
|
80
|
+
3. Edit app/agent/instructions.md (your agent's persona)
|
|
81
|
+
4. Write tools in app/agent/tools/ (keyword signature = schema)
|
|
82
|
+
5. Talk to it: bin/rails silas:chat (or Silas.agent.start(input: "hello"))
|
|
83
|
+
6. Schedules: edit app/agent/schedules/*, then `bin/rails silas:schedules`
|
|
84
|
+
7. Channels (optional): set credentials.silas.slack.{signing_secret,bot_token}
|
|
73
85
|
for Slack; route inbound mail to Silas::AgentMailbox for email.
|
|
74
86
|
Delete app/agent/channels/{slack,email}.rb to disable.
|
|
75
|
-
|
|
87
|
+
8. Inbox: live at /silas/inbox once you set config.inbox_auth
|
|
76
88
|
(deny-by-default until you do).
|
|
77
89
|
MSG
|
|
78
90
|
end
|
|
@@ -3,7 +3,10 @@ Silas.configure do |config|
|
|
|
3
3
|
# :agent_sdk (a `claude -p` subprocess; API-key auth only). See silas/README.
|
|
4
4
|
config.engine = :ruby_llm
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
# Any model your installed ruby_llm's registry resolves (newer Claude models
|
|
7
|
+
# may need `RubyLLM.models.refresh!` first). Alternatives: "claude-sonnet-5"
|
|
8
|
+
# (balanced), "claude-haiku-4-5" (fastest/cheapest).
|
|
9
|
+
config.default_model = "claude-opus-4-8"
|
|
7
10
|
|
|
8
11
|
# Parked approvals expire after this long (the turn fails as approval_expired).
|
|
9
12
|
# config.approval_ttl = 7.days
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Point RubyLLM at your model provider. RubyLLM does NOT read provider API keys
|
|
2
|
+
# from the environment automatically — this initializer maps them in.
|
|
3
|
+
#
|
|
4
|
+
# export ANTHROPIC_API_KEY=sk-ant-...
|
|
5
|
+
#
|
|
6
|
+
# Any provider RubyLLM supports works the same way (openai_api_key, etc.).
|
|
7
|
+
RubyLLM.configure do |c|
|
|
8
|
+
c.anthropic_api_key = ENV["ANTHROPIC_API_KEY"]
|
|
9
|
+
end if ENV["ANTHROPIC_API_KEY"].present?
|
data/lib/silas/chat.rb
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
module Silas
|
|
2
|
+
# Terminal REPL for talking to the app's agent: `bin/rails silas:chat`.
|
|
3
|
+
#
|
|
4
|
+
# The dev-loop equivalent of a hosted platform's CLI — except there is no
|
|
5
|
+
# platform: this runs inside your app, so tools hit your real dev database
|
|
6
|
+
# and the transcript is rows in your own Postgres. Parked approvals prompt
|
|
7
|
+
# inline and call the exact same approve!/decline! as the inbox and Slack.
|
|
8
|
+
#
|
|
9
|
+
# IO is injected so the loop is testable; the rake task wires $stdin/$stdout
|
|
10
|
+
# and forces the synchronous :inline adapter (a REPL wants each turn settled
|
|
11
|
+
# before the next prompt, and the dev-default Async adapter is unsafe).
|
|
12
|
+
class Chat
|
|
13
|
+
GLYPH = { "completed" => "✓", "failed" => "✗", "pending" => "⏸",
|
|
14
|
+
"started" => "…", "in_doubt" => "?" }.freeze
|
|
15
|
+
|
|
16
|
+
def initialize(io_in: $stdin, io_out: $stdout, actor: ENV["USER"] || "cli", session: nil)
|
|
17
|
+
@in = io_in
|
|
18
|
+
@out = io_out
|
|
19
|
+
@actor = actor
|
|
20
|
+
@session = session
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def run
|
|
24
|
+
banner
|
|
25
|
+
if @session && pending_for(@session).exists? # resuming a session that parked last time
|
|
26
|
+
settle_parked
|
|
27
|
+
print_outcome(@session.turns.reload.last)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
loop do
|
|
31
|
+
@out.print "\nyou> "
|
|
32
|
+
line = @in.gets
|
|
33
|
+
break if line.nil?
|
|
34
|
+
|
|
35
|
+
line = line.strip
|
|
36
|
+
next if line.empty?
|
|
37
|
+
break if %w[exit quit].include?(line.downcase)
|
|
38
|
+
|
|
39
|
+
submit(line)
|
|
40
|
+
end
|
|
41
|
+
@out.puts "bye — session #{@session.id}" if @session
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def banner
|
|
47
|
+
description = Silas.agent.description.presence || "your agent"
|
|
48
|
+
@out.puts "Silas chat — #{description} (#{Silas.agent.model})."
|
|
49
|
+
@out.puts "Approvals prompt inline. 'exit' or Ctrl-D to quit."
|
|
50
|
+
@out.puts "Resuming session #{@session.id} (#{@session.turns.count} turns)." if @session
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def submit(text)
|
|
54
|
+
turn =
|
|
55
|
+
if @session.nil?
|
|
56
|
+
@session = Silas.agent.start(input: text)
|
|
57
|
+
@session.turns.first
|
|
58
|
+
else
|
|
59
|
+
@session.continue(input: text)
|
|
60
|
+
end
|
|
61
|
+
# start/continue enqueue the loop job; the :inline adapter has already run
|
|
62
|
+
# it synchronously by the time they return.
|
|
63
|
+
report(turn)
|
|
64
|
+
rescue TurnInProgressError
|
|
65
|
+
@out.puts "! a turn is still parked awaiting approval — settle it first:"
|
|
66
|
+
settle_parked
|
|
67
|
+
print_outcome(@session.turns.reload.last)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def report(turn)
|
|
71
|
+
turn.reload
|
|
72
|
+
print_trace(turn)
|
|
73
|
+
|
|
74
|
+
if turn.parked?
|
|
75
|
+
settle_parked
|
|
76
|
+
turn.reload
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
print_outcome(turn)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def print_outcome(turn)
|
|
83
|
+
case turn.reload.status
|
|
84
|
+
when "completed"
|
|
85
|
+
@out.puts "agent> #{turn.answer_text}"
|
|
86
|
+
when "waiting", "in_doubt"
|
|
87
|
+
@out.puts "(parked — #{pending_for(turn.session).count} approval(s) still pending; " \
|
|
88
|
+
"they also render in /silas/inbox)"
|
|
89
|
+
when "failed"
|
|
90
|
+
@out.puts "(turn failed: #{turn.failure_reason})"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def print_trace(turn)
|
|
95
|
+
ToolInvocation.where(turn_id: turn.id).order(:id).each do |inv|
|
|
96
|
+
gate = inv.approval_state == "required" ? " — awaiting approval" : ""
|
|
97
|
+
@out.puts " #{GLYPH.fetch(inv.status, '·')} #{inv.tool_name}(#{compact_args(inv.arguments)})#{gate}"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Prompt for every parked approval in the session. Approving resumes the
|
|
102
|
+
# turn synchronously (inline adapter), so new invocations may appear —
|
|
103
|
+
# loop until nothing is pending or the user skips.
|
|
104
|
+
def settle_parked
|
|
105
|
+
loop do
|
|
106
|
+
invocation = pending_for(@session).order(:id).first
|
|
107
|
+
break unless invocation
|
|
108
|
+
|
|
109
|
+
@out.puts "\napproval needed — #{invocation.tool_name}(#{compact_args(invocation.arguments)})"
|
|
110
|
+
@out.print "approve? [y]es / [d]ecline / [s]kip> "
|
|
111
|
+
answer = @in.gets&.strip&.downcase
|
|
112
|
+
|
|
113
|
+
case answer
|
|
114
|
+
when "y", "yes"
|
|
115
|
+
invocation.approve!(by: @actor)
|
|
116
|
+
@out.puts
|
|
117
|
+
print_trace(@session.turns.reload.last)
|
|
118
|
+
when "d", "decline"
|
|
119
|
+
@out.print "reason> "
|
|
120
|
+
reason = @in.gets&.strip
|
|
121
|
+
invocation.decline!(reason: reason.presence || "declined from CLI", by: @actor)
|
|
122
|
+
else
|
|
123
|
+
@out.puts "(left parked — approve later here or in /silas/inbox)"
|
|
124
|
+
break
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def pending_for(session)
|
|
130
|
+
session.pending_approvals.where(status: %w[pending in_doubt])
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def compact_args(arguments)
|
|
134
|
+
(arguments || {}).map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
data/lib/silas/configuration.rb
CHANGED
|
@@ -64,7 +64,9 @@ module Silas
|
|
|
64
64
|
def initialize
|
|
65
65
|
@engine = :ruby_llm
|
|
66
66
|
@auth = :api_key
|
|
67
|
-
|
|
67
|
+
# Must be resolvable by the installed ruby_llm's model registry — newer
|
|
68
|
+
# Claude models may need `RubyLLM.models.refresh!` before they resolve.
|
|
69
|
+
@default_model = "claude-opus-4-8"
|
|
68
70
|
@queue_name = :default
|
|
69
71
|
@around_model_call = nil
|
|
70
72
|
@approval_ttl = 7.days
|
|
@@ -102,10 +104,12 @@ module Silas
|
|
|
102
104
|
@inbox_public_read = false
|
|
103
105
|
@inbox_actor = ->(controller) { controller.try(:current_user)&.try(:email) || "inbox" }
|
|
104
106
|
# Current Claude rates as cost-units per 1k tokens (1e6 units = $1):
|
|
105
|
-
#
|
|
107
|
+
# Opus 4.8 $5/$25, Sonnet $3/$15, Haiku $1/$5 per million tokens.
|
|
106
108
|
@model_prices = {
|
|
109
|
+
"claude-opus-4-8" => { in: 5000, out: 25_000 },
|
|
107
110
|
"claude-sonnet-5" => { in: 3000, out: 15_000 },
|
|
108
|
-
"claude-
|
|
111
|
+
"claude-sonnet-4-6" => { in: 3000, out: 15_000 },
|
|
112
|
+
"claude-haiku-4-5" => { in: 1000, out: 5000 },
|
|
109
113
|
"claude-haiku-4-5-20251001" => { in: 1000, out: 5000 }
|
|
110
114
|
}
|
|
111
115
|
end
|
|
@@ -26,7 +26,14 @@ module Silas
|
|
|
26
26
|
private
|
|
27
27
|
|
|
28
28
|
def build_chat(context, &on_event)
|
|
29
|
-
chat =
|
|
29
|
+
chat = begin
|
|
30
|
+
::RubyLLM.chat(model: context[:model])
|
|
31
|
+
rescue ::RubyLLM::ModelNotFoundError
|
|
32
|
+
raise Silas::Error,
|
|
33
|
+
"Model #{context[:model].inspect} is not in ruby_llm's model registry. " \
|
|
34
|
+
"Newer models may need a registry refresh (`RubyLLM.models.refresh!`), " \
|
|
35
|
+
"or pick a registry-known model in config.default_model / agent.yml."
|
|
36
|
+
end
|
|
30
37
|
chat.with_instructions(context[:system]) if context[:system].present?
|
|
31
38
|
context[:tools].each { |definition| chat.with_tool(HaltProxy.new(definition)) }
|
|
32
39
|
|
data/lib/silas/ledger.rb
CHANGED
|
@@ -34,12 +34,19 @@ module Silas
|
|
|
34
34
|
"the continuation believes are committed."
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
# Settle EVERY invocation in the step, then report whether the turn must
|
|
38
|
+
# park. When the model emits parallel tool calls, an ungated one must not
|
|
39
|
+
# be blocked by a gated sibling's approval — and it wouldn't be protected
|
|
40
|
+
# anyway: an unsettled sibling executes on resume regardless of whether the
|
|
41
|
+
# human approves or declines the gated call, so stopping at the first park
|
|
42
|
+
# only delays independent work. Execute what can proceed; park if any
|
|
43
|
+
# invocation is awaiting a human.
|
|
37
44
|
def settle!(step, resolver:)
|
|
45
|
+
parked = false
|
|
38
46
|
step.tool_invocations.order(:id).each do |invocation|
|
|
39
|
-
|
|
40
|
-
return :parked if outcome == :parked
|
|
47
|
+
parked = true if settle_invocation!(invocation, resolver) == :parked
|
|
41
48
|
end
|
|
42
|
-
:completed
|
|
49
|
+
parked ? :parked : :completed
|
|
43
50
|
end
|
|
44
51
|
|
|
45
52
|
# Drive a SINGLE freshly-created invocation to a terminal state — the
|
data/lib/silas/schedule.rb
CHANGED
|
@@ -38,7 +38,9 @@ module Silas
|
|
|
38
38
|
|
|
39
39
|
def self.from_markdown(name, path)
|
|
40
40
|
content = File.read(path)
|
|
41
|
-
|
|
41
|
+
# Tolerate leading whitespace/blank lines (e.g. an ERB comment in a
|
|
42
|
+
# generator template renders to an empty first line).
|
|
43
|
+
if content.sub(/\A\s+/, "") =~ /\A---\s*\n(.*?)\n---\s*\n(.*)\z/m
|
|
42
44
|
frontmatter = YAML.safe_load(Regexp.last_match(1)) || {}
|
|
43
45
|
body = Regexp.last_match(2)
|
|
44
46
|
else
|
data/lib/silas/version.rb
CHANGED
data/lib/silas.rb
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
namespace :silas do
|
|
2
|
+
desc "Chat with your agent in the terminal (REPL). SESSION=id resumes one; approvals prompt inline."
|
|
3
|
+
task chat: :environment do
|
|
4
|
+
# A REPL wants each turn settled synchronously in this one process before
|
|
5
|
+
# the next prompt — and Rails' dev-default Async adapter is unsafe for Silas
|
|
6
|
+
# (it double-executes continuation retries; Silas warns at boot). Force the
|
|
7
|
+
# synchronous :inline adapter for this process only; production still runs
|
|
8
|
+
# Solid Queue.
|
|
9
|
+
ActiveJob::Base.queue_adapter = :inline
|
|
10
|
+
Silas.config.isolate_steps = false
|
|
11
|
+
Silas::Registry.install!
|
|
12
|
+
|
|
13
|
+
session = ENV["SESSION"].present? ? Silas::Session.find(ENV["SESSION"]) : nil
|
|
14
|
+
Silas::Chat.new(session: session).run
|
|
15
|
+
end
|
|
16
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: silas
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel St Paul
|
|
@@ -30,6 +30,9 @@ dependencies:
|
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: '1.0'
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '2'
|
|
33
36
|
type: :runtime
|
|
34
37
|
prerelease: false
|
|
35
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -37,6 +40,9 @@ dependencies:
|
|
|
37
40
|
- - ">="
|
|
38
41
|
- !ruby/object:Gem::Version
|
|
39
42
|
version: '1.0'
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '2'
|
|
40
46
|
- !ruby/object:Gem::Dependency
|
|
41
47
|
name: rspec-rails
|
|
42
48
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -153,6 +159,7 @@ files:
|
|
|
153
159
|
- lib/generators/silas/install/templates/example_tool.rb
|
|
154
160
|
- lib/generators/silas/install/templates/initializer.rb
|
|
155
161
|
- lib/generators/silas/install/templates/instructions.md
|
|
162
|
+
- lib/generators/silas/install/templates/ruby_llm.rb
|
|
156
163
|
- lib/silas.rb
|
|
157
164
|
- lib/silas/agent.rb
|
|
158
165
|
- lib/silas/agent_scope.rb
|
|
@@ -161,6 +168,7 @@ files:
|
|
|
161
168
|
- lib/silas/agent_sdk/version_guard.rb
|
|
162
169
|
- lib/silas/budget.rb
|
|
163
170
|
- lib/silas/channel.rb
|
|
171
|
+
- lib/silas/chat.rb
|
|
164
172
|
- lib/silas/configuration.rb
|
|
165
173
|
- lib/silas/connection.rb
|
|
166
174
|
- lib/silas/connections.rb
|
|
@@ -202,6 +210,7 @@ files:
|
|
|
202
210
|
- lib/silas/tools/load_skill.rb
|
|
203
211
|
- lib/silas/tools/run_code.rb
|
|
204
212
|
- lib/silas/version.rb
|
|
213
|
+
- lib/tasks/silas_chat.rake
|
|
205
214
|
- lib/tasks/silas_eval.rake
|
|
206
215
|
- lib/tasks/silas_schedules.rake
|
|
207
216
|
homepage: https://github.com/danielstpaul/silas
|