aboutsummaryrefslogtreecommitdiff
path: root/src/domains
diff options
context:
space:
mode:
Diffstat (limited to 'src/domains')
-rw-r--r--src/domains/isolated-tab.ts27
-rw-r--r--src/domains/navigate.ts31
-rw-r--r--src/domains/setup.ts6
3 files changed, 29 insertions, 35 deletions
diff --git a/src/domains/isolated-tab.ts b/src/domains/isolated-tab.ts
index 73ed158..7969c6b 100644
--- a/src/domains/isolated-tab.ts
+++ b/src/domains/isolated-tab.ts
@@ -13,7 +13,7 @@
import type { BrowserClient } from "../client";
import { type Result, err, ok } from "../util/result";
import { safeJs } from "../util/js-template";
-import { bindHarnessWindowId } from "../cdp/window-binding";
+import { ensureHarnessWindow, openHarnessTab } from "../cdp/target-factory";
import type { ToolErr } from "../util/tool";
/** A tab attached by its own sessionId, ready to drive via callOnTarget. */
@@ -32,20 +32,19 @@ const toToolErr = (message: string, kind: ToolErr["kind"] = "cdp_error"): ToolEr
* sessionId. Does not navigate. The caller MUST `closeIsolatedTab` when done.
*/
export const openIsolatedTab = async (client: BrowserClient): Promise<Result<IsolatedTab, ToolErr>> => {
- const hw = client.ownership().harnessWindow();
- const createParams: Record<string, unknown> = hw
- ? { url: "about:blank", openerId: hw }
- : { url: "about:blank", newWindow: true };
-
- const created = await client.session().callBrowser("Target.createTarget", createParams);
- if (!created.success) return err(toToolErr(created.error.message));
- const { targetId } = created.data as { targetId: string };
-
- if (createParams["newWindow"]) {
- client.ownership().setHarnessWindow(targetId);
- await bindHarnessWindowId(client, targetId);
+ // Routed through the target factory so an isolated tab lands in the pinned
+ // profile like every other harness tab — otherwise a search or page read
+ // would run with a different profile's cookies than the visible tabs.
+ const window = await ensureHarnessWindow(client);
+ if (!window.success) return err(toToolErr(window.error.message));
+ let targetId: string;
+ if (window.data.freshlyCreated) {
+ targetId = window.data.targetId;
+ } else {
+ const opened = await openHarnessTab(client, window.data.targetId);
+ if (!opened.success) return err(toToolErr(opened.error.message));
+ targetId = opened.data;
}
- client.ownership().add(targetId);
const attached = await client.session().callBrowser("Target.attachToTarget", { targetId, flatten: true });
if (!attached.success) {
diff --git a/src/domains/navigate.ts b/src/domains/navigate.ts
index e402efc..aa0d671 100644
--- a/src/domains/navigate.ts
+++ b/src/domains/navigate.ts
@@ -4,7 +4,7 @@ import { type Result, err, ok } from "../util/result";
import { defineBrowserTool, type ToolErr, type ToolOk } from "../util/tool";
import { applyTruncation } from "../util/truncate";
import { safeJs } from "../util/js-template";
-import { bindHarnessWindowId } from "../cdp/window-binding";
+import { ensureHarnessWindow, openHarnessTab } from "../cdp/target-factory";
const NavigateArgs = Type.Object({
url: Type.String({ description: "Full URL to navigate to (e.g. https://github.com)" }),
@@ -89,28 +89,21 @@ export const openUrlsTool = defineBrowserTool({
// (fresh/closed), create the FIRST url as a new dedicated window (binding
// its windowId) and open the rest as children of it — a parallel
// newWindow:true per url would otherwise scatter tabs across N windows.
- let seed = client.ownership().harnessWindow();
- let seedResult: TabResult | undefined;
- if (!seed) {
- const url0 = args.urls[0];
- if (url0 === undefined) return err({ kind: "invalid_state", message: "no URLs provided" });
- const r0 = await client.session().callBrowser("Target.createTarget", { url: "about:blank", newWindow: true });
- if (!r0.success) return err({ kind: "cdp_error", message: r0.error.message });
- seed = (r0.data as { targetId: string }).targetId;
- client.ownership().setHarnessWindow(seed);
- await bindHarnessWindowId(client, seed);
- client.ownership().add(seed);
- seedResult = { url: url0, targetId: seed, ok: true };
- }
- const seededWindow = seed;
+ const url0 = args.urls[0];
+ if (url0 === undefined) return err({ kind: "invalid_state", message: "no URLs provided" });
+ const window = await ensureHarnessWindow(client);
+ if (!window.success) return err({ kind: "cdp_error", message: window.error.message });
+ const seededWindow = window.data.targetId;
+ // A freshly created window's seed tab serves as the first URL's tab.
+ const seedResult: TabResult | undefined = window.data.freshlyCreated
+ ? { url: url0, targetId: seededWindow, ok: true }
+ : undefined;
const created = await Promise.all(args.urls.map(async (url, i): Promise<TabResult> => {
// Reuse the seed tab (created above) for the first url instead of duplicating it.
if (i === 0 && seedResult) return seedResult;
- const r = await client.session().callBrowser("Target.createTarget", { url: "about:blank", openerId: seededWindow });
+ const r = await openHarnessTab(client, seededWindow);
if (!r.success) return { url, targetId: "", ok: false, error: r.error.message };
- const c = r.data as { targetId: string };
- client.ownership().add(c.targetId);
- return { url, targetId: c.targetId, ok: true };
+ return { url, targetId: r.data, ok: true };
}));
// Phase 2: attach + navigate each created tab in parallel.
let completed = 0;
diff --git a/src/domains/setup.ts b/src/domains/setup.ts
index bbd767f..2ddd739 100644
--- a/src/domains/setup.ts
+++ b/src/domains/setup.ts
@@ -31,8 +31,10 @@ export const setupTool = defineBrowserTool({
// ensureAlive:false — this tool IS the setup; it must bypass the socket guard
ensureAlive: false,
renderCall: () => new Text("🔧 Initializing browser...", 0, 0),
- async handler(_args, { client }): Promise<Result<ToolOk, ToolErr>> {
- const result = await performSetup(client);
+ async handler(_args, { client, extensionCtx }): Promise<Result<ToolOk, ToolErr>> {
+ // extensionCtx carries the UI, so the agent calling browser_setup gets the
+ // same first-run profile picker the user gets from /browser-setup.
+ const result = await performSetup(client, extensionCtx);
if (result.success) {
return ok({ text: result.data });
}