cascade-ai 0.78.0 → 0.79.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.
package/dist/cli.cjs CHANGED
@@ -1849,7 +1849,7 @@ var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SEC
1849
1849
  var init_constants = __esm({
1850
1850
  "src/constants.ts"() {
1851
1851
  init_pricing();
1852
- CASCADE_VERSION = "0.78.0";
1852
+ CASCADE_VERSION = "0.79.0";
1853
1853
  CASCADE_CONFIG_FILE = ".cascade/config.json";
1854
1854
  CASCADE_DB_FILE = ".cascade/memory.db";
1855
1855
  CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
@@ -15554,6 +15554,31 @@ var ToolsConfigSchema = zod.z.object({
15554
15554
  * stop control for the run that is acting.
15555
15555
  */
15556
15556
  agentBrowserControl: zod.z.boolean().default(false),
15557
+ /**
15558
+ * Where a HOSTED run gets a browser, since it has none of its own.
15559
+ *
15560
+ * The desktop drives the Chromium the user is signed into and can watch. A
15561
+ * server has neither, and running one inside the container is wrong twice
15562
+ * over — page JavaScript issues requests no URL check ever sees, from inside
15563
+ * the trust boundary, and a session costs 200-400 MB. So this points at a
15564
+ * browser somebody else runs.
15565
+ *
15566
+ * Absent by default: no provider, no capability. There is nothing to turn
15567
+ * off because nothing is registered until an endpoint is supplied.
15568
+ */
15569
+ remoteBrowser: zod.z.object({
15570
+ /** `cdp` for an endpoint you run; `steel` for a Steel deployment. */
15571
+ provider: zod.z.enum(["cdp", "steel"]).optional(),
15572
+ /** The websocket endpoint for `cdp`, or the API base for `steel`. */
15573
+ url: zod.z.string().optional(),
15574
+ apiKey: zod.z.string().optional(),
15575
+ /**
15576
+ * Concurrent sessions. One by default and deliberately so: every session
15577
+ * is billed, and a wave of workers each opening their own is a cost the
15578
+ * user did not choose. Raising it is a decision, not a default.
15579
+ */
15580
+ maxSessions: zod.z.number().int().min(1).max(16).default(1)
15581
+ }).optional(),
15557
15582
  mcpServers: zod.z.array(McpServerConfigSchema).optional(),
15558
15583
  mcpTrusted: zod.z.array(zod.z.string()).optional(),
15559
15584
  /** Web search backends — at least one should be configured for best results */
@@ -31949,8 +31974,38 @@ ${input}` }],
31949
31974
  * run — it is simply not there.
31950
31975
  */
31951
31976
  setBrowserController(controller, release) {
31977
+ this.registerBrowserControl(controller, release, this.config.tools?.agentBrowserControl === true);
31978
+ }
31979
+ /**
31980
+ * Wire a REMOTE browser — one the host connects to rather than one the user
31981
+ * is signed into.
31982
+ *
31983
+ * A separate entry point because the enablement is a different question, and
31984
+ * conflating them made the hosted capability dead on arrival: this went out
31985
+ * calling `setBrowserController`, which gates on `agentBrowserControl`, and
31986
+ * that flag means "drive the browser I am signed into on this machine". It
31987
+ * defaults to false and has no meaning on a server, so the hosted tool was
31988
+ * never registered at all — the feature looked shipped and did nothing.
31989
+ *
31990
+ * Configuring a provider IS the opt-in here. It takes a deliberate act with
31991
+ * an endpoint and usually a paid account, and the browser it reaches is a
31992
+ * throwaway signed into nothing — a smaller consent than handing over the
31993
+ * session the user is already authenticated in, and one the desktop flag
31994
+ * cannot stand in for.
31995
+ */
31996
+ setRemoteBrowserController(controller, release) {
31997
+ this.registerBrowserControl(controller, release, Boolean(this.config.tools?.remoteBrowser?.provider));
31998
+ }
31999
+ /**
32000
+ * The gates every surface shares, with only enablement differing.
32001
+ *
32002
+ * One place, because the two callers above are exactly the sibling-writer
32003
+ * shape that has already drifted twice in this feature: a rule added to one
32004
+ * and not the other is silent on the surface that missed it.
32005
+ */
32006
+ registerBrowserControl(controller, release, enabled) {
31952
32007
  if (this.unattended) return;
31953
- if (!this.config.tools?.agentBrowserControl) return;
32008
+ if (!enabled) return;
31954
32009
  if ((this.config.tools?.disabledTools ?? []).includes("browser_control")) return;
31955
32010
  this.toolRegistry.register(new BrowserControlTool(controller, release));
31956
32011
  }
@@ -38427,6 +38482,20 @@ function applySettingsPayload(config, data) {
38427
38482
  if (data.webSearch.tavilyApiKey) next.tavilyApiKey = data.webSearch.tavilyApiKey;
38428
38483
  config.tools.webSearch = next;
38429
38484
  }
38485
+ if (data.remoteBrowser && typeof data.remoteBrowser === "object") {
38486
+ const prior = config.tools.remoteBrowser ?? {};
38487
+ const next = { ...prior };
38488
+ if (data.remoteBrowser.provider === "cdp" || data.remoteBrowser.provider === "steel") {
38489
+ next.provider = data.remoteBrowser.provider;
38490
+ }
38491
+ if (typeof data.remoteBrowser.url === "string") next.url = data.remoteBrowser.url.trim() || void 0;
38492
+ if (data.remoteBrowser.apiKey) next.apiKey = data.remoteBrowser.apiKey;
38493
+ const n = data.remoteBrowser.maxSessions;
38494
+ if (typeof n === "number" && Number.isFinite(n)) {
38495
+ next.maxSessions = Math.min(Math.max(Math.floor(n), 1), 16);
38496
+ }
38497
+ config.tools.remoteBrowser = next;
38498
+ }
38430
38499
  if (data.advanced && typeof data.advanced === "object") {
38431
38500
  const a = data.advanced;
38432
38501
  const num = (v, min, max) => {
@@ -38473,6 +38542,7 @@ function settingsSnapshot(config) {
38473
38542
  if (p?.type && p?.baseUrl && p.type !== "azure") endpoints[p.type] = p.baseUrl;
38474
38543
  }
38475
38544
  const ws = config.tools?.webSearch ?? {};
38545
+ const rb = config.tools?.remoteBrowser ?? {};
38476
38546
  return {
38477
38547
  models: config.models ?? {},
38478
38548
  budget: {
@@ -38502,6 +38572,15 @@ function settingsSnapshot(config) {
38502
38572
  hasBraveKey: typeof ws.braveApiKey === "string" && ws.braveApiKey.length > 0,
38503
38573
  hasTavilyKey: typeof ws.tavilyApiKey === "string" && ws.tavilyApiKey.length > 0
38504
38574
  },
38575
+ // `hasApiKey`, never the key. The panel needs to show whether one is set;
38576
+ // sending the value back would put a live credential in every settings
38577
+ // response, where it reaches any client that can read them.
38578
+ remoteBrowser: {
38579
+ provider: rb.provider,
38580
+ url: rb.url,
38581
+ hasApiKey: typeof rb.apiKey === "string" && rb.apiKey.length > 0,
38582
+ maxSessions: rb.maxSessions
38583
+ },
38505
38584
  advanced: {
38506
38585
  autonomy: config.autonomy,
38507
38586
  planApproval: config.planApproval,