ruby_llm-browser 0.1.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.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +61 -0
  3. data/CODE_OF_CONDUCT.md +11 -0
  4. data/CONTRIBUTING.md +42 -0
  5. data/LICENSE.txt +21 -0
  6. data/NOTICE.md +92 -0
  7. data/README.md +139 -0
  8. data/SECURITY.md +46 -0
  9. data/docs/API.md +177 -0
  10. data/docs/CNAME +1 -0
  11. data/docs/RAILS.md +167 -0
  12. data/docs/README.md +16 -0
  13. data/docs/RELEASE_ACTION_PLAN.md +81 -0
  14. data/docs/RELEASE_AUDIT.md +158 -0
  15. data/docs/RELEASING.md +131 -0
  16. data/docs/SECURITY_MODEL.md +117 -0
  17. data/docs/SPEC_BASELINE.md +107 -0
  18. data/docs/WORK.md +260 -0
  19. data/docs/_advanced/live-backend-qualification.md +74 -0
  20. data/docs/_advanced/rails-integration.md +56 -0
  21. data/docs/_config.yml +109 -0
  22. data/docs/_core_features/inspect-extract.md +91 -0
  23. data/docs/_core_features/offline-act.md +110 -0
  24. data/docs/_core_features/operation-lifecycle.md +63 -0
  25. data/docs/_core_features/source-policy.md +61 -0
  26. data/docs/_data/navigation.yml +12 -0
  27. data/docs/_data/sidebar.yml +8 -0
  28. data/docs/_data/social_links.yml +3 -0
  29. data/docs/_getting_started/configuration.md +68 -0
  30. data/docs/_getting_started/getting-started.md +77 -0
  31. data/docs/_getting_started/overview.md +52 -0
  32. data/docs/_plugins/versioned_docs.rb +46 -0
  33. data/docs/_reference/api-reference.md +29 -0
  34. data/docs/_reference/release-history.md +23 -0
  35. data/docs/_reference/upgrading.md +33 -0
  36. data/docs/bin/build-api.sh +23 -0
  37. data/docs/bin/build.sh +161 -0
  38. data/docs/bin/export-api-markdown.rb +53 -0
  39. data/docs/bin/link_versions.rb +54 -0
  40. data/docs/bin/postprocess_api_seo.rb +129 -0
  41. data/docs/bin/prepare_versions.rb +40 -0
  42. data/docs/bin/serve.sh +12 -0
  43. data/docs/bin/versions.yml +14 -0
  44. data/docs/index.md +82 -0
  45. data/examples/plain_ruby.rb +34 -0
  46. data/examples/rails_adapter.rb +47 -0
  47. data/lib/generators/ruby_llm/browser/install_generator.rb +29 -0
  48. data/lib/generators/ruby_llm/browser/templates/create_ruby_llm_browser_tables.rb.tt +60 -0
  49. data/lib/ruby_llm/browser/approval.rb +128 -0
  50. data/lib/ruby_llm/browser/approval_store.rb +38 -0
  51. data/lib/ruby_llm/browser/backend.rb +99 -0
  52. data/lib/ruby_llm/browser/budget.rb +90 -0
  53. data/lib/ruby_llm/browser/capabilities.rb +76 -0
  54. data/lib/ruby_llm/browser/configuration.rb +74 -0
  55. data/lib/ruby_llm/browser/context.rb +117 -0
  56. data/lib/ruby_llm/browser/doctor.rb +134 -0
  57. data/lib/ruby_llm/browser/engine.rb +326 -0
  58. data/lib/ruby_llm/browser/errors.rb +86 -0
  59. data/lib/ruby_llm/browser/fake_backend.rb +134 -0
  60. data/lib/ruby_llm/browser/immutable.rb +19 -0
  61. data/lib/ruby_llm/browser/in_memory_approval_store.rb +67 -0
  62. data/lib/ruby_llm/browser/in_memory_operation_store.rb +174 -0
  63. data/lib/ruby_llm/browser/operation.rb +218 -0
  64. data/lib/ruby_llm/browser/operation_store.rb +92 -0
  65. data/lib/ruby_llm/browser/policy.rb +298 -0
  66. data/lib/ruby_llm/browser/rails/active_record_approval_store.rb +72 -0
  67. data/lib/ruby_llm/browser/rails/active_record_operation_store.rb +231 -0
  68. data/lib/ruby_llm/browser/rails/actor_record.rb +13 -0
  69. data/lib/ruby_llm/browser/rails/approval_record.rb +49 -0
  70. data/lib/ruby_llm/browser/rails/operation_job.rb +163 -0
  71. data/lib/ruby_llm/browser/rails/operation_record.rb +56 -0
  72. data/lib/ruby_llm/browser/rails/reaper.rb +117 -0
  73. data/lib/ruby_llm/browser/rails/schema.rb +81 -0
  74. data/lib/ruby_llm/browser/rails.rb +79 -0
  75. data/lib/ruby_llm/browser/redactor.rb +159 -0
  76. data/lib/ruby_llm/browser/result.rb +102 -0
  77. data/lib/ruby_llm/browser/stagehand_backend.rb +332 -0
  78. data/lib/ruby_llm/browser/tool.rb +70 -0
  79. data/lib/ruby_llm/browser/tools/act.rb +91 -0
  80. data/lib/ruby_llm/browser/tools/extract.rb +60 -0
  81. data/lib/ruby_llm/browser/tools/inspect.rb +38 -0
  82. data/lib/ruby_llm/browser/url_guard.rb +253 -0
  83. data/lib/ruby_llm/browser/version.rb +9 -0
  84. data/lib/ruby_llm/browser.rb +79 -0
  85. metadata +267 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e6184d0ab1ce52e3a77ed427ff4e083790a7d573a51b5ea07ddffeab034b356e
