@@ -225,6 +225,54 @@ Python 3.10 or newer, anywhere that can reach the server. Same CLI as the npm pa
225225
226226See [ ` clients/python/README.md ` ] ( clients/python/README.md ) .
227227
228+ ## Optional Jev web decisions (Python)
229+
230+ The Python client has an opt-in ` aether_browser.jev ` layer for ** Jev → your selected model** .
231+ Jev reads a caller-selected text excerpt and chooses between up to three caller-approved URLs,
232+ handoff, or human takeover. The browser navigates only to a URL the caller supplied, and its
233+ normal destination checks still apply. On handoff, ** your callback** receives the current page
234+ evidence and Jev's request IDs, token counts, and costs; your application invokes its chosen
235+ reasoning model to write, analyze, or continue browsing. Jev returns decisions, not prose or
236+ visual judgments, so screenshots and complex page interactions belong to that selected model or
237+ the human watching the live browser.
238+
239+ ``` python
240+ import os
241+
242+ from aether_browser import AgentBrowser, session
243+ from aether_browser.jev import HumanTakeover, JevWebAgent, NavigationOption, OpenRouterJev
244+
245+ browser = AgentBrowser(controller_token = os.environ[" AGENT_BROWSER_CONTROLLER_TOKEN" ])
246+ with session(browser) as live:
247+ first_page = live.navigate(" https://example.com" )
248+ result = JevWebAgent(OpenRouterJev(os.environ[" OPENROUTER_API_KEY" ])).run(
249+ live,
250+ goal = " Read the site's documentation" ,
251+ initial_page = first_page,
252+ options_for = lambda page : [
253+ NavigationOption(" https://example.com/docs" , " Official documentation" )
254+ ] if page[" final_url" ] == " https://example.com/" else [],
255+ excerpt_for = lambda page : page[" readable_text" ][:6000 ],
256+ selected_model = lambda handoff : your_model(handoff), # Supply your own model callback.
257+ )
258+ if isinstance (result, HumanTakeover):
259+ print (" Take control at" , result.view_url)
260+ input (" Press Enter when the human is done to close this session: " )
261+ else :
262+ print (result)
263+ ```
264+
265+ The example's ` your_model ` is an application-defined function, not part of this package. The
266+ ` excerpt_for ` callback explicitly chooses text sent to OpenRouter, and ` options_for ` explicitly
267+ chooses candidate URLs. Never return private page content or signed URLs unless your application
268+ intends to send them to that provider. The OpenRouter key stays in the calling process; the
269+ browser server does not store it. No Jev request occurs unless you call this optional layer.
270+ If Jev fails or returns an invalid decision, the selected-model callback receives a handoff
271+ with ` reason="jev_unavailable" ` and the browser takes no additional action. Recognized authentication
272+ or payment pages prompt human takeover before Jev receives their text. The session remains owned
273+ by your code; keep it open while the human takes over. See [ ` docs/JEV.md ` ] ( docs/JEV.md ) for
274+ the complete contract and limitations.
275+
228276## What makes it different
229277
230278- ** One session, two participants.** The agent acts through JSON. You watch the same display, and
@@ -302,7 +350,7 @@ and credential injection are excluded from the public core. Provenance status is
302350## What it does not do
303351
304352- No hosted cloud service, cloud control plane, or production remote-hosting claim.
305- - No bundled LLM, account system, dashboard, credential vault, or credential injection.
353+ - No bundled LLM or default model calls , account system, dashboard, credential vault, or credential injection.
306354- No CAPTCHA bypass, anti-detection guarantee, stealth claim, or proxy rotation.
307355- No arbitrary JavaScript, shell, filesystem, upload, clipboard, download, or raw CDP API.
308356- No multi-session pool, ATS integration, trading integration, or brokerage behavior.
0 commit comments