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.js CHANGED
@@ -1796,7 +1796,7 @@ var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SEC
1796
1796
  var init_constants = __esm({
1797
1797
  "src/constants.ts"() {
1798
1798
  init_pricing();
1799
- CASCADE_VERSION = "0.78.0";
1799
+ CASCADE_VERSION = "0.79.0";
1800
1800
  CASCADE_CONFIG_FILE = ".cascade/config.json";
1801
1801
  CASCADE_DB_FILE = ".cascade/memory.db";
1802
1802
  CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
@@ -15501,6 +15501,31 @@ var ToolsConfigSchema = z.object({
15501
15501
  * stop control for the run that is acting.
15502
15502
  */
15503
15503
  agentBrowserControl: z.boolean().default(false),
15504
+ /**
15505
+ * Where a HOSTED run gets a browser, since it has none of its own.
15506
+ *
15507
+ * The desktop drives the Chromium the user is signed into and can watch. A
15508
+ * server has neither, and running one inside the container is wrong twice
15509
+ * over — page JavaScript issues requests no URL check ever sees, from inside
15510
+ * the trust boundary, and a session costs 200-400 MB. So this points at a
15511
+ * browser somebody else runs.
15512
+ *
15513
+ * Absent by default: no provider, no capability. There is nothing to turn
15514
+ * off because nothing is registered until an endpoint is supplied.
15515
+ */
15516
+ remoteBrowser: z.object({
15517
+ /** `cdp` for an endpoint you run; `steel` for a Steel deployment. */
15518
+ provider: z.enum(["cdp", "steel"]).optional(),
15519
+ /** The websocket endpoint for `cdp`, or the API base for `steel`. */
15520
+ url: z.string().optional(),
15521
+ apiKey: z.string().optional(),
15522
+ /**
15523
+ * Concurrent sessions. One by default and deliberately so: every session
15524
+ * is billed, and a wave of workers each opening their own is a cost the
15525
+ * user did not choose. Raising it is a decision, not a default.
15526
+ */
15527
+ maxSessions: z.number().int().min(1).max(16).default(1)
15528
+ }).optional(),
15504
15529
  mcpServers: z.array(McpServerConfigSchema).optional(),
15505
15530
  mcpTrusted: z.array(z.string()).optional(),
15506
15531
  /** Web search backends — at least one should be configured for best results */
@@ -31896,8 +31921,38 @@ ${input}` }],
31896
31921
  * run — it is simply not there.
31897
31922
  */
31898
31923
  setBrowserController(controller, release) {
31924
+ this.registerBrowserControl(controller, release, this.config.tools?.agentBrowserControl === true);
31925
+ }
31926
+ /**
31927
+ * Wire a REMOTE browser — one the host connects to rather than one the user
31928
+ * is signed into.
31929
+ *
31930
+ * A separate entry point because the enablement is a different question, and
31931
+ * conflating them made the hosted capability dead on arrival: this went out
31932
+ * calling `setBrowserController`, which gates on `agentBrowserControl`, and
31933
+ * that flag means "drive the browser I am signed into on this machine". It
31934
+ * defaults to false and has no meaning on a server, so the hosted tool was
31935
+ * never registered at all — the feature looked shipped and did nothing.
31936
+ *
31937
+ * Configuring a provider IS the opt-in here. It takes a deliberate act with
31938
+ * an endpoint and usually a paid account, and the browser it reaches is a
31939
+ * throwaway signed into nothing — a smaller consent than handing over the
31940
+ * session the user is already authenticated in, and one the desktop flag
31941
+ * cannot stand in for.
31942
+ */
31943
+ setRemoteBrowserController(controller, release) {
31944
+ this.registerBrowserControl(controller, release, Boolean(this.config.tools?.remoteBrowser?.provider));
31945
+ }
31946
+ /**
31947
+ * The gates every surface shares, with only enablement differing.
31948
+ *
31949
+ * One place, because the two callers above are exactly the sibling-writer
31950
+ * shape that has already drifted twice in this feature: a rule added to one
31951
+ * and not the other is silent on the surface that missed it.
31952
+ */
31953
+ registerBrowserControl(controller, release, enabled) {
31899
31954
  if (this.unattended) return;
31900
- if (!this.config.tools?.agentBrowserControl) return;
31955
+ if (!enabled) return;
31901
31956
  if ((this.config.tools?.disabledTools ?? []).includes("browser_control")) return;
31902
31957
  this.toolRegistry.register(new BrowserControlTool(controller, release));
31903
31958
  }
@@ -38374,6 +38429,20 @@ function applySettingsPayload(config, data) {
38374
38429
  if (data.webSearch.tavilyApiKey) next.tavilyApiKey = data.webSearch.tavilyApiKey;
38375
38430
  config.tools.webSearch = next;
38376
38431
  }
38432
+ if (data.remoteBrowser && typeof data.remoteBrowser === "object") {
38433
+ const prior = config.tools.remoteBrowser ?? {};
38434
+ const next = { ...prior };
38435
+ if (data.remoteBrowser.provider === "cdp" || data.remoteBrowser.provider === "steel") {
38436
+ next.provider = data.remoteBrowser.provider;
38437
+ }
38438
+ if (typeof data.remoteBrowser.url === "string") next.url = data.remoteBrowser.url.trim() || void 0;
38439
+ if (data.remoteBrowser.apiKey) next.apiKey = data.remoteBrowser.apiKey;
38440
+ const n = data.remoteBrowser.maxSessions;
38441
+ if (typeof n === "number" && Number.isFinite(n)) {
38442
+ next.maxSessions = Math.min(Math.max(Math.floor(n), 1), 16);
38443
+ }
38444
+ config.tools.remoteBrowser = next;
38445
+ }
38377
38446
  if (data.advanced && typeof data.advanced === "object") {
38378
38447
  const a = data.advanced;
38379
38448
  const num = (v, min, max) => {
@@ -38420,6 +38489,7 @@ function settingsSnapshot(config) {
38420
38489
  if (p?.type && p?.baseUrl && p.type !== "azure") endpoints[p.type] = p.baseUrl;
38421
38490
  }
38422
38491
  const ws = config.tools?.webSearch ?? {};
38492
+ const rb = config.tools?.remoteBrowser ?? {};
38423
38493
  return {
38424
38494
  models: config.models ?? {},
38425
38495
  budget: {
@@ -38449,6 +38519,15 @@ function settingsSnapshot(config) {
38449
38519
  hasBraveKey: typeof ws.braveApiKey === "string" && ws.braveApiKey.length > 0,
38450
38520
  hasTavilyKey: typeof ws.tavilyApiKey === "string" && ws.tavilyApiKey.length > 0
38451
38521
  },
38522
+ // `hasApiKey`, never the key. The panel needs to show whether one is set;
38523
+ // sending the value back would put a live credential in every settings
38524
+ // response, where it reaches any client that can read them.
38525
+ remoteBrowser: {
38526
+ provider: rb.provider,
38527
+ url: rb.url,
38528
+ hasApiKey: typeof rb.apiKey === "string" && rb.apiKey.length > 0,
38529
+ maxSessions: rb.maxSessions
38530
+ },
38452
38531
  advanced: {
38453
38532
  autonomy: config.autonomy,
38454
38533
  planApproval: config.planApproval,