silas 0.5.0 → 0.6.1

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +145 -0
  3. data/DEPLOY.md +111 -0
  4. data/README.md +87 -256
  5. data/app/helpers/silas/inbox/trace_helper.rb +29 -6
  6. data/app/models/concerns/silas/inbox/broadcastable.rb +12 -0
  7. data/app/views/layouts/silas/inbox.html.erb +89 -30
  8. data/app/views/silas/inbox/invocations/_approval_card.html.erb +11 -2
  9. data/app/views/silas/inbox/invocations/_invocation.html.erb +17 -5
  10. data/app/views/silas/inbox/sessions/_cost.html.erb +3 -1
  11. data/app/views/silas/inbox/sessions/_row.html.erb +14 -0
  12. data/app/views/silas/inbox/sessions/index.html.erb +16 -15
  13. data/app/views/silas/inbox/sessions/show.html.erb +10 -1
  14. data/app/views/silas/inbox/turns/_header.html.erb +31 -25
  15. data/app/views/silas/inbox/turns/_turn.html.erb +5 -3
  16. data/docs/agents.md +81 -0
  17. data/docs/budgets.md +67 -0
  18. data/docs/cancellation.md +41 -0
  19. data/docs/channels.md +290 -0
  20. data/docs/configuration.md +106 -0
  21. data/docs/connections.md +58 -0
  22. data/docs/conventions.md +161 -0
  23. data/docs/evals.md +95 -0
  24. data/docs/guarantees.md +76 -0
  25. data/docs/inbox-and-api.md +91 -0
  26. data/docs/memory.md +35 -0
  27. data/docs/sandbox.md +44 -0
  28. data/docs/tools.md +77 -0
  29. data/docs/tutorial.md +272 -0
  30. data/docs/vs-eve.md +93 -0
  31. data/docs/why-silas.md +87 -0
  32. data/lib/generators/silas/install/install_generator.rb +9 -0
  33. data/lib/generators/silas/install/templates/claude_skill.md +136 -0
  34. data/lib/generators/silas/install/templates/ruby_llm.rb +4 -1
  35. data/lib/silas/connection.rb +16 -0
  36. data/lib/silas/eval/dsl.rb +7 -2
  37. data/lib/silas/version.rb +1 -1
  38. metadata +22 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f722788fa436905b6ea94ee9666ca71013fa6eace683f7d3dc8eec4f257fbe2
4
- data.tar.gz: 85d3ae116127d4b91207526d7936440092250854380938a94eba177cd26f3eeb
3
+ metadata.gz: d6fb5d6a2e04f91a4bc0ac0569a37da43017f6adae7209148878a2cb8ac41a66
4
+ data.tar.gz: 9d8ed143aac8d96d264a10ed3cf87c8ecee1ed838ba01ca67075a4a9a07e3b09
5
5
  SHA512:
