-
Notifications
You must be signed in to change notification settings - Fork 89
browser-use/browsercode #163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: lean-browser-prompt
Are you sure you want to change the base?
Changes from all commits
1fdb393
c99117f
0a2ccc3
4b1cee0
d9d571e
b58d06c
e812c25
a990237
657519a
67bd12b
b48dbef
9651c9f
4b22bab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -6,7 +6,7 @@ | |||||
| import { Context, Effect, Layer } from "effect" | ||||||
| import { HttpClient, HttpClientRequest } from "effect/unstable/http" | ||||||
|
|
||||||
| const ENDPOINT = "https://fetch.browser-use.com/fetch" | ||||||
| const DEFAULT_ENDPOINT = "https://fetch.browser-use.com/fetch" | ||||||
|
|
||||||
| export interface FetchResult { | ||||||
| readonly body: ArrayBuffer | ||||||
|
|
@@ -32,11 +32,12 @@ export const layer = Layer.effect( | |||||
| Effect.gen(function* () { | ||||||
| const http = yield* HttpClient.HttpClient | ||||||
| const apiKey = process.env.BROWSER_USE_API_KEY ?? "" | ||||||
| const endpoint = resolveEndpoint() | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents
Suggested change
|
||||||
| return Service.of({ | ||||||
| enabled: apiKey.length > 0, | ||||||
| fetch: (url, { timeoutMs }) => | ||||||
| Effect.gen(function* () { | ||||||
| const request = yield* HttpClientRequest.post(ENDPOINT).pipe( | ||||||
| const request = yield* HttpClientRequest.post(endpoint).pipe( | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: When the configured endpoint returns a redirect, Prompt for AI agents |
||||||
| HttpClientRequest.setHeaders({ "Content-Type": "application/json", "X-Browser-Use-API-Key": apiKey }), | ||||||
| HttpClientRequest.bodyJson({ url, timeout_ms: timeoutMs }), | ||||||
| ) | ||||||
|
|
@@ -56,4 +57,42 @@ export const layer = Layer.effect( | |||||
| }), | ||||||
| ) | ||||||
|
|
||||||
| // Overridable so a caller can mediate the request and keep the real key out of | ||||||
| // this process entirely. The default endpoint is a general-purpose URL fetcher, | ||||||
| // so anything holding the key can send it to an arbitrary host -- an untrusted | ||||||
| // or injectable agent should be given a mediating endpoint and a throwaway | ||||||
| // credential instead of the real one. | ||||||
| // | ||||||
| // Every rejection below is an operator mistake at startup, and each one would | ||||||
| // otherwise put X-Browser-Use-API-Key somewhere it should not go. Set-but-empty | ||||||
| // is a mistake rather than a default, because the default is the direct fetcher | ||||||
| // -- the exact path someone setting this variable is trying to leave. | ||||||
| function resolveEndpoint() { | ||||||
| const configured = process.env.BCODE_FETCH_USE_ENDPOINT | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: Prompt for AI agents |
||||||
| if (configured === undefined) return DEFAULT_ENDPOINT | ||||||
| if (configured.trim() === "") | ||||||
| throw new Error("BCODE_FETCH_USE_ENDPOINT is set but empty; unset it to use the default fetcher") | ||||||
| // The messages below name the variable and at most the destination's origin, | ||||||
| // never the value: it can carry userinfo or a token in its query, and writing | ||||||
| // that to stderr is the same leak this override exists to close. The operator | ||||||
| // can read back their own environment variable. | ||||||
| if (!URL.canParse(configured)) throw new Error("BCODE_FETCH_USE_ENDPOINT is not a valid url") | ||||||
| const url = new URL(configured) | ||||||
| // Checked before the loopback exemption below, which would otherwise wave | ||||||
| // through ftp://localhost and defer the failure to the first webfetch. | ||||||
| if (url.protocol !== "https:" && url.protocol !== "http:") | ||||||
| throw new Error(`BCODE_FETCH_USE_ENDPOINT must be http or https, not ${url.protocol}`) | ||||||
| if (url.protocol !== "https:" && !LOOPBACK.test(url.hostname)) | ||||||
| throw new Error( | ||||||
| `BCODE_FETCH_USE_ENDPOINT must use https outside loopback; refusing to send the api key in cleartext to ${url.origin}`, | ||||||
| ) | ||||||
| return configured | ||||||
| } | ||||||
|
|
||||||
| // All of 127.0.0.0/8 is loopback rather than 127.0.0.1 alone, a trailing dot is | ||||||
| // the same name in its rooted form, and URL reports the IPv6 literal with its | ||||||
| // brackets, so "::1" would never match. Anchored and numeric so a DNS name like | ||||||
| // 127.example.com is not mistaken for the subnet. | ||||||
| const LOOPBACK = /^(localhost\.?|\[::1\]|127(\.\d{1,3}){3})$/ | ||||||
|
|
||||||
| export * as FetchUse from "./fetch-use" | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1108,9 +1108,22 @@ const layer = Layer.effect( | |
| (part) => part.type === "tool" && !part.metadata?.providerExecuted && !isOrphanedInterruptedTool(part), | ||
| ) ?? false | ||
|
|
||
| // "unknown" is every mapper's fallback for a finish reason we could not | ||
| // interpret, and the AI SDK reports a stream that closed without any | ||
| // finish chunk at all as "other", which maps here too. Neither means the | ||
| // model was done, so resample the turn like "tool-calls" instead of | ||
| // exiting as a clean completion. Exiting here silently truncated runs | ||
| // mid-task: the error check below already excludes "unknown", so nothing | ||
| // was recorded anywhere. | ||
| if (lastAssistant?.finish === "unknown") | ||
| yield* Effect.logWarning("resampling turn that ended with an unmapped finish reason", { | ||
| "session.id": sessionID, | ||
| messageID: lastAssistant.id, | ||
| }) | ||
|
|
||
| if ( | ||
| lastAssistant?.finish && | ||
| !["tool-calls"].includes(lastAssistant.finish) && | ||
| !["tool-calls", "unknown"].includes(lastAssistant.finish) && | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: When the provider repeatedly emits an unsupported or missing finish reason, this branch keeps Prompt for AI agents |
||
| !hasToolCalls && | ||
| lastAssistant.parentID === lastUser.id | ||
| ) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: The solid-js bump from 1.9.10 to 1.9.15 drops the previous solid-js@1.9.10.patch without carrying its fix forward. If that patch corrected a bug that 1.9.15 does not include, the fix is silently lost in whichever package relied on it (the TUI, console, and app all consume solid-js). Confirm the patch's fix landed upstream in 1.9.15, or port the patch to 1.9.15, before merging.
Prompt for AI agents