fastbrowse 0.1.0__tar.gz

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 (90) hide show
  1. fastbrowse-0.1.0/.env.example +27 -0
  2. fastbrowse-0.1.0/.github/workflows/ci.yml +23 -0
  3. fastbrowse-0.1.0/.github/workflows/release.yml +37 -0
  4. fastbrowse-0.1.0/.gitignore +11 -0
  5. fastbrowse-0.1.0/.vale/styles/Fastbrowse/Filler.yml +31 -0
  6. fastbrowse-0.1.0/.vale.ini +11 -0
  7. fastbrowse-0.1.0/LICENSE +21 -0
  8. fastbrowse-0.1.0/NOTICE +29 -0
  9. fastbrowse-0.1.0/PKG-INFO +257 -0
  10. fastbrowse-0.1.0/README.md +237 -0
  11. fastbrowse-0.1.0/assets/architecture-dark.svg +88 -0
  12. fastbrowse-0.1.0/assets/architecture.svg +88 -0
  13. fastbrowse-0.1.0/assets/logo.svg +13 -0
  14. fastbrowse-0.1.0/assets/wordmark-dark.svg +19 -0
  15. fastbrowse-0.1.0/assets/wordmark.svg +19 -0
  16. fastbrowse-0.1.0/docs/design.md +22 -0
  17. fastbrowse-0.1.0/docs/evals.md +86 -0
  18. fastbrowse-0.1.0/docs/jev.md +63 -0
  19. fastbrowse-0.1.0/pyproject.toml +60 -0
  20. fastbrowse-0.1.0/scripts/no_slop.py +59 -0
  21. fastbrowse-0.1.0/src/fastbrowse/__init__.py +10 -0
  22. fastbrowse-0.1.0/src/fastbrowse/adapters/__init__.py +1 -0
  23. fastbrowse-0.1.0/src/fastbrowse/adapters/bitwarden.py +97 -0
  24. fastbrowse-0.1.0/src/fastbrowse/adapters/browser_use_cloud.py +125 -0
  25. fastbrowse-0.1.0/src/fastbrowse/adapters/local_chrome.py +108 -0
  26. fastbrowse-0.1.0/src/fastbrowse/agent.py +876 -0
  27. fastbrowse-0.1.0/src/fastbrowse/artifacts.py +21 -0
  28. fastbrowse-0.1.0/src/fastbrowse/browser/__init__.py +6 -0
  29. fastbrowse-0.1.0/src/fastbrowse/browser/capture.js +121 -0
  30. fastbrowse-0.1.0/src/fastbrowse/browser/page.py +794 -0
  31. fastbrowse-0.1.0/src/fastbrowse/browser/session.py +410 -0
  32. fastbrowse-0.1.0/src/fastbrowse/browser/snapshot.js +221 -0
  33. fastbrowse-0.1.0/src/fastbrowse/cli.py +115 -0
  34. fastbrowse-0.1.0/src/fastbrowse/clients/__init__.py +1 -0
  35. fastbrowse-0.1.0/src/fastbrowse/clients/environment.py +160 -0
  36. fastbrowse-0.1.0/src/fastbrowse/clients/openai_compatible.py +217 -0
  37. fastbrowse-0.1.0/src/fastbrowse/clients/typesafe.py +66 -0
  38. fastbrowse-0.1.0/src/fastbrowse/clients/validation.py +338 -0
  39. fastbrowse-0.1.0/src/fastbrowse/clients/vercel.py +89 -0
  40. fastbrowse-0.1.0/src/fastbrowse/config.py +51 -0
  41. fastbrowse-0.1.0/src/fastbrowse/evals/__init__.py +1 -0
  42. fastbrowse-0.1.0/src/fastbrowse/evals/fixtures/account.html +7 -0
  43. fastbrowse-0.1.0/src/fastbrowse/evals/fixtures/cities.html +8 -0
  44. fastbrowse-0.1.0/src/fastbrowse/evals/fixtures/contact.html +9 -0
  45. fastbrowse-0.1.0/src/fastbrowse/evals/fixtures/drafts.html +5 -0
  46. fastbrowse-0.1.0/src/fastbrowse/evals/fixtures/product.html +4 -0
  47. fastbrowse-0.1.0/src/fastbrowse/evals/fixtures/results.html +11 -0
  48. fastbrowse-0.1.0/src/fastbrowse/evals/fixtures/shop.html +8 -0
  49. fastbrowse-0.1.0/src/fastbrowse/evals/latency.py +115 -0
  50. fastbrowse-0.1.0/src/fastbrowse/evals/live.py +340 -0
  51. fastbrowse-0.1.0/src/fastbrowse/evals/local.py +68 -0
  52. fastbrowse-0.1.0/src/fastbrowse/evals/runner.py +103 -0
  53. fastbrowse-0.1.0/src/fastbrowse/evals/tasks.py +116 -0
  54. fastbrowse-0.1.0/src/fastbrowse/jev.py +78 -0
  55. fastbrowse-0.1.0/src/fastbrowse/llm.py +43 -0
  56. fastbrowse-0.1.0/src/fastbrowse/memory.py +74 -0
  57. fastbrowse-0.1.0/src/fastbrowse/models.py +253 -0
  58. fastbrowse-0.1.0/src/fastbrowse/page.py +142 -0
  59. fastbrowse-0.1.0/src/fastbrowse/planner.py +70 -0
  60. fastbrowse-0.1.0/src/fastbrowse/policy.py +366 -0
  61. fastbrowse-0.1.0/src/fastbrowse/retrieval.py +703 -0
  62. fastbrowse-0.1.0/src/fastbrowse/run.py +138 -0
  63. fastbrowse-0.1.0/src/fastbrowse/safety.py +118 -0
  64. fastbrowse-0.1.0/src/fastbrowse/shortcut.py +67 -0
  65. fastbrowse-0.1.0/src/fastbrowse/telemetry.py +63 -0
  66. fastbrowse-0.1.0/src/fastbrowse/verification.py +253 -0
  67. fastbrowse-0.1.0/tests/browser/__init__.py +1 -0
  68. fastbrowse-0.1.0/tests/browser/conftest.py +134 -0
  69. fastbrowse-0.1.0/tests/browser/sites/iframe/iframe.html +14 -0
  70. fastbrowse-0.1.0/tests/browser/sites/main/index.html +129 -0
  71. fastbrowse-0.1.0/tests/browser/sites/main/inner.html +9 -0
  72. fastbrowse-0.1.0/tests/browser/sites/main/popup.html +8 -0
  73. fastbrowse-0.1.0/tests/browser/sites/main/safety.html +14 -0
  74. fastbrowse-0.1.0/tests/browser/test_browser.py +339 -0
  75. fastbrowse-0.1.0/tests/browser/test_lifecycle.py +331 -0
  76. fastbrowse-0.1.0/tests/browser/test_safety.py +298 -0
  77. fastbrowse-0.1.0/tests/browser/test_speed.py +265 -0
  78. fastbrowse-0.1.0/tests/clients/conftest.py +15 -0
  79. fastbrowse-0.1.0/tests/clients/test_environment.py +46 -0
  80. fastbrowse-0.1.0/tests/clients/test_jev.py +305 -0
  81. fastbrowse-0.1.0/tests/clients/test_openai_compatible.py +245 -0
  82. fastbrowse-0.1.0/tests/clients/test_validation.py +113 -0
  83. fastbrowse-0.1.0/tests/test_bitwarden.py +52 -0
  84. fastbrowse-0.1.0/tests/test_contracts.py +28 -0
  85. fastbrowse-0.1.0/tests/test_memory.py +57 -0
  86. fastbrowse-0.1.0/tests/test_planner.py +61 -0
  87. fastbrowse-0.1.0/tests/test_policy.py +130 -0
  88. fastbrowse-0.1.0/tests/test_retrieval.py +601 -0
  89. fastbrowse-0.1.0/tests/test_safety.py +41 -0
  90. fastbrowse-0.1.0/uv.lock +505 -0