6
- metadata.gz: 645b843c5dbde892fa0725c1f33302bf39b3b10eef43ff14b0828efeb0420ead72d82e34b9bb3828cf99aa8f880db4f6017f2913023884a679385ca68a21ae41
7
- data.tar.gz: 2b5b8df36d8747fde43b1eccffb466f17a3a9ef430ebc77b055b36c73ff987c4e07c66eb14ef2265d34b37b1ff824cf4681737a2bca1333e4e7dad88c8db142b
6
+ metadata.gz: e01f7110a093b91b30af1c08d4d8167aaaa24861812702c10ba5078f952e7eea4ddb5851e484f6cb2815d171271de9a45d8b7cc275e3d59dcaa2541ad9731628
7
+ data.tar.gz: c84142c165b45db56d5761a64a62993e7332a2209593ff1c59afd58723beafc5e2bd2c14f1495fbb8963d664a69469f28829dece7d6e973e8a5e123963ca526b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,150 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.1 (2026-07-26)
4
+
5
+ Two live-inbox fixes found by driving real apps, a security hardening pass,
6
+ and the polished brand kit.
7
+
8
+ ### Changed
9
+
10
+ - **Polished brand geometry everywhere.** Lamps sit on a shared r4.8 circle
11
+ (proceed and held aspects now one geometry), the favicon is redrawn
12
+ natively at 32px with a heavier housing (it reads at 16px now), and the
13
+ wordmark lockup is cropped to the ink — re-outlined to font-independent
14
+ paths on the new geometry. The inbox layout's favicon and header mark
15
+ carry the new coordinates, as do the templates' landing pages.
16
+ - The docs gained real product imagery: an 18-frame park→approve→clear GIF
17
+ captured from a genuine template-app run, the held approval card, and the
18
+ signal board — on the site landing, inbox-and-api, and tutorial pages.
19
+
20
+ ### Fixed
21
+
22
+ - **The turn pill now flips to held live.** Turbo `replace` swaps the target
23
+ element itself, but the header's and cost line's broadcast-target ids lived
24
+ on wrapper divs in the parent views — so the FIRST status broadcast
25
+ destroyed the target and every later one (including the park that reads
26
+ **held**) silently no-opped until a reload. The partials now carry their own
27
+ root ids, with regression specs asserting a replace re-emits its target.
28
+ - **The first broadcast of a fresh worker no longer dies on lazy routes.**
29
+ Rails 8.1's lazy route set only retries a missing url helper when the app
30
+ routes were *just* loaded; in a worker's first Turbo render the engine's
31
+ helper module could predate its route draw, killing the job silently
32
+ (`undefined method 'cancel_inbox_turn_path'`). `silas_engine_path` now
33
+ forces the draw once on miss.
34
+
35
+ ### Security
36
+
37
+ - **Connections refuse credentials over plaintext http.** A connection with
38
+ `auth:` configured and an `http://` URL to a remote host now fails loudly at
39
+ parse time (localhost exempt; unparseable URLs with auth fail closed).
40
+ Finding from the pre-release security audit — which otherwise confirmed the
41
+ posture: fail-closed HMAC webhook verification with constant-time compare
42
+ and a replay window, expiring signed approval tokens, deny-by-default
43
+ inbox/API auth enforced at the base controllers, argv-array sandbox exec
44
+ (no shell interpolation), timing-safe MCP token compare, restricted ERB
45
+ bindings, no interpolated SQL, Brakeman and bundler-audit clean.
46
+
47
+ ## 0.6.0 (2026-07-26)
48
+
49
+ The brand release: the Signals inbox, the docs surface (site + gem-shipped
50
+ guides + tutorial), the templates family (desk + analyst) with its anti-rot
51
+ CI gate, community files, and the framework-first README.
52
+
53
+ ### Added
54
+
55
+ - **Docs for the whole surface.** New guides, all shipped in the gem:
56
+ `tutorial` (build the refund desk outward, one primitive per chapter),
57
+ `guarantees`, `tools`, `agents`, `memory`, `inbox-and-api`, `sandbox`,
58
+ `evals`, `budgets`, `cancellation`, `connections`, and a full
59
+ `configuration` reference. A docs site (Jekyll + just-the-docs, dark-first,
60
+ Archivo/Space Mono, `early · 0.x` chip) builds from `docs/` in CI and
61
+ deploys to Pages, with an `llms.txt` for coding agents. Brand assets
62
+ (`brand/`, hero at `docs/img/`) now live in the repo; the hero image is
63
+ excluded from the packaged gem.
64
+ - **Rails application templates, as a family** — `templates/desk.rb` and
65
+ `templates/analyst.rb`, each a single CI-gated `rails new -m` script.
66
+ `rails new desk -m
67
+ https://raw.githubusercontent.com/danielstpaul/silas/main/templates/desk.rb`
68
+ builds a deployable agent app from nothing: a refund desk with one tool per effect
69
+ mode (`lookup_order` idempotent, `issue_refund` transactional behind a
70
+ £25 approval gate, `notify_customer` at-most-once), Solid Queue **and** Solid
71
+ Cable wired in development (the durability contract needs a real worker; live
72
+ deltas need a cross-process cable), a keyless scripted stand-in so the first
73
+ `bin/dev` works with zero secrets, three deterministic agent evals asserting
74
+ the hold and the exactly-once execution, and a Signals-branded signal-board
75
+ landing page. The template only runs the generators the gem already tests,
76
+ and the `templates_smoke` workflow regenerates and tests an app from every
77
+ template on each push — starters that structurally cannot rot. (eve needs
78
+ template repos because it has no generator story; Rails has application
79
+ templates.) **The analyst** is the second template: a scheduled reporting
80
+ agent — `query_metrics` reads, `flag_anomaly` lands rows exactly once,
81
+ `publish_report` holds at the signal (`approval :always`), a Monday-07:00
82
+ schedule, and a schema-checked `final_answer` (`Turn#answer_data`). Both
83
+ templates also wire `ANTHROPIC_API_KEY` into Kamal's secrets so the
84
+ generated app deploys without a scavenger hunt.
85
+ - **Community files** — `CONTRIBUTING.md` (the deliberate scope no-list, the
86
+ chaos-gate requirement, how templates are contributed), `SECURITY.md`
87
+ (private vulnerability reporting; deny-by-default surfaces; contract
88
+ violations count), and GitHub issue forms.
89
+ - **The gem ships its own docs, and the installer ships a coding-agent skill.**
90
+ `docs/**` and `DEPLOY.md` are in the packaged gem, so `bundle show silas`
91
+ gives a coding agent (or an offline human) the real reference — and the
92
+ README's links stop 404ing for gem-only readers. `rails g silas:install` now
93
+ also writes `.claude/skills/silas/SKILL.md`: the `app/agent/` conventions,
94
+ the effect-mode and approval decision rules, and the ledger rules an agent
95
+ must never violate — so a Claude Code/Codex session building on this app
96
+ gets the framework's judgment without the human learning it first. Gemspec
97
+ gains `documentation_uri` and `bug_tracker_uri`.
98
+
99
+ ### Changed
100
+
101
+ - **The inbox wears the brand** (direction "Signals"). Dark-first — the tokens'
102
+ base is the night palette and light is the `prefers-color-scheme` override —
103
+ with a position-light mark, a lowercase wordmark, and one white "lamp" accent
104
+ that never means state. The seven run states each get their aspect:
105
+ `in_doubt` its own violet (it is neither waiting-by-design nor failed),
106
+ `canceled` a dashed quiet (a lamp going out, not turning red), and two
107
+ UI-only relabels — `waiting` reads **held**, `completed` reads **clear**. The
108
+ database strings and the JSON API are untouched (`docs/conventions.md`).
109
+ - **Approvals and questions are hoisted to the top of the session** — the
110
+ operator never scrolls a long trace hunting for the card; the trace keeps a
111
+ one-line "held at the signal" stub in place. Live parks append their card via
112
+ the same Turbo broadcasts; settled cards remove themselves. Tool arguments
113
+ render as key/value rows and results collapse behind a disclosure; the
114
+ session index groups its rail by who's blocked: **Held / Working / Filed**.
115
+ - The playground's chat page consumes the engine's colour tokens instead of
116
+ re-hardcoding a palette, and hosts the hoisted approval/question cards above
117
+ its transcript.
118
+
119
+ - **README reframed, framework-first** — it now mirrors eve's shape exactly:
120
+ the mark as the logo (dark/light `<picture>`), badges, a one-paragraph
121
+ thesis, "The filesystem is the authoring interface" with the tree, quick
122
+ start, one minimal example, status/community/license. The hero illustration
123
+ moved to the repo's social-preview role (its inbox panel is a designed
124
+ idealization, not a screenshot — it shouldn't sit where users compare it to
125
+ the real `/silas/inbox`). The docs site nav is grouped eve-style — Tutorial
126
+ and Guarantees up top, then **Core / Advanced / Reference / About**
127
+ sections — via build-time frontmatter injection (`site/assemble.rb`, which
128
+ fails the build if a docs page lacks a nav entry), keeping the gem-shipped
129
+ markdown frontmatter-free. `docs/vs-eve.md` was **factually rewritten**
130
+ (2026-07-26): the previous revision described eve as a managed-cloud
131
+ platform; eve is fully self-hostable, and the comparison now rests on what's
132
+ actually different — exactly-once vs documented at-least-once, a shipped
133
+ production inbox vs a dev TUI, memory vs none, and the transaction-boundary
134
+ argument, date-stamped against eve 0.27.6 and framed as siblings (pick by
135
+ stack). `docs/why-silas.md` rewritten in the same voice — you build the same
136
+ things with Silas you'd build with any modern agent framework; the
137
+ guarantees are where it goes further — and DEPLOY.md dropped its stale "eve
138
+ without the bill" subtitle.
139
+
140
+ ### Fixed
141
+
142
+ - The generated `config/initializers/ruby_llm.rb` no longer guards its whole
143
+ configure block behind `ANTHROPIC_API_KEY` — keyless boots skipped the
144
+ `use_new_acts_as` opt-in, so every demo-mode boot printed RubyLLM's legacy
145
+ acts_as deprecation warning. A nil key assignment is inert; the block now
146
+ always runs.
147
+
3
148
  ## 0.5.0 (2026-07-26)
