Skip to content

Commit c96b9df

Browse files
committed
cleanup, screenshots, better config setup, proxies off default
1 parent 28388b1 commit c96b9df

File tree

9 files changed

+493
-456
lines changed

9 files changed

+493
-456
lines changed

browserbase/config.d.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export type Config = {
1313
*
1414
* @default false
1515
*/
16-
proxies? : boolean;
16+
proxies?: boolean;
1717
/**
1818
* Potential Browserbase Context to use
1919
* Would be a context ID
@@ -32,5 +32,17 @@ export type Config = {
3232
* The host to bind the server to. Default is localhost. Use 0.0.0.0 to bind to all interfaces.
3333
*/
3434
host?: string;
35-
},
35+
};
36+
tools?: {
37+
/**
38+
* Configuration for the browser_take_screenshot tool.
39+
*/
40+
browser_take_screenshot?: {
41+
/**
42+
* Whether to disable base64-encoded image responses to the clients that
43+
* don't support binary data or prefer to save on tokens.
44+
*/
45+
omitBase64?: boolean;
46+
}
47+
}
3648
};

browserbase/src/context.ts

Lines changed: 123 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
closeAllSessions,
77
} from "./sessionManager.js";
88
import type { Tool, ToolContext, ToolResult } from "./tools/tool.js";
9-
import type { Config } from "./config.js";
9+
import type { Config } from "../config.js";
1010
import {
1111
Resource,
1212
CallToolResult,
@@ -48,9 +48,8 @@ export type ToolActionResult =
4848
*/
4949
export class Context {
5050
private server: Server;
51-
private config: Config;
51+
public readonly config: Config;
5252
public currentSessionId: string = defaultSessionId;
53-
private screenshots = new Map<string, string>();
5453
private latestSnapshots = new Map<string, PageSnapshot>();
5554
private screenshotResources = new Map<
5655
string,
@@ -63,11 +62,6 @@ export class Context {
6362
this.screenshotResources = new Map();
6463
}
6564

66-
// --- Public Getter for Config ---
67-
public getConfig(): Config {
68-
return this.config;
69-
}
70-
7165
// --- Snapshot State Handling (Using PageSnapshot) ---
7266

7367
/**
@@ -261,7 +255,9 @@ export class Context {
261255

262256
// Get the CURRENT latest snapshot - DO NOT capture a new one here.
263257
const snapshot = this.latestSnapshots.get(this.currentSessionId);
264-
const initialSnapshotIdentifier = snapshot?.text().substring(0, 60).replace(/\\n/g, '\\\\n') ?? "[No Snapshot]";
258+
const initialSnapshotIdentifier =
259+
snapshot?.text().substring(0, 60).replace(/\\n/g, "\\\\n") ??
260+
"[No Snapshot]";
265261

266262
let locator: Locator | undefined;
267263

@@ -270,43 +266,67 @@ export class Context {
270266
identifier = validatedArgs.selector;
271267
identifierType = "selector";
272268
if (!identifier) {
273-
throw new Error(`Missing required 'selector' argument for tool ${toolName}.`);
269+
throw new Error(
270+
`Missing required 'selector' argument for tool ${toolName}.`
271+
);
274272
}
275273
try {
276274
locator = page.locator(identifier);
277275
} catch (locatorError) {
278-
throw new Error(`Failed to create locator for selector '${identifier}': ${locatorError instanceof Error ? locatorError.message : String(locatorError)}`);
276+
throw new Error(
277+
`Failed to create locator for selector '${identifier}': ${
278+
locatorError instanceof Error
279+
? locatorError.message
280+
: String(locatorError)
281+
}`
282+
);
279283
}
280284
} else if (validatedArgs?.ref) {
281285
identifier = validatedArgs.ref;
282286
identifierType = "ref";
283287
if (!identifier) {
284-
throw new Error(`Missing required 'ref' argument for tool ${toolName}.`);
288+
throw new Error(
289+
`Missing required 'ref' argument for tool ${toolName}.`
290+
);
285291
}
286292
if (!snapshot) {
287-
throw new Error(`Cannot resolve ref '${identifier}' because no snapshot is available for session ${this.currentSessionId}. Capture a snapshot or ensure one exists.`);
293+
throw new Error(
294+
`Cannot resolve ref '${identifier}' because no snapshot is available for session ${this.currentSessionId}. Capture a snapshot or ensure one exists.`
295+
);
288296
}
289297
try {
290298
// Resolve using the snapshot we just retrieved
291299
locator = snapshot.refLocator(identifier);
292300
} catch (locatorError) {
293301
// Use the existing snapshot identifier in the error
294302
throw new Error(
295-
`Failed to resolve ref ${identifier} using existing snapshot ${initialSnapshotIdentifier} before action attempt: ${locatorError instanceof Error ? locatorError.message : String(locatorError)}`
303+
`Failed to resolve ref ${identifier} using existing snapshot ${initialSnapshotIdentifier} before action attempt: ${
304+
locatorError instanceof Error
305+
? locatorError.message
306+
: String(locatorError)
307+
}`
296308
);
297309
}
298310
} else if (requiresIdentifier) {
299311
// If neither ref nor selector is provided, but one is required
300-
throw new Error(`Missing required 'ref' or 'selector' argument for tool ${toolName}.`);
312+
throw new Error(
313+
`Missing required 'ref' or 'selector' argument for tool ${toolName}.`
314+
);
301315
} else {
302-
// No identifier needed or provided
303-
identifierType = "none"; // Explicitly set to none
316+
// No identifier needed or provided
317+
identifierType = "none"; // Explicitly set to none
304318
}
305319

306320
// --- Single Attempt ---
307321
try {
308322
// Pass page, the used identifier (selector or ref), args, the resolved locator, and identifierType
309-
const actionFnResult = await actionFn(page, identifier, validatedArgs, locator, identifierType);
323+
const actionFnResult = await actionFn(
324+
page,
325+
identifier,
326+
validatedArgs,
327+
locator,
328+
identifierType
329+
);
310330

311331
if (typeof actionFnResult === "string") {
312332
resultText = actionFnResult;
@@ -330,7 +350,9 @@ export class Context {
330350
return { resultText, actionResult };
331351
} catch (error: any) {
332352
throw new Error(
333-
`Action ${toolName} failed: ${error instanceof Error ? error.message : String(error)}`
353+
`Action ${toolName} failed: ${
354+
error instanceof Error ? error.message : String(error)
355+
}`
334356
);
335357
}
336358
}
@@ -340,7 +362,8 @@ export class Context {
340362
let initialPage: Page | null = null;
341363
let initialBrowser: BrowserSession["browser"] | null = null;
342364
let toolResultFromHandle: ToolResult | null = null; // Legacy handle result
343-
let finalResult: CallToolResult = { // Initialize finalResult here
365+
let finalResult: CallToolResult = {
366+
// Initialize finalResult here
344367
content: [{ type: "text", text: `Initialization error for ${toolName}` }],
345368
isError: true,
346369
};
@@ -406,48 +429,49 @@ export class Context {
406429
}
407430
}
408431

409-
let executionResultText = "";
432+
let toolActionOutput: ToolActionResult | undefined = undefined; // New variable to store direct tool action output
410433
let actionSucceeded = false;
411434
let shouldCaptureSnapshotAfterAction = false;
412435
let postActionSnapshot: PageSnapshot | undefined = undefined;
413436

414437
try {
415-
let actionToRun: (() => Promise<ToolActionResult>) | undefined = undefined;
438+
let actionToRun: (() => Promise<ToolActionResult>) | undefined =
439+
undefined;
416440
let shouldCaptureSnapshot = false;
417441

418442
try {
419-
if ('handle' in tool && typeof tool.handle === 'function') {
420-
toolResultFromHandle = await tool.handle(this as any, validatedArgs);
421-
actionToRun = toolResultFromHandle?.action;
422-
shouldCaptureSnapshot = toolResultFromHandle?.captureSnapshot ?? false;
423-
shouldCaptureSnapshotAfterAction = shouldCaptureSnapshot;
443+
if ("handle" in tool && typeof tool.handle === "function") {
444+
toolResultFromHandle = await tool.handle(this as any, validatedArgs);
445+
actionToRun = toolResultFromHandle?.action;
446+
shouldCaptureSnapshot =
447+
toolResultFromHandle?.captureSnapshot ?? false;
448+
shouldCaptureSnapshotAfterAction = shouldCaptureSnapshot;
424449
} else {
425-
throw new Error(`Tool ${toolName} could not be handled (no handle method).`);
450+
throw new Error(
451+
`Tool ${toolName} could not be handled (no handle method).`
452+
);
426453
}
427454

428455
if (actionToRun) {
429-
const actionResult = await actionToRun();
430-
if (actionResult?.content) {
431-
executionResultText = actionResult.content
432-
.map((c: { type: string; text?: string }) => c.type === "text" ? c.text : `[${c.type}]`)
433-
.filter(Boolean)
434-
.join(" ") || `${toolName} action completed.`;
435-
} else {
436-
executionResultText = `${toolName} action completed successfully.`;
437-
}
438-
actionSucceeded = true;
456+
toolActionOutput = await actionToRun();
457+
actionSucceeded = true;
439458
} else {
440-
throw new Error(`Tool ${toolName} handled without action.`);
459+
throw new Error(`Tool ${toolName} handled without action.`);
441460
}
442461
} catch (error) {
443-
process.stderr.write(`${logPrefix} Error executing tool ${toolName}: ${error instanceof Error ? error.message : String(error)}\\n`); // Changed and added newline
444-
// --- LOG STACK TRACE ---
462+
process.stderr.write(
463+
`${logPrefix} Error executing tool ${toolName}: ${
464+
error instanceof Error ? error.message : String(error)
465+
}\\n`
466+
);
445467
if (error instanceof Error && error.stack) {
446-
process.stderr.write(`${logPrefix} Stack Trace: ${error.stack}\\n`);
468+
process.stderr.write(`${logPrefix} Stack Trace: ${error.stack}\\n`);
447469
}
448470
// -----------------------
449471
finalResult = this.createErrorResult(
450-
`Execution failed: ${error instanceof Error ? error.message : String(error)}`,
472+
`Execution failed: ${
473+
error instanceof Error ? error.message : String(error)
474+
}`,
451475
toolName
452476
);
453477
actionSucceeded = false;
@@ -465,12 +489,22 @@ export class Context {
465489
try {
466490
postActionSnapshot = await this.captureSnapshot();
467491
if (postActionSnapshot) {
468-
process.stderr.write(`[Context.run ${toolName}] Added snapshot to final result text.\n`);
492+
process.stderr.write(
493+
`[Context.run ${toolName}] Added snapshot to final result text.\n`
494+
);
469495
} else {
470-
process.stderr.write(`[Context.run ${toolName}] WARN: Snapshot was expected after action but failed to capture.\n`); // Keep warning
496+
process.stderr.write(
497+
`[Context.run ${toolName}] WARN: Snapshot was expected after action but failed to capture.\n`
498+
); // Keep warning
471499
}
472500
} catch (postSnapError) {
473-
process.stderr.write(`[Context.run ${toolName}] WARN: Error capturing post-action snapshot: ${postSnapError instanceof Error ? postSnapError.message : String(postSnapError)}\n`); // Keep warning
501+
process.stderr.write(
502+
`[Context.run ${toolName}] WARN: Error capturing post-action snapshot: ${
503+
postSnapError instanceof Error
504+
? postSnapError.message
505+
: String(postSnapError)
506+
}\n`
507+
); // Keep warning
474508
}
475509
} else if (
476510
actionSucceeded &&
@@ -481,49 +515,78 @@ export class Context {
481515
}
482516

483517
if (actionSucceeded) {
518+
const finalContentItems: (TextContent | ImageContent)[] = [];
519+
520+
// 1. Add content from the tool action itself
521+
if (toolActionOutput?.content && toolActionOutput.content.length > 0) {
522+
finalContentItems.push(...toolActionOutput.content);
523+
} else {
524+
// If toolActionOutput.content is empty/undefined but action succeeded,
525+
// provide a generic success message.
526+
finalContentItems.push({ type: "text", text: `${toolName} action completed successfully.` });
527+
}
528+
529+
// 2. Prepare and add additional textual information (URL, Title, Snapshot)
530+
const additionalInfoParts: string[] = [];
484531
const currentPage = await this.getActivePage();
485-
let finalOutputText = executionResultText; // Start with execution text
486532

487533
if (currentPage) {
488534
try {
489535
const url = currentPage.url();
490536
const title = await currentPage
491537
.title()
492538
.catch(() => "[Error retrieving title]");
493-
finalOutputText += `\n\n- Page URL: ${url}\n- Page Title: ${title}`;
539+
additionalInfoParts.push(`- Page URL: ${url}`);
540+
additionalInfoParts.push(`- Page Title: ${title}`);
494541
} catch (pageStateError) {
495-
finalOutputText +=
496-
"\n\n- [Error retrieving page state after action]";
542+
additionalInfoParts.push(
543+
"- [Error retrieving page state after action]"
544+
);
497545
}
498546
} else {
499-
finalOutputText += "\n\n- [Page unavailable after action]";
547+
additionalInfoParts.push("- [Page unavailable after action]");
500548
}
501549

502550
const snapshotToAdd = postActionSnapshot;
503551
if (snapshotToAdd) {
504-
finalOutputText += `\n\n- Page Snapshot\n\`\`\`yaml\n${snapshotToAdd.text()}\n\`\`\`\n`;
552+
additionalInfoParts.push(
553+
`- Page Snapshot\n\`\`\`yaml\n${snapshotToAdd.text()}\n\`\`\`\n`
554+
);
505555
} else {
506-
finalOutputText += `\n\n- [No relevant snapshot available after action]`;
556+
additionalInfoParts.push(
557+
`- [No relevant snapshot available after action]`
558+
);
559+
}
560+
561+
// 3. Add the additional information as a new TextContent item if it's not empty
562+
if (additionalInfoParts.length > 0) {
563+
// Add leading newlines if there's preceding content, to maintain separation
564+
const additionalInfoText = (finalContentItems.length > 0 ? "\\n\\n" : "") + additionalInfoParts.join("\\n");
565+
finalContentItems.push({ type: "text", text: additionalInfoText });
507566
}
508567

509568
finalResult = {
510-
content: [{ type: "text", text: finalOutputText }],
569+
content: finalContentItems,
511570
isError: false,
512571
};
513572
} else {
514573
// Error result is already set in catch block, but ensure it IS set.
515574
if (!finalResult || !finalResult.isError) {
516-
finalResult = this.createErrorResult(
517-
`Unknown error occurred during ${toolName}`,
518-
toolName
519-
);
575+
finalResult = this.createErrorResult(
576+
`Unknown error occurred during ${toolName}`,
577+
toolName
578+
);
520579
}
521580
}
522581
return finalResult;
523582
}
524583
} catch (error) {
525-
process.stderr.write(`${logPrefix} Error running tool ${toolName}: ${error instanceof Error ? error.message : String(error)}\n`);
584+
process.stderr.write(
585+
`${logPrefix} Error running tool ${toolName}: ${
586+
error instanceof Error ? error.message : String(error)
587+
}\n`
588+
);
526589
throw error;
527590
}
528591
}
529-
}
592+
}

0 commit comments

Comments
 (0)