@@ -0,0 +1,27 @@
1
+ # Copy to .env and fill in. A real environment variable wins over this file.
2
+
3
+ # Jev, the choice model: one of these two.
4
+ AI_GATEWAY_API_KEY= # Vercel AI Gateway, https://vercel.com/ai-gateway
5
+ # TYPESAFE_API_KEY= # Jev directly, https://typesafe.ai
6
+ # FASTBROWSE_JEV_SOURCE=typesafe # typesafe or gateway, when both keys are set (default: typesafe)
7
+ # FASTBROWSE_JEV_BASE_URL= # a proxy or other host serving that source's API
8
+ # FASTBROWSE_JEV_MODEL=jev-1.13.0 # direct API only; the gateway serves typesafe-ai/jev
9
+
10
+ # The LLM that plans, reads and writes answers.
11
+ OPENROUTER_API_KEY= # https://openrouter.ai/keys
12
+
13
+ # Optional: a Browser Use Cloud browser (--cloud, and the live evals).
14
+ # BROWSER_USE_API_KEY= # https://cloud.browser-use.com
15
+
16
+ # Optional: models and reasoning. Defaults are in src/fastbrowse/clients/environment.py.
17
+ # FASTBROWSE_LLM_MODEL=google/gemini-3.8-flash # every purpose
18
+ # FASTBROWSE_LLM_MODEL_PLAN= # or one purpose: PLAN, READ, FIELD_TEXT,
19
+ # FASTBROWSE_LLM_MODEL_READ= # SHORTCUT, RECOVER, COMPOSE, VERIFY
20
+ # FASTBROWSE_LLM_REASONING=low # low, medium or high
21
+
22
+ # Optional: local Chrome. The binary, when it is not on PATH under a usual name:
23
+ # FASTBROWSE_CHROME=/usr/bin/chromium
24
+ # FASTBROWSE_HEADED=1 # show the window
25
+ # FASTBROWSE_PROFILE=~/.fastbrowse/default # keep the profile between runs
26
+
27
+ # Values for --secret NAME=ENV_VAR are read from the process environment, not this file.
@@ -0,0 +1,23 @@
1
+ name: CI
2
+ on:
3
+ pull_request:
4
+ push:
5
+ branches: [main]
6
+ jobs:
7
+ check:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - uses: actions/checkout@v4
11
+ - uses: astral-sh/setup-uv@v6
12
+ with:
13
+ python-version: "3.14"
14
+ # The hosted-arm eval imports browser_use_sdk; without the extra, pyright sees a different tree than we do.
15
+ - run: uv sync --locked --all-extras
16
+ - run: uv run ruff check .
17
+ - run: uv run ruff format --check .
18
+ - run: uv run pyright
19
+ # Prose is part of the product surface: ASCII punctuation everywhere, and Vale's wording rules on prose.
20
+ - run: uv run python scripts/no_slop.py
21
+ - run: uv run vale sync
22
+ - run: uv run vale README.md docs src scripts tests
23
+ - run: uv run pytest -q
@@ -0,0 +1,37 @@
1
+ name: Release
2
+ on:
3
+ push:
4
+ tags: ["v*"]
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ permissions:
9
+ contents: write
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: astral-sh/setup-uv@v6
13
+ with:
14
+ python-version: "3.14"
15
+ # A tag that disagrees with the package version would publish one number under another's name.
16
+ - run: test "v$(uv version --short)" = "$GITHUB_REF_NAME"
17
+ - run: uv build
18
+ - run: gh release create "$GITHUB_REF_NAME" dist/* --generate-notes --verify-tag
19
+ env:
20
+ GH_TOKEN: ${{ github.token }}
21
+ - uses: actions/upload-artifact@v4
22
+ with:
23
+ name: dist
24
+ path: dist/
25
+ pypi:
26
+ needs: build
27
+ runs-on: ubuntu-latest
28
+ # Trusted publishing: PyPI accepts this job's OIDC identity, so no token is stored anywhere.
29
+ environment: pypi
30
+ permissions:
31
+ id-token: write
32
+ steps:
33
+ - uses: actions/download-artifact@v4
34
+ with:
35
+ name: dist
36
+ path: dist/
37
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,11 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .env
5
+ artifacts/
6
+ dist/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+
10
+ # Synced by `vale sync`; only the house style is tracked.
11
+ .vale/styles/write-good/
@@ -0,0 +1,31 @@
1
+ extends: existence
2
+ message: "'%s' says nothing a plain word would not."
3
+ level: error
4
+ ignorecase: true
5
+ tokens:
6
+ - delve
7
+ - seamless(ly)?
8
+ - tapestry
9
+ - testament to
10
+ - game-chang\w*
11
+ - unleash\w*
12
+ - supercharg\w*
13
+ - effortless(ly)?
14
+ - cutting-edge
15
+ - state-of-the-art
16
+ - ever-evolving
17
+ - in today's
18
+ - look no further
19
+ - dive into
20
+ - harness the power
21
+ - unlock the
22
+ - elevate your
23
+ - at the end of the day
24
+ - it's not just
25
+ - meticulous(ly)?
26
+ - leverag\w*
27
+ - robust(ly|ness)?
28
+ - comprehensive(ly)?
29
+ - it's worth noting
30
+ - worth noting
31
+ - needless to say
@@ -0,0 +1,11 @@
1
+ # Prose rules for Markdown and for Python comments and docstrings. scripts/no_slop.py still owns
2
+ # typographic punctuation, which Vale cannot see outside prose.
3
+ StylesPath = .vale/styles
4
+ MinAlertLevel = error
5
+ Packages = https://github.com/vale-cli/write-good/releases/download/v0.4.1/write-good.zip
6
+
7
+ [*.{md,py}]
8
+ BasedOnStyles = Fastbrowse
9
+ Vale.Repetition = error
10
+ write-good.Weasel = error
11
+ write-good.Cliches = error
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Agent Labs
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.
@@ -0,0 +1,29 @@
1
+ fastbrowse includes code adapted from the following MIT-licensed projects:
2
+
3
+ - browser-use/jev-ultrafast, Copyright (c) 2026 Browser Use
4
+ (src/fastbrowse/browser/page.py and session.py name what they port)
5
+ - browser-use/browser-harness, Copyright (c) 2026 Browser Use
6
+ - browser-use/browser-use, Copyright (c) 2024 Gregor Zunic
7
+
8
+ The wordmark in assets/ is set in Inter, Copyright (c) 2016 The Inter Project Authors,
9
+ licensed under the SIL Open Font License 1.1 and outlined to paths.
10
+
11
+ Those portions are provided under the following terms:
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
@@ -0,0 +1,257 @@
1
+ Metadata-Version: 2.5
2
+ Name: fastbrowse
3
+ Version: 0.1.0
4
+ Summary: A browser agent that picks instead of generating: Jev chooses, an LLM reads and plans, code verifies.
5
+ Project-URL: Homepage, https://github.com/agent-labs-dev/fastbrowse
6
+ Project-URL: Repository, https://github.com/agent-labs-dev/fastbrowse
7
+ Project-URL: Issues, https://github.com/agent-labs-dev/fastbrowse/issues
8
+ Author: Agent Labs
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ License-File: NOTICE
12
+ Requires-Python: >=3.14
13
+ Requires-Dist: cdp-use==1.4.5
14
+ Requires-Dist: httpx[http2]<1,>=0.28
15
+ Requires-Dist: pydantic-settings<3,>=2.10
16
+ Requires-Dist: pydantic<3,>=2.11
17
+ Provides-Extra: browser-use
18
+ Requires-Dist: browser-use-sdk<3.11,>=3.10; extra == 'browser-use'
19
+ Description-Content-Type: text/markdown
20
+
21
+ <div align="center">
22
+
23
+ <picture>
24
+ <source media="(prefers-color-scheme: dark)" srcset="assets/wordmark-dark.svg">
25
+ <img src="assets/wordmark.svg" alt="fastbrowse" width="360">
26
+ </picture>
27
+
28
+ **A browser agent that picks instead of generating.**
29
+
30
+ Jev chooses each action, an LLM plans and reads, and code owns verification, safety and secrets.
31
+
32
+ ![python](https://img.shields.io/badge/python-3.14-475569?style=flat-square)
33
+ ![license](https://img.shields.io/badge/license-MIT-475569?style=flat-square)
34
+ ![status](https://img.shields.io/badge/status-pre--alpha-6366F1?style=flat-square)
35
+
36
+ </div>
37
+
38
+ ---
39
+
40
+ ## Why
41
+
42
+ Most browser agents generate each action from a screenshot. fastbrowse indexes the page into
43
+ candidates and has [Jev](https://typesafe.ai), a choice model, **pick one**, so it cannot click
44
+ something that was never on the page. Every claim in an answer cites a quote stored verbatim from
45
+ the page, and Jev checks each claim against its quote.
46
+
47
+ Six live tasks, two passes each, against hosted Browser Use on the same day: the latest httpx
48
+ version on PyPI (as text and as structured output), the top Hacker News story, httpx's license on
49
+ GitHub, the year of Gödel's incompleteness theorems via Wikipedia search, and a saucedemo login and
50
+ add-to-cart. Each answer is graded against the live source ([method](docs/evals.md)):
51
+
52
+ | | passed | correct answer | median time | mean time | cost per task |
53
+ |:--|:--|:--|:--|:--|:--|
54
+ | **fastbrowse** (cloud browser) | **12/12** | 12/12 | **12.9s** | **15.4s** | **$0.0072** |
55
+ | hosted Browser Use | 11/12 | 11/12 | 14.7s | 25.8s | $0.3767 |
56
+
57
+ **Speed.** fastbrowse beats hosted Browser Use on median and mean at about a fiftieth of the
58
+ cost. Best successful run per task, fastbrowse against hosted:
59
+ pypi-version 9.5s against 18.1s, pypi-structured 11.8s against 15.7s, saucedemo-cart 20.0s against
60
+ 90.7s; hosted is still ahead by 1 to 2.6s on hn-top, github-license and wiki-godel. What did it: a
61
+ direct address for the task proposed while the start page loads, a plan written from the task
62
+ alone on a small model, settling on DOM quiet instead of every image and tracker, short facts picked
63
+ by Jev, hedged requests against provider tails, and no low-confidence recovery for steps that do not act
64
+ ([details](docs/evals.md#results)).
65
+
66
+ Twelve runs is a smoke test, not a benchmark: single runs swing by 4 to 5 seconds, and one
67
+ wiki-godel run took 34s on a slow read. `passed` counts only `complete`; the hosted miss ended with
68
+ "Task ended unexpectedly".
69
+
70
+ ## How it works
71
+
72
+ <picture>
73
+ <source media="(prefers-color-scheme: dark)" srcset="assets/architecture-dark.svg">
74
+ <img src="assets/architecture.svg" alt="The task is planned and the start page opened in parallel; each step indexes the page, Jev picks an operation and target, code gates it and acts; reads keep verbatim quotes, and the answer cites every claim.">
75
+ </picture>
76
+
77
+ Jev never writes an action, it picks one of the candidates on the page, so it cannot click something
78
+ that is not there. The LLM plans, reads and writes. Code owns the gates: irreversible actions stop
79
+ without `--authorize`, secrets reach models by name only, and every claim in an answer cites a
80
+ quote from the page. More in [docs/design.md](docs/design.md).
81
+
82
+ ## How it compares
83
+
84
+ | | hosted Browser Use | [jev-ultrafast](https://github.com/browser-use/jev-ultrafast) | fastbrowse |
85
+ |:--|:--|:--|:--|
86
+ | Choosing an action | LLM generates from a screenshot | Jev picks from indexed controls | Jev picks from indexed controls |
87
+ | Returns | an answer | `DONE` or `BLOCKED` | an answer with quotes, or why it stopped |
88
+ | Reads pages | yes | no | yes, every claim cited |
89
+ | Signing in | yes | password fields excluded | `--secret`, models see names only |
90
+ | Irreversible actions | not gated | not gated | stop unless `--authorize` |
91
+ | Browser | cloud | local Chrome, your profile | local Chrome or cloud |
92
+
93
+ jev-ultrafast is Browser Use's navigation agent, with a measured 7.1 second Google Flights run, and
94
+ fastbrowse shares its core techniques (one browser call per page read, operation and target chosen
95
+ in one Jev request). The column describes its `main` branch as of 2026-09-18; an experimental
96
+ branch adds a planner with requirement checks.
97
+
98
+ ## Try it
99
+
100
+ Needs Python 3.14, [uv](https://docs.astral.sh/uv/), and Chrome (not needed with `--cloud`).
101
+
102
+ ```sh
103
+ git clone https://github.com/agent-labs-dev/fastbrowse.git && cd fastbrowse
104
+ uv sync
105
+ cp .env.example .env # add AI_GATEWAY_API_KEY or TYPESAFE_API_KEY, and OPENROUTER_API_KEY
106
+ uv run fastbrowse "What is the title of the top story right now?" --start https://news.ycombinator.com/
107
+ ```
108
+
109
+ ```
110
+ 0 read -> executed
111
+ complete ($0.0071, 1 steps)
112
+ The top story on Hacker News is titled "...".
113
+ ```
114
+
115
+ Steps go to stderr and the answer to stdout. `--json` prints every step, the quotes behind the
116
+ answer, and cost by component.
117
+
118
+ | Flag | Effect |
119
+ |:--|:--|
120
+ | `--start URL` | required: the page to open first |
121
+ | `--cloud` | use a [Browser Use Cloud](https://cloud.browser-use.com) browser (`BROWSER_USE_API_KEY`); far less likely to be bot-challenged. Prints a URL to watch it live |
122
+ | `--headed` | show the local Chrome window |
123
+ | `--profile DIR` | keep the local Chrome profile in `DIR`, so a site signed into there stays signed in |
124
+ | `--authorize` | allow submit, pay, delete and send; without it the run stops at `needs_confirmation` first |
125
+ | `--secret NAME=ENV_VAR` | let the agent type `$ENV_VAR` on the start origin; models only see `NAME` |
126
+ | `--bitwarden ITEM` | let the agent type that vault login's `username` and `password`, only where the item's saved URIs and their match detection allow |
127
+ | `--max-steps N`, `--max-dollars N` | bound the run |
128
+ | `--downloads DIR` | keep downloaded files |
129
+ | `--json` | full result instead of the answer |
130
+
131
+ ```sh
132
+ export SAUCE_PASSWORD=secret_sauce
133
+ uv run fastbrowse "Log in as standard_user with the saved password and add the backpack to the cart." \
134
+ --start https://www.saucedemo.com/ --secret password=SAUCE_PASSWORD --authorize
135
+ ```
136
+
137
+ ### Signed-in sites
138
+
139
+ Sign in once by hand in a profile of its own, then point runs at it. The agent reuses the session
140
+ and never sees a password.
141
+
142
+ ```sh
143
+ google-chrome --user-data-dir="$HOME/.fastbrowse/amazon" https://www.amazon.com/ # sign in, then close Chrome
144
+ uv run fastbrowse "Add a UGREEN USB-A to USB-C cable, 2m, to my cart." \
145
+ --start https://www.amazon.com/ --profile ~/.fastbrowse/amazon --headed
146
+ ```
147
+
148
+ Or sign in from your vault: with the [Bitwarden CLI](https://bitwarden.com/help/cli/) signed in, name
149
+ the item. Its values stay in this process and are typed only on a site the item's saved URIs cover.
150
+
151
+ ```sh
152
+ export BW_SESSION="$(bw unlock --raw)"
153
+ uv run fastbrowse "Sign in with the saved login, then add a UGREEN USB-A to USB-C cable, 2m, to my cart." \
154
+ --start https://www.amazon.com/ --bitwarden Amazon --headed
155
+ ```
156
+
157
+ ### Models
158
+
159
+ The LLM defaults to `google/gemini-3.8-flash` at low reasoning effort, with
160
+ `google/gemini-3.5-flash-lite` for planning, proposing a direct address and typing field text.
161
+ Override with `FASTBROWSE_LLM_MODEL` (every purpose), `FASTBROWSE_LLM_MODEL_<PURPOSE>` (`PLAN`,
162
+ `READ`, `FIELD_TEXT`, `SHORTCUT`, `RECOVER`, `COMPOSE`, `VERIFY`) and `FASTBROWSE_LLM_REASONING`
163
+ (`low`, `medium`, `high`). Flash-lite for every purpose is faster but scored 8/12 live, so it is
164
+ used only where its output is checked downstream.
165
+
166
+ ### Results
167
+
168
+ The exit code is 0 only for `complete`.
169
+
170
+ | Status | Meaning |
171
+ |:--|:--|
172
+ | `complete` | every information requirement is backed by a quote, and every action is confirmed on the page |
173
+ | `unverified` | it believes it finished but could not back every claim |
174
+ | `needs_confirmation` | stopped before an irreversible action; re-run with `--authorize` |
175
+ | `needs_login` | a sign-in wall that no `--secret` covers |
176
+ | `needs_input` | a field needs a value you did not give, which is never invented |
177
+ | `stuck` | recovery ran out without the page moving |
178
+ | `budget_exceeded` | a step, call, time or dollar limit was reached |
179
+ | `observation_limit` | the page has more controls than Jev can take in |
180
+ | `error` | a model or browser failure |
181
+
182
+ ## Embed it
183
+
184
+ ```python
185
+ import asyncio
186
+
187
+ from pydantic import BaseModel
188
+
189
+ from fastbrowse import run_task
190
+ from fastbrowse.models import Limits
191
+
192
+
193
+ class Release(BaseModel):
194
+ package: str
195
+ version: str
196
+
197
+
198
+ async def main() -> None:
199
+ result = await run_task(
200
+ "Find the httpx package and report its name and latest released version.",
201
+ start="https://pypi.org/",
202
+ output_schema=Release,
203
+ limits=Limits(max_dollars=0.10),
204
+ )
205
+ print(result.status, result.data, f"${result.cost.known_dollars:.4f}")
206
+ for evidence in result.evidence:
207
+ print(f' "{evidence.quote}" from {evidence.url}')
208
+
209
+
210
+ asyncio.run(main())
211
+ ```
212
+
213
+ ```
214
+ complete {'package': 'httpx', 'version': '0.28.1'} $0.0114
215
+ "httpx 0.28.1" from https://pypi.org/project/httpx/
216
+ "pip install httpx" from https://pypi.org/project/httpx/
217
+ ```
218
+
219
+ `run_task` builds the browser and clients, runs the agent, and closes the browser on every path.
220
+ The result has `status`, `answer`, `data`, `evidence`, `final_url` and an itemized `cost`.
221
+
222
+ Jev comes from Typesafe directly or through the Vercel AI Gateway, whichever key is set; with both,
223
+ `FASTBROWSE_JEV_SOURCE` picks one, and `FASTBROWSE_JEV_BASE_URL` sends it through a proxy. Anything
224
+ else, such as a cache or a recorded fixture, can be passed as `run_task(jev=...)`: an object with one
225
+ `evaluate(state, questions)` method (the `JevClient` protocol in `jev.py`). The LLM works the same way.
226
+
227
+ ## Safety model
228
+
229
+ - **Irreversible actions.** Before any button or submit, Jev is asked whether it commits something
230
+ that cannot be undone. Without `--authorize`, a yes stops the run. This is a classifier, not a
231
+ guarantee: a page can word a harmful control to look harmless.
232
+ - **Secrets.** Models see secret names only. A value is resolved at the moment of typing, only for
233
+ its declared origin, and is redacted from everything the run returns, in raw, URL-encoded and
234
+ JSON-escaped form. A password field is typed only from a stored secret, never
235
+ generated.
236
+ - **Page content is data.** Every prompt says so, and completion is judged against quotes and page
237
+ state rather than the model's say-so.
238
+
239
+ ## Evals and development
240
+
241
+ ```sh
242
+ uv sync --all-extras # the hosted-arm SDK too, which pyright checks
243
+ uv run python -m fastbrowse.evals.runner # local fixtures, under half a cent a task
244
+ uv run --extra browser-use python -m fastbrowse.evals.live # live head-to-head; --arms fast skips hosted
245
+ uv run ruff format . && uv run ruff check . && uv run pyright && uv run pytest
246
+ uv run python scripts/no_slop.py && uv run vale sync && uv run vale README.md docs src scripts tests
247
+ ```
248
+
249
+ Grades come only from things the agent cannot write: requests the fixture server recorded, truth
250
+ from a site's own API, or the URL the browser ended on. See [docs/evals.md](docs/evals.md),
251
+ [docs/design.md](docs/design.md), and [docs/jev.md](docs/jev.md) for every Jev assumption checked against
252
+ Typesafe's documentation.
253
+
254
+ ## License
255
+
256
+ MIT, copyright Agent Labs. Adapted third-party code is credited in
257
+ [NOTICE](NOTICE).