Everything lives in one block, usually in config/initializers/silas.rb (the
installer generates a commented starter):
Silas.configuredo|c|# ...end
Inference
Option
Default
Meaning
adapter
:ruby_llm
The inference seam. :ruby_llm (any provider RubyLLM supports), or any object responding to #execute_step — the eval harness, the chaos suite, and the template’s keyless demo all inject one.
default_model
"claude-sonnet-4-5"
Used when agent.yml doesn’t set model:. Must resolve in your installed RubyLLM registry.
around_model_call
nil
Wrap every model call — e.g. ->(ctx, &call) { RubyLLM::Resilience.chain(:anthropic) { call.() } }.
queue_name
:default
Active Job queue for agent turns.
The loop
Option
Default
Meaning
max_steps
25
Hard cap on model calls per turn; agent.ymllimits.max_steps overrides per agent. Hitting it fails the turn (max_steps).
compact_at
0.9
Context compaction trigger. A Float in (0, 1] = fraction of the model’s registry context window; an Integer = absolute token threshold (the form custom adapters need); nil/false disables. Compaction summarises prior turns into a persisted, exactly-once row — the current turn is never compacted, and replays stay byte-identical.
isolate_steps
true
Continuation isolation per step — the durability contract. Leave on; specs may disable for inline runs.
Approvals & questions
Option
Default
Meaning
approval_ttl
7.days
How long a parked approval (or in-doubt invocation) waits before expiring. Parked-forever ghosts are a bug, not a feature.
ask_question
true
The built-in tool that parks the turn to ask the operator something. Toggling it changes the definitions digest — settle parked turns before flipping, or they fail loudly on resume (the nondeterminism guard working as designed).
Memory
Option
Default
Meaning
memory
true
false removes the memory tools entirely (digest note above applies).
memory_approval
:always
Every remember parks a card for a human; :never auto-approves.
memory_injection_limit
8
How many recent memories are injected into each turn; recall digs deeper on demand.
Inbox (/silas/inbox)
Option
Default
Meaning
inbox_auth
deny (404)
The lambda DENIES by rendering (or head-ing) and PASSES by not rendering — Devise-compatible: ->(controller) { controller.head :not_found unless controller.current_user&.admin? }.
inbox_public_read
false
Read-only for anyone; approve/decline/answer stay gated.
inbox_actor
current_user&.email \|\| "inbox"
Identity recorded on approvals/declines/answers made in the inbox.
inbox_streaming
nil (auto)
Live Turbo updates when the host has turbo-rails; false forces the polling fallback.
model_prices
{}
Override map for cost accounting: {"model-id" => { in:, out: }} in units per 1k tokens, 1e6 units = $1 (a $3/MTok rate is 3000). Everything else prices from RubyLLM’s registry.
JSON API (/silas/api/v1)
Option
Default
Meaning
api_auth
deny (404)
Same contract as inbox_auth.
api_actor
"api"
Identity recorded on approvals made through the API.
api_stream_poll_interval
0.5
Seconds between SSE row polls.
api_stream_max_duration
300
Seconds before an SSE stream closes itself (clients reconnect with Last-Event-ID); bounds thread hold.
Channels
Option
Default
Meaning
slack_signing_secret / slack_bot_token
credentials.silas.slack.*
Explicit setters override the credentials path; nil disables Slack.
Evals
Option
Default
Meaning
eval_dir
"test/agent_evals"
Where *_eval.rb scenarios live (bin/rails silas:eval).
eval_grader
nil
Custom LLM grader for assert_rubric; offline, rubric asserts skip rather than fail.
Sandbox
Option
Default
Meaning
sandbox
:none
Code execution off. :docker (interim), or any object responding to #run — e.g. Hermetic.gvisor(image: "python:3.12-slim"). Configuring one advertises the run_code tool automatically.
Bind host for the in-process MCP server (serving your tools to MCP clients).
mcp_client_factory
nil
->(connection) { client } — inject a fake MCP client per connection in tests.
The remaining accessors (tool_resolver, tool_definitions,
definitions_digest, skills, schedules, subagent_*,
named_agent_scopes, channel_resolver, instructions_dir,
agent_override) are internal wiring seams the Registry fills at boot — test
hooks, not app configuration.
Boot guards (fail-loud misconfiguration)
Two checks run at boot and raise in production, warn in development:
No provider key configured while adapter = :ruby_llm — the most common
first-run failure, surfaced at boot with the fix instead of dying inside the
first turn.
ActiveJob on the in-process :async adapter — it runs a re-enqueued
continuation concurrently with the original, double-executing steps and
voiding exactly-once. Use Solid Queue (production) or :inline
(scripts/demos). bin/rails silas:doctor checks both, everywhere.