4
149
 
5
150
  Two new loop primitives (replay-safe compaction, ask_question), a whole-channel
data/DEPLOY.md ADDED
@@ -0,0 +1,111 @@
1
+ # Deploying Silas
2
+
3
+ A Silas app is an ordinary Rails app, so it deploys the way your Rails apps
4
+ already deploy — a single cheap VPS with Kamal works fine; there is no second
5
+ runtime or control plane to stand up. The one thing you MUST get right: the
6
+ **Solid Queue worker has to run**, because the durability contract (a turn
7
+ surviving a crash / deploy) depends on it. A web-only deploy will accept turns
8
+ and never run them.
9
+
10
+ > Status: this is the reference configuration. It is correct by construction
11
+ > (worker wired, rescuer active, secrets injected) but has not yet been run
12
+ > against a live VPS end-to-end — that's the final launch step.
13
+
14
+ ## 1. The worker must run
15
+
16
+ > **Queue adapter: use Solid Queue.** Silas's durability and exactly-once
17
+ > guarantees require a durable, serializing, DB-backed adapter. **Do not run on
18
+ > the in-process `:async` adapter** (Rails' development default) — it runs
19
+ > continuation retries on a thread pool concurrently with the original job,
20
+ > which double-executes steps and breaks exactly-once. Silas warns at boot if it
21
+ > detects the Async adapter. For scripts and single-process demos, the
22
+ > synchronous `:inline` adapter is safe (but has no durability).
23
+
24
+ `rails g silas:install` already appends the durability rescuer to
25
+ `config/recurring.yml`:
26
+
27
+ ```yaml
28
+ production:
29
+ silas_dead_job_rescuer: # retries jobs failed by dead-worker reaping/pruning
30
+ class: Silas::DeadJobRescuerJob
31
+ schedule: every 30 seconds
32
+ ```
33
+
34
+ Pick one way to run Solid Queue:
35
+
36
+ - **Simple (one container):** run the worker inside Puma —
37
+ set `SOLID_QUEUE_IN_PUMA=true` (Rails 8 default plugin). Fine for a demo box.
38
+ - **Recommended (durable):** a dedicated `bin/jobs` process, so a `kill -9` of the
39
+ worker is isolated from the web tier and recovery is observable.
40
+
41
+ ## 2. Kamal config (`config/deploy.yml`)
42
+
43
+ A standard `rails new` ships a Dockerfile and `config/deploy.yml`. Add a **job
44
+ role** so the worker is its own container:
45
+
46
+ ```yaml
47
+ service: my-agent
48
+ image: you/my-agent
49
+ servers:
50
+ web:
51
+ - 1.2.3.4 # your £5/month VPS
52
+ job:
53
+ hosts:
54
+ - 1.2.3.4
55
+ cmd: bin/jobs # the Solid Queue worker (+ dispatcher + scheduler)
56
+
57
+ registry:
58
+ username: you
59
+ password:
60
+ - KAMAL_REGISTRY_PASSWORD
61
+
62
+ env:
63
+ secret:
64
+ - RAILS_MASTER_KEY
65
+ - ANTHROPIC_API_KEY # the :ruby_llm engine authenticates with this
66
+ clear:
67
+ SOLID_QUEUE_IN_PUMA: false # the job role runs the worker instead
68
+
69
+ # Persist SQLite + Solid Queue on a volume if you're using SQLite (or point at Postgres).
70
+ volumes:
71
+ - "my_agent_storage:/rails/storage"
72
+ ```
73
+
74
+ Secrets (Slack signing secret / bot token, model keys) go in `.kamal/secrets`
75
+ (pulled from your env or a vault), never in the image.
76
+
77
+ ## 3. Deploy
78
+
79
+ ```sh
80
+ kamal setup # first time
81
+ kamal deploy # subsequent
82
+ ```
83
+
84
+ Mid-deploy safety is built in: a turn interrupted by the SIGTERM of a rolling
85
+ deploy re-enqueues and resumes (chaos-gated). Recovery latency after a hard
86
+ crash ≈ `SolidQueue.process_alive_threshold` + the rescuer's 30s cadence.
87
+
88
+ ## 3½. Template apps deploy stock
89
+
90
+ An app generated from `templates/*.rb` needs no deploy surgery: Rails 8.1's
91
+ generated `config/deploy.yml` already runs Solid Queue inside Puma for the
92
+ single-server SQLite shape (`SOLID_QUEUE_IN_PUMA: true` — that satisfies "the
93
+ worker must run"), the template adds `ANTHROPIC_API_KEY` to Kamal's secret
94
+ list and `.kamal/secrets`, and `config/recurring.yml` already carries the
95
+ rescuer. Set the two secrets in your shell, point `deploy.yml` at your server
96
+ and registry, `kamal setup`. Scale beyond one box by moving to the dedicated
97
+ worker role in §1 and a real Postgres in §2.
98
+
99
+ ## 4. Cost
100
+
101
+ The whole thing runs on one VPS + your model spend. The engine authenticates
102
+ with an API key for whatever provider you configure through RubyLLM. Your data
103
+ and the ledger never leave your own Postgres, and there is no Silas platform
104
+ bill or per-run metering.
105
+
106
+ ## 5. Local development note (macOS + Postgres)
107
+
108
+ Solid Queue forks workers, and on macOS the pg gem needs
109
+ `PGGSSENCMODE=disable OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES` in the
110
+ environment or forked workers crash inside libpq. SQLite development needs
111
+ nothing.