aboutsummaryrefslogtreecommitdiff
path: root/src/index.ts
diff options
context:
space:
mode:
authoramankumarsingh77 <amankumarsingh7702@gmal.com>2026-05-05 23:23:59 +0530
committeramankumarsingh77 <amankumarsingh7702@gmal.com>2026-05-05 23:23:59 +0530
commit20ceaa1c6ade4950ed7d16caa5269ad2f4805db4 (patch)
treedf4459a67b08533704f0dd2a1c85010578471917 /src/index.ts
parente42d9538ad5dafd4e11cf8f5a3912ebf0763d770 (diff)
feat: tab ownership — isolate harness tabs from the user's browsing
Track which page targets this harness session opened so the agent can never attach to, switch to, or close a tab the user opened themselves. New tabs are spawned in a dedicated Chrome window (newWindow:true on first open, openerId chaining after) so harness activity stays visually separate. - new OwnershipRegistry (src/cdp/ownership.ts) tracks owned targetIds and the harness window seed; mutations notify a single listener that persists to BrowserState (ownedTargetIds, harnessWindowTargetId) - attachFirstPage prefers an already-owned live target; otherwise opens a fresh harness-owned tab in its own window instead of grabbing whatever page the user is on - session subscribes Target.setDiscoverTargets and reaps targetDestroyed events so the registry stays in sync with reality - listTabs returns owned:boolean per tab and reconciles dead ids - browser_list_tabs defaults to scope:"owned"; pass scope:"all" to inspect the user's other tabs (read-only — they cannot be switched to or closed) - browser_switch_tab refuses non-owned tabs, with a clear remediation hint - browser_new_tab opens inside the harness window via openerId, falling back to a fresh window when the seed has been closed - new browser_close_tab tool — owned tabs only, supports unique hex prefix Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'src/index.ts')
-rw-r--r--src/index.ts18
1 files changed, 17 insertions, 1 deletions
diff --git a/src/index.ts b/src/index.ts
index ee59f10..c2aa2d0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -98,7 +98,23 @@ export default function browserHarnessExtension(pi: ExtensionAPI): void {
pi.on("session_start", async (_event, ctx) => {
state = restoreState(ctx, state.namespace);
- client = createBrowserClient({ namespace: state.namespace });
+ const initialOwnership: { ownedTargetIds?: ReadonlyArray<string>; harnessWindowTargetId?: string } = {};
+ if (state.ownedTargetIds !== undefined) initialOwnership.ownedTargetIds = state.ownedTargetIds;
+ if (state.harnessWindowTargetId !== undefined) initialOwnership.harnessWindowTargetId = state.harnessWindowTargetId;
+ client = createBrowserClient({
+ namespace: state.namespace,
+ ...(Object.keys(initialOwnership).length > 0 ? { initialOwnership } : {}),
+ onOwnershipChange: (snap) => {
+ state = {
+ ...state,
+ ownedTargetIds: snap.ownedTargetIds,
+ ...(snap.harnessWindowTargetId !== undefined
+ ? { harnessWindowTargetId: snap.harnessWindowTargetId }
+ : {}),
+ };
+ persistState(pi, state);
+ },
+ });
// failure is fine — surfaced via tool errors when the agent first tries to use the browser
await client.start();
if (!toolsRegistered) {