4
+ data.tar.gz: c08d7fd335590fa99341551ce77ca6a8f8ec61662b3e63903308f56ea9ff05d6
5
+ SHA512:
6
+ metadata.gz: 3f671bfe311dea830346ce657373b81f514c227eac636576f76fb962a4690a0e688fecb476d36ef83a288d02cdc28e0bd2f8fece092b00cd8925e57cae6b3509
7
+ data.tar.gz: 0f11137930c35dc67cf7642db5152864fa5b101bb957bba24edeab974279c66f33e728766bb5005c29bb331b37989f09edbbaee70f461acf419ea31db9b4a209
data/CHANGELOG.md ADDED
@@ -0,0 +1,61 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project aims to
5
+ follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.0] - 2026-10-18
8
+
9
+ ### Fixed
10
+
11
+ - Atomic start claims, actor quota locking, payload-bound idempotency, exact fence/state
12
+ checks, cancellation quota retention, and cleanup after post-start persistence errors.
13
+ - Reaper fencing before cleanup and recovery of known sessions without a correlation API.
14
+ - Host/operation output ceilings, UTF-8/escaped-JSON bounds, retained byte accounting,
15
+ malformed schema handling, and provider extraction schema validation.
16
+ - Defensive policy/approval/schema copies, safer progress/errors/provenance, and suppressed
17
+ Active Job argument logging.
18
+ - Real-clock durable timestamp round-trips and effective process budget/result defaults.
19
+ - Stagehand response validation, explicit disabled self-healing/CAPTCHA solving/recording,
20
+ environment-drift rejection, remaining-time timeouts, and no guessed final URL.
21
+ - Rails install generator namespace, README API errors, shipped documentation/examples,
22
+ and RubyLLM 2.0/2.1 public-API compatibility tests.
23
+
24
+ ### Security
25
+
26
+ - Require patched CGI, DNS resolver, URI and zlib versions instead of accepting old
27
+ vulnerable default gems on a clean Ruby installation.
28
+ - The first-release scope is offline read-only. Stagehand remains unsupported until
29
+ provider egress and confirmed cleanup are qualified; no live calls were made. Full live
30
+ Act is deferred to a later version, while offline Act simulation remains available.
31
+ - See `docs/RELEASE_AUDIT.md` for findings, mitigations and residual limits.
32
+
33
+ The first implementation milestone. Everything below is proven offline against the fake
34
+ backend and an in-memory SQLite database; **no live browser provider call has been run**,
35
+ and mocked tests never establish remote egress isolation, TTL or billing limits.
36
+
37
+ ### Added
38
+
39
+ - Plain-Ruby Gem core (`require "ruby_llm/browser"`) with a manual require chain, no Rails
40
+ and no provider credentials loaded on require.
41
+ - Read-only `RubyLLM::Browser::Inspect` and `Extract` tools; `Extract` takes a
42
+ host-pinned schema instead of a model-supplied one.
43
+ - Simulation-only `RubyLLM::Browser::Act` (never registered by default) with a read-only RubyLLM
44
+ approval resolver and an atomic, digest-bound, single-use approval consumed in `execute`.
45
+ - Source policy: URL normalization with userinfo/scheme/port and ambiguous-IP rejection,
46
+ allowlist matching, and fail-closed egress qualification that returns
47
+ `UNSUPPORTED_SECURITY_CAPABILITY` before any session when a backend cannot prove
48
+ browser-side egress isolation.
49
+ - Durable operation lifecycle (`reserved → starting → active → closing → closed`, plus
50
+ `unknown` / `cleanup_pending` / `cancelled`), fencing and per-actor concurrency budgets,
51
+ with in-memory and ActiveRecord stores.
52
+ - Official Stagehand Ruby SDK adapter (loaded lazily) that passes every client argument
53
+ explicitly, sets `max_retries: 0` on effectful calls, drops `cdp_url`, and reports honest
54
+ capabilities.
55
+ - Bounded, sanitized `Result` with provenance, plus a redactor that strips secrets,
56
+ cookies and DevTools URLs.
57
+ - Read-only preflight `Doctor`.
58
+ - Optional Rails 8 adapter (`require "ruby_llm/browser/rails"`): ActiveRecord operation and
59
+ approval stores, a scoped Active Job with fence/cancel handling, a cleanup reaper, a
60
+ canonical schema module and an install generator with migration.
61
+ - Offline examples, documentation and an FR evidence ledger (`docs/WORK.md`).
@@ -0,0 +1,11 @@
1
+ # Contributor Code of Conduct
2
+
3
+ This project follows the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
4
+
5
+ Be respectful, assume good faith, and keep discussions focused on the work. Harassment,
6
+ discrimination, doxxing and disclosure of other people's data or credentials are not
7
+ acceptable. Maintainers may edit, lock or remove contributions and comments that violate
8
+ this code, and may ban contributors for repeated or severe violations.
9
+
10
+ Report concerns privately to the maintainer listed in the gemspec. Reports are handled
11
+ confidentially.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,42 @@
1
+ # Contributing to ruby_llm-browser
2
+
3
+ Thanks for helping. This is an unofficial community extension for
4
+ [RubyLLM](https://rubyllm.com), not an official RubyLLM project.
5
+
6
+ ## Ground rules
7
+
8
+ 1. **Fail closed.** Anything that touches URLs, egress, sessions, approvals, budgets or
9
+ secrets must default to deny and must be provable by a test.
10
+ 2. **No new unsafe surface.** No arbitrary JS/shell, no credential or cookie scraping,
11
+ no CAPTCHA/age/access bypass, no purchases, posting or account changes, and no
12
+ second browser engine.
13
+ 3. **Offline by default.** CI must never make a paid provider call. Use the fake backend
14
+ or an injected client. Live tests are opt-in and owner-authorized.
15
+ 4. **Say what is not proven.** Distinguish PASS from NOT RUN. Mocked egress proves the
16
+ Gem's logic, never a provider's network isolation.
17
+
18
+ ## Development
19
+
20
+ ```bash
21
+ bin/setup # bundle install
22
+ bundle exec rake # specs + rubocop
23
+ bundle exec rspec # specs only
24
+ bundle exec bundle-audit check --update
25
+ bundle exec rubocop -A # autocorrect style
26
+ ```
27
+
28
+ ## Changing behaviour
29
+
30
+ - Add a failing spec first, including the negative/denial case.
31
+ - Update `CHANGELOG.md` under `Unreleased`.
32
+ - Update `docs/SECURITY_MODEL.md` or `README.md` if a boundary or public API moved.
33
+ - Keep runnable examples and the package's local links green in
34
+ `spec/integration/documentation_spec.rb`.
35
+ - Run both alternate bundles in `gemfiles/` when changing Tool or Rails integration.
36
+ - Keep the Gem core loadable without Rails: `ruby -e 'require "ruby_llm/browser"'` must
37
+ not load `rails`, `active_job` or open any browser session.
38
+
39
+ ## Pull requests
40
+
41
+ Keep commits small and focused. Explain the security impact of the change, list the
42
+ tests you ran, and note anything you could not verify. Maintainers control releases.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andy Wang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/NOTICE.md ADDED
@@ -0,0 +1,92 @@
1
+ # Third-party notices
2
+
3
+ `ruby_llm-browser` is MIT licensed (see `LICENSE.txt`). It declares separate runtime
4
+ dependencies; no upstream source code is vendored in this Gem. The inventory below covers
5
+ the 26 runtime dependency gems resolved in the local lockfile on 2026-10-18 and matches
6
+ their exact installed gemspec license metadata. Stagehand has no license metadata in its
7
+ published gem, so its MIT license is tied to the pinned upstream source tag below.
8
+
9
+ This is a point-in-time technical inventory, not legal advice or trademark, affiliation,
10
+ account-ownership, or publication clearance. Version ranges can resolve different
11
+ versions later; recheck the exact artifact before distribution.
12
+
13
+ ## RubyLLM (`ruby_llm`)
14
+
15
+ - License: MIT
16
+ - Source: <https://github.com/crmne/ruby_llm>
17
+ - Used for: the `RubyLLM::Tool` protocol and the chat tool-execution loop.
18
+
19
+ ## Stagehand Ruby SDK (`stagehand`)
20
+
21
+ - License: MIT, as declared in the repository `LICENSE` file, "Copyright 2026 stagehand"
22
+ (`https://github.com/browserbase/stagehand-ruby/blob/v3.21.0/LICENSE`).
23
+ - Source tag: `v3.21.0` (commit `b389c6e3ca7e4a5f9d46511082a6466d37163e17`).
24
+ - Used for: the reference browser REST client behind
25
+ `RubyLLM::Browser::StagehandBackend`.
26
+ - Note: the published `stagehand` gem ships without license metadata and without the
27
+ `LICENSE` file, so this notice cites the repository license at the pinned tag. Pin
28
+ the exact version and checksum in your `Gemfile.lock`.
29
+
30
+ ## Point-in-time runtime inventory (2026-10-18)
31
+
32
+ Resolved locally on Ruby 3.3.5 with RubyLLM 2.1.1. Every dependency row was matched to
33
+ the local lockfile version and installed gemspec license metadata. RubyGems version links
34
+ identify the exact distributed releases; Stagehand's pinned-source exception is noted
35
+ above. Rails/Active Record/Active Job and sqlite3 are host/test dependencies and are not
36
+ part of this Gem's runtime dependency graph.
37
+
38
+ | Dependency | Version | Declared license(s) | RubyGems version record |
39
+ | --- | --- | --- | --- |
40
+ | [`ruby_llm`](https://rubygems.org/gems/ruby_llm/versions/2.1.1) | 2.1.1 | MIT | [RubyGems version](https://rubygems.org/gems/ruby_llm/versions/2.1.1) |
41
+ | [`stagehand`](https://rubygems.org/gems/stagehand/versions/3.21.0) | 3.21.0 | No gem metadata; pinned source MIT text below | [RubyGems version](https://rubygems.org/gems/stagehand/versions/3.21.0) |
42
+ | [`json_schemer`](https://rubygems.org/gems/json_schemer/versions/2.5.0) | 2.5.0 | MIT | [RubyGems version](https://rubygems.org/gems/json_schemer/versions/2.5.0) |
43
+ | [`hana`](https://rubygems.org/gems/hana/versions/1.3.7) | 1.3.7 | MIT | [RubyGems version](https://rubygems.org/gems/hana/versions/1.3.7) |
44
+ | [`simpleidn`](https://rubygems.org/gems/simpleidn/versions/0.3.0) | 0.3.0 | MIT | [RubyGems version](https://rubygems.org/gems/simpleidn/versions/0.3.0) |
45
+ | [`regexp_parser`](https://rubygems.org/gems/regexp_parser/versions/2.13.1) | 2.13.1 | MIT | [RubyGems version](https://rubygems.org/gems/regexp_parser/versions/2.13.1) |
46
+ | [`schematist`](https://rubygems.org/gems/schematist/versions/1.1.0) | 1.1.0 | MIT | [RubyGems version](https://rubygems.org/gems/schematist/versions/1.1.0) |
47
+ | [`zeitwerk`](https://rubygems.org/gems/zeitwerk/versions/2.8.3) | 2.8.3 | MIT | [RubyGems version](https://rubygems.org/gems/zeitwerk/versions/2.8.3) |
48
+ | [`connection_pool`](https://rubygems.org/gems/connection_pool/versions/3.0.2) | 3.0.2 | MIT | [RubyGems version](https://rubygems.org/gems/connection_pool/versions/3.0.2) |
49
+ | [`faraday`](https://rubygems.org/gems/faraday/versions/2.15.0) | 2.15.0 | MIT | [RubyGems version](https://rubygems.org/gems/faraday/versions/2.15.0) |
50
+ | [`faraday-net_http`](https://rubygems.org/gems/faraday-net_http/versions/3.5.0) | 3.5.0 | MIT | [RubyGems version](https://rubygems.org/gems/faraday-net_http/versions/3.5.0) |
51
+ | [`faraday-multipart`](https://rubygems.org/gems/faraday-multipart/versions/1.2.0) | 1.2.0 | MIT | [RubyGems version](https://rubygems.org/gems/faraday-multipart/versions/1.2.0) |
52
+ | [`faraday-retry`](https://rubygems.org/gems/faraday-retry/versions/2.4.0) | 2.4.0 | MIT | [RubyGems version](https://rubygems.org/gems/faraday-retry/versions/2.4.0) |
53
+ | [`multipart-post`](https://rubygems.org/gems/multipart-post/versions/2.4.1) | 2.4.1 | MIT | [RubyGems version](https://rubygems.org/gems/multipart-post/versions/2.4.1) |
54
+ | [`marcel`](https://rubygems.org/gems/marcel/versions/2.1.0) | 2.1.0 | MIT / Apache-2.0 | [RubyGems version](https://rubygems.org/gems/marcel/versions/2.1.0) |
55
+ | [`base64`](https://rubygems.org/gems/base64/versions/0.3.0) | 0.3.0 | Ruby / BSD-2-Clause | [RubyGems version](https://rubygems.org/gems/base64/versions/0.3.0) |
56
+ | [`bigdecimal`](https://rubygems.org/gems/bigdecimal/versions/4.1.3) | 4.1.3 | Ruby / BSD-2-Clause | [RubyGems version](https://rubygems.org/gems/bigdecimal/versions/4.1.3) |
57
+ | [`cgi`](https://rubygems.org/gems/cgi/versions/0.5.2) | 0.5.2 | Ruby / BSD-2-Clause | [RubyGems version](https://rubygems.org/gems/cgi/versions/0.5.2) |
58
+ | [`json`](https://rubygems.org/gems/json/versions/3.1.0) | 3.1.0 | Ruby | [RubyGems version](https://rubygems.org/gems/json/versions/3.1.0) |
59
+ | [`logger`](https://rubygems.org/gems/logger/versions/1.7.0) | 1.7.0 | Ruby / BSD-2-Clause | [RubyGems version](https://rubygems.org/gems/logger/versions/1.7.0) |
60
+ | [`net-http`](https://rubygems.org/gems/net-http/versions/0.10.0) | 0.10.0 | Ruby / BSD-2-Clause | [RubyGems version](https://rubygems.org/gems/net-http/versions/0.10.0) |
61
+ | [`net-protocol`](https://rubygems.org/gems/net-protocol/versions/0.4.0) | 0.4.0 | Ruby / BSD-2-Clause | [RubyGems version](https://rubygems.org/gems/net-protocol/versions/0.4.0) |
62
+ | [`timeout`](https://rubygems.org/gems/timeout/versions/0.6.1) | 0.6.1 | Ruby / BSD-2-Clause | [RubyGems version](https://rubygems.org/gems/timeout/versions/0.6.1) |
63
+ | [`uri`](https://rubygems.org/gems/uri/versions/1.1.2) | 1.1.2 | Ruby / BSD-2-Clause | [RubyGems version](https://rubygems.org/gems/uri/versions/1.1.2) |
64
+ | [`resolv`](https://rubygems.org/gems/resolv/versions/0.8.1) | 0.8.1 | Ruby / BSD-2-Clause | [RubyGems version](https://rubygems.org/gems/resolv/versions/0.8.1) |
65
+ | [`zlib`](https://rubygems.org/gems/zlib/versions/3.2.4) | 3.2.4 | Ruby / BSD-2-Clause | [RubyGems version](https://rubygems.org/gems/zlib/versions/3.2.4) |
66
+
67
+ Rails/Active Record/Active Job are optional host dependencies (MIT); sqlite3 is a test/
68
+ host adapter (BSD-3-Clause). Development-only dependencies are not gem runtime requirements.
69
+
70
+ ## Stagehand pinned source license text
71
+
72
+ Source: <https://github.com/browserbase/stagehand-ruby/blob/v3.21.0/LICENSE>, fetched
73
+ 2026-10-18. Included because the published Stagehand 3.21.0 gem omits the license file.
74
+
75
+ Copyright 2026 stagehand
76
+
77
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this
78
+ software and associated documentation files (the "Software"), to deal in the Software
79
+ without restriction, including without limitation the rights to use, copy, modify,
80
+ merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
81
+ permit persons to whom the Software is furnished to do so, subject to the following
82
+ conditions:
83
+
84
+ The above copyright notice and this permission notice shall be included in all copies
85
+ or substantial portions of the Software.
86
+
87
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
88
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
89
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
90
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
91
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
92
+ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # ruby_llm-browser
2
+
3
+ An **unofficial community extension** for [RubyLLM](https://rubyllm.com), with an
4
+ experimental adapter for the official [Stagehand Ruby SDK](https://github.com/browserbase/stagehand-ruby).
5
+ It provides read-only `Inspect` and schema-pinned `Extract` tools, operation bookkeeping,
6
+ and an optional Rails 8 adapter. It does not implement a browser engine.
7
+
8
+ The first release supports the offline `FakeBackend` only. `StagehandBackend` is present
9
+ for future qualification, but it refuses live operations until browser-side egress and
10
+ provider cleanup are qualified.
11
+
12
+ > **Version 0.1.0 is the offline-only first release.** The built-in Stagehand backend
13
+ > is unsupported, and the full live Act approval protocol is deferred. No live provider
14
+ > calls were made. See the [release audit](docs/RELEASE_AUDIT.md). This project is not
15
+ > maintained or endorsed by RubyLLM or Browserbase.
16
+
17
+ ## Install
18
+
19
+ Add the gem to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem "ruby_llm-browser", "~> 0.1.0"
23
+ ```
24
+
25
+ The core requires Ruby 3.2+, RubyLLM `>= 2.0, < 3`, `json_schemer ~> 2.5`, and
26
+ `stagehand ~> 3.21.0`, plus patched DNS/URL/CGI/compression gems (see the
27
+ [dependency reference](docs/API.md)). Requiring `ruby_llm/browser` loads neither Rails nor the Stagehand
28
+ client, discovers no provider credentials, and opens no browser session.
29
+
30
+ ## Try it offline
31
+
32
+ This complete example makes no network request and requires no API key:
33
+
34
+ ```ruby
35
+ # smoke-test: offline
36
+ require "ruby_llm/browser"
37
+
38
+ # Fake DNS is only for this offline demonstration. Production uses real DNS.
39
+ class OfflineResolver
40
+ def getaddresses(_host) = [ "93.184.216.34" ]
41
+ end
42
+
43
+ context = RubyLLM::Browser::Context.new(
44
+ actor_id: "demo-actor", run_id: "demo-run",
45
+ backend: RubyLLM::Browser::FakeBackend.new,
46
+ source_policy: RubyLLM::Browser::Policy.new(
47
+ allowed_hosts: [ "docs.example.com" ], resolver: OfflineResolver.new
48
+ ),
49
+ operation_store: RubyLLM::Browser::InMemoryOperationStore.new
50
+ )
51
+ schema = {
52
+ "type" => "object",
53
+ "properties" => { "title" => { "type" => "string" } },
54
+ "required" => [ "title" ], "additionalProperties" => false
55
+ }
56
+
57
+ inspect_tool = RubyLLM::Browser::Inspect.new(context: context)
58
+ extract_tool = RubyLLM::Browser::Extract.new(context: context, schema: schema)
59
+ puts inspect_tool.execute(url: "https://docs.example.com/")[:status] # => ok
60
+ puts extract_tool.execute(url: "https://docs.example.com/")[:data] # => {"title"=>"Example"}
61
+ ```
62
+
63
+ Register these instances on your configured RubyLLM chat with
64
+ `chat.with_tools(inspect_tool, extract_tool)`. Never register `Act` by default.
65
+ The host supplies authenticated **string** actor/run IDs, authorization, consent,
66
+ the pinned extraction schema, budgets, and the backend.
67
+
68
+ The tools return a **Hash**, not a `Result` object. Direct `Engine` calls return a
69
+ `Result`. Check `status` and `error`; page data is untrusted evidence, not verified truth.
70
+
71
+ ## Safety boundaries
72
+
73
+ - URL policy checks hosts, schemes, ports, and public DNS/IP addresses. It cannot
74
+ sandbox a remote browser or control page-triggered requests.
75
+ - No built-in live backend is supported in this release. `StagehandBackend` always
76
+ reports egress as unverified and refuses live operations. A host assertion cannot
77
+ enable it.
78
+ - The model cannot increase the host's output limit. Results are redacted, schema-checked
79
+ for extraction, and bounded by the policy and remaining operation bytes.
80
+ - A durable operation is claimed before start. Duplicate jobs cannot start it again.
81
+ SDK retries are disabled; uncertain outcomes are never automatically replayed.
82
+ - Cancellation fences workers but cannot undo an in-flight request. Dispatched operations
83
+ retain quota until cleanup is confirmed. Run a reaper independently of job workers.
84
+ - `max_cost_usd` is advisory metadata, not an enforced billing cap. Provider timeouts and
85
+ cleanup are also not a promise of zero future charges.
86
+ - `Act` is an offline simulation only. The full same-session live approval protocol is
87
+ deferred to a later version. Candidate-list hashes and selector keyword filters are
88
+ not proof of target identity or action safety.
89
+
90
+ ## Rails integration
91
+
92
+ Add Rails 8's `activerecord` and `activejob` to your host application, then opt in:
93
+
94
+ ```ruby
95
+ require "ruby_llm/browser/rails"
96
+ ```
97
+
98
+ ```bash
99
+ bin/rails generate ruby_llm:browser:install
100
+ bin/rails db:migrate
101
+ ```
102
+
103
+ The adapter provides durable operation/approval stores, actor-scoped jobs, cancellation,
104
+ and a reaper. Follow the [Rails guide](docs/RAILS.md) for the context factory, matching
105
+ budgets, status access, scheduling, and upgrading an earlier development database.
106
+ Only SQLite concurrency is tested locally; PostgreSQL requires its own qualification.
107
+
108
+ ## Documentation
109
+
110
+ - [Documentation website](https://rubyllm-browser.wangyaodi.com) (GitHub Pages)
111
+ - [Documentation index](docs/README.md)
112
+ - [API, configuration, and result reference](docs/API.md)
113
+ - [Rails jobs and recovery](docs/RAILS.md)
114
+ - [Security model and live qualification](docs/SECURITY_MODEL.md)
115
+ - [Compatibility and release checklist](docs/RELEASING.md)
116
+ - [Audit findings and unresolved blockers](docs/RELEASE_AUDIT.md)
117
+ - [Requirement evidence](docs/WORK.md)
118
+ - [Offline scripts](examples/)
119
+
120
+ These documents and examples are included in the built gem.
121
+
122
+ ## Development
123
+
124
+ ```bash
125
+ bin/setup
126
+ bundle exec rake
127
+ bundle exec bundle-audit check --update
128
+ bundle exec ruby examples/plain_ruby.rb
129
+ bundle exec ruby examples/rails_adapter.rb
130
+ bundle exec rake build
131
+ bundle exec ruby bin/verify-package --rails --audit
132
+ ```
133
+
134
+ Tests block unmocked network requests. No paid live call or RubyGems publication is part
135
+ of the default workflow. See [contributing](CONTRIBUTING.md).
136
+
137
+ ## License
138
+
139
+ MIT. See [LICENSE.txt](LICENSE.txt) and [third-party notices](NOTICE.md).
data/SECURITY.md ADDED
@@ -0,0 +1,46 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Report security issues privately via GitHub Security Advisories on
6
+ <https://github.com/yorzi/ruby_llm-browser> ("Report a vulnerability"), or by email
7
+ to the maintainer listed in the gemspec. Please do not open a public issue for a
8
+ suspected vulnerability.
9
+
10
+ Include: affected version, a minimal reproduction, the impact you observed, and
11
+ whether secrets, cross-actor data, browser sessions or provider billing are involved.
12
+ We aim to acknowledge within a few days and to publish a fix and advisory once a
13
+ patch is available. Never include real credentials, cookies, `cdp_url` values or
14
+ private page payloads in a report; use synthetic fixtures.
15
+
16
+ ## Security model in one page
17
+
18
+ - **Untrusted everything external.** Model output and page text are untrusted data.
19
+ They can never change tool policy, grant permissions or authorize actions.
20
+ - **Default deny.** Only hosts you allow explicitly, over `http`/`https`, on approved
21
+ ports, are reachable. Loopback, private, link-local, multicast and cloud-metadata
22
+ addresses are rejected, including IPv6 and IPv4-mapped forms.
23
+ - **Fail closed on egress.** Ruby-side URL checks cannot prove what a *remote* browser
24
+ does. `Policy#egress_qualified?` must be satisfied by the backend before a session
25
+ starts; otherwise the Gem raises `UNSUPPORTED_SECURITY_CAPABILITY` and never
26
+ contacts a provider.
27
+ - **Effectful retries are off.** Session creation, navigation, `act` and `end_` are
28
+ issued with `max_retries: 0`. Unknown outcomes stay `unknown` /
29
+ `cleanup_pending` and are never silently retried into a second session or a
30
+ duplicated action.
31
+ - **Reduce secret exposure.** Credential-like fields, known secret literals and
32
+ `cdp_url` endpoints are redacted; this is not a general PII/page-secret detector.
33
+ Avoid authenticated/private browsing and raw payload logging. SDK client arguments are passed
34
+ explicitly; the Gem never reads SDK environment defaults for credentials or base
35
+ URLs, and refuses to start when `STAGEHAND_CUSTOM_HEADERS` is set unless the host
36
+ approved it.
37
+ - **Live Act is disabled.** `RubyLLM::Browser::Act` is never registered by default,
38
+ and the Engine refuses non-fake action backends. Offline single-use approval tests
39
+ do not establish the missing same-session/target authorization protocol. Purchases,
40
+ posting, account changes and access-control bypass remain unsupported.
41
+
42
+ ## Scope of guarantees
43
+
44
+ Mocked or fake backends demonstrate the Gem's API logic only. They do not prove
45
+ provider-side network egress isolation, provider TTL enforcement or billing limits.
46
+ See `docs/SECURITY_MODEL.md` and `docs/SPEC_BASELINE.md` for the full, honest status.
data/docs/API.md ADDED
@@ -0,0 +1,177 @@
1
+ ---
2
+ layout: default
3
+ title: API and Configuration
4
+ permalink: /api-and-configuration/
5
+ description: Configure trusted host objects and interpret bounded untrusted results.
6
+ ---
7
+
8
+ # API and configuration
9
+
10
+ Configure trusted host objects and interpret bounded, untrusted results.
11
+
12
+ After reading this guide, you will know:
13
+
14
+ - How to register read-only tools.
15
+ - Which inputs belong to the host and which belong to the model.
16
+ - How to configure budgets, schemas, backends, and error handling.
17
+
18
+ ## Creating a context
19
+
20
+ Use the complete offline example in [Getting Started]({% link _getting_started/getting-started.md %}). `Context.new` requires:
21
+
22
+ | Keyword | Type and meaning |
23
+ | --- | --- |
24
+ | `actor_id`, `run_id` | Nonempty strings from authenticated host records. Convert numeric database IDs with `to_s`. |
25
+ | `backend` | A `Backend` instance. Use `FakeBackend` for offline tests. |
26
+ | `source_policy` | A host-created `Policy`. |
27
+ | `operation_store` | An `OperationStore`; durable storage is required across restarts or workers. |
28
+ | `approval_store` | Optional `ApprovalStore`, needed only for experimental Act. |
29
+ | `budget` | Optional `Budget`; defaults listed below. |
30
+ | `clock` | Optional callable returning a UTC `Time`. |
31
+ | `logger` | Optional host logger. Do not enable raw provider or tool payload logging. |
32
+ | `consent` | Optional host consent record. The gem records its presence; it does not collect or authenticate consent. |
33
+ | `metadata` | Optional host metadata. Never supply credentials here. |
34
+
35
+ The host must reauthorize actor/run access when building a context, including background
36
+ jobs. Passing IDs alone is not authentication. The model must never choose a backend,
37
+ credential, schema, source allowlist, consent record, or budget.
38
+
39
+ ## Registering tools
40
+
41
+ ```ruby
42
+ inspect_tool = RubyLLM::Browser::Inspect.new(context: context)
43
+ extract_tool = RubyLLM::Browser::Extract.new(context: context, schema: schema)
44
+ chat.with_tools(inspect_tool, extract_tool)
45
+ ```
46
+
47
+ Both tools expose `url`, optional `instruction`, and optional positive integer
48
+ `max_bytes`. `max_bytes` can only tighten the policy and operation ceilings.
49
+ `Extract` takes its schema in the constructor, not in model arguments.
50
+
51
+ `Inspect` returns **candidate interactions**, not a screenshot or arbitrary page dump.
52
+ Neither read-only tool dispatches candidates automatically. Pages may still run scripts
53
+ and initiate requests; read-only does not mean browsing has no external effects.
54
+
55
+ `Tool#execute` and RubyLLM `Tool#call` return a Hash. If you need a `Result` object, use:
56
+
57
+ ```ruby
58
+ engine = RubyLLM::Browser::Engine.new(context: context)
59
+ result = engine.inspect(url: url)
60
+ result = engine.extract(url: url, schema: schema, instruction: "Find the title")
61
+ ```
62
+
63
+ The optional `operation:` Engine keyword accepts a host-reserved operation with the
64
+ same actor/run and budget. Starting it is an atomic reserved-to-starting claim. It is
65
+ not a resume or retry API for active, unknown, or closed operations.
66
+
67
+ ## Source policy
68
+
69
+ `Policy.new(allowed_hosts: [...])` accepts exact hosts and `*.example.com` subdomains.
70
+ A wildcard does not allow the bare domain. Defaults are HTTP/HTTPS and ports 80/443.
71
+ Keep `verify_dns: true`; `resolver:` is injectable for offline fixtures.
72
+
73
+ Other options are `allowed_schemes`, `allowed_ports`, `max_result_bytes` (65,536),
74
+ `max_schema_depth` (8), `max_schema_properties` (64 root properties), and
75
+ `max_schema_bytes` (16,384). Browser-side enforcement must also cover explicitly
76
+ approved asset origins, redirects, frames, popups, WebSockets, and subresources.
77
+
78
+ ## Extraction schemas
79
+
80
+ Schemas must be bounded object JSON Schemas, passed as a Hash, JSON object string, or
81
+ an object/class supporting `to_json_schema`. Supported keywords are `type`,
82
+ `description`, `properties`, `required`, `items`, `enum`, `additionalProperties`,
83
+ `title`, `minimum`, `maximum`, `minLength`, `maxLength`, `minItems`, `maxItems`,
84
+ `format`, and `default`. Remote references and arbitrary schema extensions are refused.
85
+
86
+ Use `required` and `additionalProperties: false` when an exact shape matters. Provider
87
+ data must satisfy the schema before redaction or truncation. Invalid data returns
88
+ `PROVIDER_ERROR`; it is not returned as a successful extraction. A truncated preview
89
+ or redacted field need not retain the original schema's type. Always check the status.
90
+
91
+ ## Results and errors
92
+
93
+ `Result#to_h` contains symbol keys: `status`, `operation_id`, `final_url`,
94
+ `captured_at` (UTC ISO 8601), `locator`, `digest`, `truncated`, `data`, and optional
95
+ `error: { code:, message: }`. Locator/data objects use string keys. JSON encodes all
96
+ keys as strings. Credential-like query values are redacted in URL provenance.
97
+
98
+ Statuses are `ok`, `truncated`, `error`, `unknown`, and `cleanup_pending`.
99
+ `Result#ok?` accepts `ok` and `truncated`; it makes no factual verification claim.
100
+ `digest` is SHA-256 of canonical JSON of the returned, sanitized data. Very small byte
101
+ limits can produce an empty string or scalar preview; `truncated` is authoritative.
102
+
103
+ Engine preflight denials raise typed errors. Tools convert them into error Hashes.
104
+ After a session starts, the Engine attempts cleanup and returns a failure result.
105
+ Cleanup failure can supersede the original result with `cleanup_pending`; inspect the
106
+ durable operation's `error_code` and arrange host review for unknown effects.
107
+
108
+ Safe error codes: `INVALID_INPUT`, `NOT_AUTHORIZED`, `UNSUPPORTED_SECURITY_CAPABILITY`,
109
+ `BUDGET_EXHAUSTED`, `SESSION_UNKNOWN`, `CLEANUP_PENDING`, `APPROVAL_REQUIRED`,
110
+ `APPROVAL_STALE`, and `PROVIDER_ERROR`.
111
+
112
+ ## Budgets
113
+
114
+ `Budget.new` defaults to `max_seconds: 60`, `max_steps: 12`, `max_bytes: 262_144`,
115
+ `max_concurrent: 1`, and `max_cost_usd: nil`.
116
+
117
+ `RubyLLM::Browser.configure` sets process defaults for `default_budget`,
118
+ `max_result_bytes`, `clock`, and `logger`. New contexts/policies use those defaults;
119
+ existing objects do not change. `configuration.backend` is only a reserved host slot:
120
+ contexts always require an explicitly supplied backend and never use a fallback.
121
+
122
+ - Steps count navigation, observation, extraction, and action calls, not start/cleanup.
123
+ - Bytes count retained sanitized result data, not all bytes the remote browser receives.
124
+ - Wall-clock checks refuse new calls after expiry. Stagehand request timeouts are bounded
125
+ by remaining time. Cleanup has a separate timeout and may outlive this budget.
126
+ - Concurrency is scoped to the actor across runs. Unknown/pending cleanup holds a slot.
127
+ - `max_cost_usd` is **advisory only**. There is no cost ledger or provider hard spending
128
+ cap in this release. Enforce billing limits in the provider/host.
129
+
130
+ ## Stagehand backend status
131
+
132
+ `StagehandBackend` is unsupported for live operations in this release. It always reports
133
+ `egress_isolation: :unverified` and `act_supported?: false`. `Engine` refuses it before
134
+ the first provider request, and direct start, navigation, observation, extraction, and
135
+ Act calls also fail closed. No constructor option or injected client enables live use.
136
+
137
+ The adapter remains in the source for future qualification. Its client pins credentials
138
+ and base URL, disables retries, self-healing, CAPTCHA solving, session recording and
139
+ session logging, and requests a provider timeout. These controls do not prove egress,
140
+ TTL, billing, final-URL reporting, or cleanup. `end_session` sends no request in this
141
+ release and returns `false`; the operation remains `cleanup_pending`.
142
+
143
+ The constructor accepts `server: :remote`, `base_url:`, `timeout: 60.0`,
144
+ `custom_headers_approved: false`, `env: ENV`, and an injected `client:` for internal
145
+ contract tests. A future live implementation must verify all relevant environment
146
+ settings, egress across every browser request path, TTL and closure status, and provider
147
+ billing controls before it can advertise support.
148
+
149
+ ## Dependency security floors
150
+
151
+ Old Ruby installations can supply vulnerable default gems even when a developer's
152
+ lockfile audit passes. Runtime requirements explicitly exclude those versions:
153
+ `cgi >= 0.4.2, < 1`, `resolv >= 0.7.2, < 1`, `uri >= 1.0.4, < 2`, and
154
+ `zlib >= 3.2.3, < 4`. A fresh install may build native extensions for these gems.
155
+ Use a supported, patched Ruby interpreter as well; these floors do not patch Ruby or
156
+ system libraries. Audit the versions actually activated by your application, including
157
+ default gems, not just a development lockfile.
158
+
159
+ ## Experimental Act
160
+
161
+ `Act.new(context:)` declares RubyLLM `requires_approval`. Its arguments are `url`,
162
+ `approval_id`, `method`, `selector`, optional `description`, and optional `arguments`.
163
+ The resolver reads the host store without consuming. Consumption immediately before
164
+ dispatch checks actor/run, exact URL/action digest, expiry, and any supplied
165
+ page/operation/session bindings. Revocation and consumption are atomic in the stores.
166
+
167
+ **Do not use this as a production authorization protocol yet.** Bindings are currently
168
+ optional; a candidate-list digest is not a DOM/target revision, and each Engine action
169
+ opens a fresh session. The same-session host approval flow required by the specification
170
+ is deferred to a later version. The Engine refuses all non-fake Act calls before starting
171
+ a session. Selector/description keyword filters are defense in depth, not semantic proof
172
+ that a click is harmless. The Stagehand adapter does not support Act in this release.
173
+
174
+ ## Next steps
175
+
176
+ - Read [Rails integration]({% link RAILS.md %}) for durable workers and recovery.
177
+ - Read [security boundaries]({% link SECURITY_MODEL.md %}) before qualifying a live backend.
data/docs/CNAME ADDED
@@ -0,0 +1 @@
1
+ rubyllm-browser.wangyaodi.com