silas

Gem CI License: MIT

Silas is a Rails-native framework for durable AI agents. An agent's capabilities live as plain files in conventional locations inside the app you already run — easy to inspect, extend, and operate — and its tool effects land exactly once, even through a crash.

The filesystem is the authoring interface

A typical Silas agent:

app/agent/
  instructions.md   # the persona
  agent.yml         # model + limits — data only
  tools/            # one file per tool; filename = identity, keywords = schema
  skills/           # markdown playbooks, loaded on demand
  schedules/        # cron frontmatter -> recurring turns
  channels/         # slack.rb, email.rb — transports bound to the loop
  connections/      # remote MCP servers as <name>.yml

Documentation lives at danielstpaul.github.io/silas — start with the tutorial. The same docs ship inside the gem (bundle show silas), and the installer writes a Claude Code skill so coding agents working in your app know the conventions.

Quick start

rails new my-agent -m https://raw.githubusercontent.com/danielstpaul/silas/main/templates/desk.rb

This creates a new agent app with Solid Queue wired, a starter refund-desk agent installed, and a keyless demo — the first cd my-agent && bin/dev needs no API key. More starting shapes live in templates/ — swap desk.rb for analyst.rb to start from a scheduled reporting agent instead.

To add Silas to an existing app:

bundle add silas
bin/rails generate silas:install
bin/rails db:migrate

bin/rails silas:doctor verifies the whole setup.

A minimal example

Replace app/agent/instructions.md with your agent's persona:

You are the refund desk. Look orders up before promising anything, and never
quote an amount a tool didn't return.

Create a tool at app/agent/tools/issue_refund.rb — the keyword signature is the schema the model sees:

class Agent::Tools::IssueRefund < Silas::Tool
  description "Refund part or all of an order."
  approval ->(session:, input:) { input[:amount_pence] > 2_500 ? :user_approval : :approved }
  transactional!   # DB effect + ledger commit atomically -> exactly-once

  def call(number:, amount_pence:, reason:)
    order = Order.find_by!(number: number)
    refund = order.refunds.create!(amount_pence:, reason:)
    { refunded_pence: refund.amount_pence, order: order.number }
  end
end

Restart, then talk to it:

bin/rails silas:chat

Refunds over £25 hold for a person — in the operator inbox the gem mounts at /silas/inbox, in Slack, or over the JSON API — and approving resumes the turn exactly where it stopped, with exactly one refund row in your database. How that's guaranteed (and verified with a kill -9 chaos harness on every release): guarantees.

Status

Early (0.6.x) and moving fast. Requires Rails >= 8.1 (Active Job Continuations) and Solid Queue >= 1.2; any model provider RubyLLM supports — Anthropic direct, OpenRouter, OpenAI-compatible gateways, local runtimes (providers guide).

Community

Questions, ideas, and bug reports → issues.

Contributing

See CONTRIBUTING.md — including the short list of things Silas deliberately doesn't do, and the chaos gate that protects the durability contract. Everyone interacting here is expected to follow the code of conduct.

Security

Please report vulnerabilities privately via GitHub's vulnerability reporting — see SECURITY.md.

License

MIT.