browser-pilot-cli 0.2.2 → 0.3.0-rc.1

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.
@@ -290,13 +290,18 @@ async function createTarget(params) {
290
290
  if (params.url !== "about:blank") throw new Error("Managed targets must start at about:blank");
291
291
  const newWindow = params.newWindow === true;
292
292
  const windowId = params.windowId;
293
+ const browserContextId = params.browserContextId;
293
294
  if (newWindow === (windowId !== void 0)) throw new Error("Exactly one managed target window selector is required");
294
295
  if (windowId !== void 0 && (!Number.isSafeInteger(windowId) || Number(windowId) < 0)) {
295
296
  throw new Error("Invalid managed target window ID");
296
297
  }
298
+ if (browserContextId !== void 0 && (typeof browserContextId !== "string" || browserContextId.length === 0 || browserContextId.length > 1024)) {
299
+ throw new Error("Invalid managed target browser context ID");
300
+ }
297
301
  const created = await cdp.send("Target.createTarget", {
298
302
  url: "about:blank",
299
- ...newWindow ? { newWindow: true } : { windowId }
303
+ ...newWindow ? { newWindow: true } : { windowId },
304
+ ...browserContextId ? { browserContextId } : {}
300
305
  });
301
306
  if (typeof created?.targetId !== "string" || created.targetId.length === 0) {
302
307
  throw new Error("Chrome returned an invalid managed target ID");
@@ -171,24 +171,37 @@ Broker and associated with exactly one ClientPrincipal after initialization.
171
171
 
172
172
  ### BrowserInstance
173
173
 
174
- One running, discoverable Chromium-family browser process identified by a
175
- process-stable identity, product, profile path, debugging endpoint, and
176
- connection generation.
174
+ One running, discoverable Chromium-family browser endpoint identified by a
175
+ process-stable identity, product, user-data root, debugging endpoint, and
176
+ connection generation. One endpoint may expose several simultaneously live
177
+ Chrome Profile contexts; those are not separate BrowserInstances.
178
+
179
+ ### ProfileContext
180
+
181
+ One live regular browser context inside a BrowserInstance. Its public
182
+ `profileContextId` is opaque and scoped to the current browser connection
183
+ generation; the raw CDP browser-context ID is internal. A ProfileContext has a
184
+ connection-scoped neutral label, optional verified display metadata, bounded
185
+ representative tabs, and current tab counts. Passive discovery never opens a
186
+ tab to infer its display name. Reconnect invalidates every ProfileContext ID.
177
187
 
178
188
  ### BrowserWorkspace
179
189
 
180
190
  A logical browser working set owned by one ClientPrincipal. It owns one or more
181
- ManagedTabSets and transient scoped state. Workspaces live in Broker memory by
182
- default and are recreated after Broker restart; Agent adapters map their own
183
- active lifecycle to this object.
191
+ ManagedTabSets, an optional selected ProfileContext, and transient scoped state.
192
+ Workspaces live in Broker memory by default and are recreated after Broker
193
+ restart; Agent adapters map their own active lifecycle to this object. Profile
194
+ selection is never persisted as a global browser default.
184
195
 
185
196
  ### ManagedTabSet
186
197
 
187
198
  The logical collection of Broker-created tabs used for isolated Agent work. It
188
199
  is normally backed by a dedicated visible Pilot window because Browser Pilot
189
200
  does not use extension-only native tab-group APIs. New task navigation and
190
- owned popups stay in this set by default. Workspace cleanup may close this set,
191
- but never user-owned tabs outside it.
201
+ owned popups stay in this set by default. A ManagedTabSet is permanently bound
202
+ to at most one ProfileContext; a Workspace that creates managed work in multiple
203
+ Profiles owns one set per Profile. Workspace cleanup may close these sets, but
204
+ never user-owned tabs outside them.
192
205
 
193
206
  ### ControlLease
194
207
 
@@ -207,7 +220,8 @@ inside the running bridge.
207
220
  ### ControlledTarget
208
221
 
209
222
  A target addressable by a Workspace. Its origin is `managed`, `managed_popup`,
210
- or `user_tab`, and it records an optional ManagedTabSet. A user tab retains user
223
+ or `user_tab`, and it records its ProfileContext and an optional ManagedTabSet.
224
+ A user tab retains user
211
225
  ownership: releasing a Workspace removes Broker mappings without closing the
212
226
  tab. Every result identifies its Workspace, Lease, opaque controlled-target ID,
213
227
  URL, origin, and browser connection generation. Raw CDP target IDs are not a
@@ -397,6 +411,23 @@ transient mappings and rules but leaves user tabs open. Closing a user tab
397
411
  always requires an explicit target-specific command; bulk cleanup applies only
398
412
  to ManagedTabSets.
399
413
 
414
+ ### FLOW-6 Multiple live Chrome Profiles
415
+
416
+ `browser.tabs.list` inventories eligible tabs from every Profile context exposed
417
+ by the selected BrowserInstance. Existing targets are immediately controllable
418
+ and carry an opaque `profileContextId`. New managed work resolves a Profile from
419
+ an explicit context, the current active target, the Workspace selection, or a
420
+ single available context, in that order. Multiple contexts without an anchor
421
+ return `profile_selection_required` before creating a target. The Agent host
422
+ asks the user which Profile to use; Browser Pilot does not add an approval UI.
423
+
424
+ `browser.profiles.list` is passive and returns bounded, connection-scoped
425
+ summaries. `browser.profiles.select` updates only the owning Workspace. A Chrome
426
+ reconnect invalidates old ProfileContext IDs and requires relisting and
427
+ reselection. Selection clears only the Lease's logical active-target anchor; it
428
+ does not release control, activate a tab, or change Chrome focus. The exact
429
+ creation and compatibility algorithm is frozen in `docs/plans/profile-context-routing.md`.
430
+
400
431
  ## Machine Protocol
401
432
 
402
433
  `bridge --stdio` uses JSON-RPC 2.0 over newline-delimited UTF-8 JSON. Each line
@@ -448,6 +479,12 @@ resource limits. Pending ordinary and out-of-band control calls have separate
448
479
  bounds so overload cannot create an unbounded Promise queue or prevent dialog
449
480
  and cancellation control from overtaking a blocked action.
450
481
 
482
+ Protocol 1.2 adds connection-generation-scoped ProfileContext discovery and
483
+ selection, Profile identity on tab inventory, and context-aware managed target
484
+ creation. Protocol 1.0/1.1 clients retain existing-target control and automatic
485
+ single-context behavior, but an ambiguous multi-context open fails rather than
486
+ silently selecting a Profile.
487
+
451
488
  ### Tool Contract
452
489
 
453
490
  `tools/list` is the canonical machine-readable manifest. Every tool declares:
@@ -566,6 +603,22 @@ Artifacts. Raw CDP is never listed. `eval` requires `developer.eval`.
566
603
  - **BR-27:** Bulk cleanup and `close --all` default to the ManagedTabSet. No
567
604
  bulk operation closes user tabs outside it. Each user tab requires an
568
605
  explicit target-specific close command.
606
+ - **BR-29:** Runtime Profile contexts are discovered from live target metadata,
607
+ not only `Target.getBrowserContexts`. Public ProfileContext IDs never expose
608
+ raw CDP identifiers and are invalid after browser reconnect.
609
+ - **BR-30:** Profile listing is passive. It cannot create, attach, activate, or
610
+ navigate a target solely to infer a Profile name. Unverified names are
611
+ omitted instead of guessed.
612
+ - **BR-31:** New-target Profile routing follows explicit context, active target,
613
+ Workspace selection, then single available context. Multiple unanchored
614
+ contexts fail with `profile_selection_required` before browser dispatch.
615
+ - **BR-32:** Every ManagedTabSet is bound to at most one ProfileContext. Managed
616
+ target creation verifies the returned context before public registration, and
617
+ fallback-created targets enter janitor ownership before they become
618
+ addressable.
619
+ - **BR-33:** Switching to an existing target may update the owning Workspace's
620
+ selected ProfileContext, but never prevents inventory or control of targets
621
+ in other Profiles. No Profile selection is a permission grant.
569
622
 
570
623
  ## Command Reliability
571
624
 
@@ -782,10 +835,15 @@ the first new download session is configured.
782
835
  ## Browser Discovery and Setup
783
836
 
784
837
  Discovery returns every supported local browser candidate with product,
785
- channel, profile, process state, remote-debugging state, authorization state,
838
+ channel, user-data root, process state, remote-debugging state, authorization state,
786
839
  and structured remediation. It does not silently select the first filesystem
787
840
  match when several viable instances exist.
788
841
 
842
+ After connection, Profile discovery separately returns every live regular
843
+ Profile context on that endpoint. Browser candidates and Profile contexts are
844
+ different layers: selecting Chrome Stable does not silently select one of its
845
+ simultaneously open Profiles.
846
+
789
847
  The client may select an instance explicitly or use a deterministic persisted
790
848
  preference. Setup that requires Chrome UI remains a user-visible action. Browser
791
849
  Pilot cannot bypass Chrome authorization and must not simulate consent.
@@ -824,6 +882,7 @@ and optional structured remediation. Required codes include:
824
882
  protocol_incompatible not_initialized capability_denied
825
883
  browser_not_found browser_not_authorized browser_disconnected
826
884
  broker_in_use
885
+ profile_selection_required profile_context_stale profile_context_unavailable
827
886
  workspace_not_found lease_expired target_not_owned
828
887
  target_busy stale_ref command_cancelled
829
888
  action_not_verified command_expired unknown_outcome
@@ -900,8 +959,14 @@ fields, never on message text.
900
959
  - **FR-8 Browser reliability:** Browser capability improvements do not alter the
901
960
  public lifecycle and isolation contract.
902
961
  - **AC-8:** Observation and action conformance tests pass against the public
903
- tool interface, including frame, navigation, stale-ref, obstruction, and
904
- input readback cases.
962
+ tool interface, including frame, navigation, stale-ref, obstruction, and
963
+ input readback cases.
964
+ - **FR-9 Multi-Profile routing:** One endpoint can expose all eligible tabs from
965
+ multiple live Chrome Profiles without ambiguous new-target placement.
966
+ - **AC-9:** Existing tabs remain directly controllable; every tab result has a
967
+ connection-scoped ProfileContext ID; ambiguous open returns
968
+ `profile_selection_required`; explicit or inferred routing is verified; and
969
+ reconnect requires relisting rather than reusing stale Profile IDs.
905
970
 
906
971
  ## Non-Functional Requirements
907
972
 
@@ -949,6 +1014,12 @@ fields, never on message text.
949
1014
  version output, bridge initialization, private Broker startup, and cleanup.
950
1015
  Native verification additionally checks manifest hashes, signature state,
951
1016
  private child roles, and archive checksums on each target platform.
1017
+ 8. Multi-Profile tests cover passive discovery, explicit and inferred
1018
+ selection, multiple ManagedTabSets, direct-create fallback, janitor adoption,
1019
+ concurrent Workspace routing, reconnect invalidation, and protocol 1.1
1020
+ ambiguous-open failure. Real Chrome acceptance verifies at least two live
1021
+ Profiles on one endpoint without reconnecting or requesting authorization
1022
+ again.
952
1023
 
953
1024
  ## Implementation Boundary
954
1025
 
@@ -105,7 +105,7 @@ exception because a browser dialog can pause the command that caused it.
105
105
  `initialize` must be the first successful request on a bridge Connection:
106
106
 
107
107
  ```json
108
- {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"client":{"id":"com.example.agent","name":"Example Agent","version":"2.0.0","instanceId":"install:01J..."},"protocol":{"min":{"major":1,"minor":1},"max":{"major":1,"minor":1}},"requestedCapabilities":["browser.control","workspace.manage","observation.read","action.input","artifact.read","event.read"],"launchMode":"embedded","limits":{"maxMessageBytes":1048576,"maxResultBytes":4194304}}}
108
+ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"client":{"id":"com.example.agent","name":"Example Agent","version":"2.0.0","instanceId":"install:01J..."},"protocol":{"min":{"major":1,"minor":1},"max":{"major":1,"minor":2}},"requestedCapabilities":["browser.control","workspace.manage","observation.read","action.input","artifact.read","event.read"],"launchMode":"embedded","limits":{"maxMessageBytes":1048576,"maxResultBytes":4194304}}}
109
109
  ```
110
110
 
111
111
  The response returns the selected protocol, supported and granted
@@ -127,6 +127,12 @@ send `limits.maxMessageBytes` and `limits.maxResultBytes`; each must be from
127
127
  and applies only to that bridge Connection. `maxArtifactBytes` and
128
128
  `eventJournalSize` remain service resource limits, not client preferences.
129
129
 
130
+ Protocol 1.2 adds connection-scoped Chrome Profile routing. It exposes
131
+ `browser.profiles.list`, `browser.profiles.select`, and `profileContextId` on
132
+ tab inventory and new-target operations. Protocol 1.0/1.1 clients retain their
133
+ existing tools; the Broker hides 1.2-only operations and rejects a manually
134
+ constructed Profile call with `protocol_incompatible`.
135
+
130
136
  The initialize request and response use the service's fixed bootstrap limits.
131
137
  The bridge switches limits only after the successful response has been written,
132
138
  and waits for that switch before parsing a pipelined next line. Oversized
@@ -169,11 +175,13 @@ also provide `clientKey` to make active Workspace creation idempotent within
169
175
  their Principal; reusing a key for another browser fails. Without a browser ID,
170
176
  the Broker prefers its first ready connection and otherwise uses its selected
171
177
  stable-order browser binding. It returns a Workspace, its default logical
172
- ManagedTabSet, and an `eventCursor`. `workspaces/get` returns the current cursor
173
- as a recovery baseline. Creating a Workspace does not itself create a browser
178
+ ManagedTabSet, the complete `managedTabSets` array, and an `eventCursor`.
179
+ `workspaces/get` also returns the Workspace's transient
180
+ `selectedProfileContextId` when one is selected and the current cursor as a
181
+ recovery baseline. Creating a Workspace does not itself create a browser
174
182
  connection or window. Call the Workspace-scoped `browser.connect` tool to
175
183
  request Chrome authorization; the first managed navigation then creates the
176
- dedicated browser window.
184
+ dedicated browser window in its resolved Profile context.
177
185
 
178
186
  `leases/create` accepts a `workspaceId` and optional `ttlMs`. Protocol 1.1
179
187
  clients may also provide a `clientKey`; repeating that key on the same live
@@ -243,6 +251,43 @@ browser dispatch becomes `unknown_outcome` and is never automatically replayed.
243
251
  Known tool failures are stored as a completed Command with a nested JSON-RPC
244
252
  `error`; the original call also returns that error normally.
245
253
 
254
+ ### Chrome Profile routing
255
+
256
+ One connected Chrome endpoint may expose ordinary tabs from several live
257
+ Profiles. Browser Pilot authorizes the endpoint once; it does not reconnect or
258
+ show another Chrome Allow dialog per Profile. `browser.tabs.list` returns every
259
+ eligible managed and user-opened tab across those contexts, each with an opaque
260
+ `profileContextId`. Existing tabs can be selected and controlled immediately;
261
+ Profile selection is routing for new managed targets, not a permission grant.
262
+
263
+ Protocol 1.2 hosts should use this flow when creating independent work:
264
+
265
+ 1. Call `browser.profiles.list`. It is passive and returns neutral labels,
266
+ bounded representative tabs, counts, and the Workspace selection.
267
+ 2. If there is one Profile, `browser.open` can infer it. If there are several
268
+ and no current target or Workspace selection, ask the user which Profile to
269
+ use.
270
+ 3. Call `browser.profiles.select` with the returned opaque ID, or pass that ID
271
+ directly as `browser.open.arguments.profileContextId`.
272
+ 4. Treat IDs as scoped to the current browser connection generation. After
273
+ reconnect, relist Profiles, tabs, frames, and Observations.
274
+
275
+ Profile names are included only when Browser Pilot has verified them without a
276
+ visible discovery tab; otherwise hosts present the neutral label and
277
+ representative page titles/URLs. Never expose or persist raw CDP context IDs.
278
+ New-target routing uses an explicit call argument, the Lease's logical active
279
+ target, the Workspace selection, or the only available context, in that order.
280
+ `browser.profiles.select` clears only the logical active-target anchor so its
281
+ selection applies to the next new managed target; it does not release control,
282
+ activate a user tab, or change Chrome focus.
283
+
284
+ With multiple unanchored contexts, `browser.open` fails before browser dispatch
285
+ with `profile_selection_required`. Relist on `profile_context_stale`; on
286
+ `profile_context_unavailable`, follow its setup remediation. An
287
+ `unknown_outcome` during Profile-window creation means Chrome may have created
288
+ a target that Browser Pilot could not prove absent; list tabs and Profiles and
289
+ inspect current state before deciding whether to retry.
290
+
246
291
  `browser.click` returns a new Observation plus bounded evidence. For example,
247
292
  a checkbox may return:
248
293
 
@@ -407,8 +452,8 @@ instead of automatically repeating the same action or navigation.
407
452
 
408
453
  `connection.lost` keeps the last connection generation and causes later browser
409
454
  tools to fail with retryable `browser_disconnected`. The daemon passively
410
- refreshes the originally selected profile's endpoint metadata and does not
411
- switch profiles or open a WebSocket. The host calls `browser.connect` when it is
455
+ refreshes the selected browser endpoint metadata and does not select a Profile
456
+ context or open a WebSocket. The host calls `browser.connect` when it is
412
457
  ready to request reconnection. `connection.restored` then carries a strictly
413
458
  newer generation. Existing Workspaces
414
459
  and active Leases remain valid, but old target IDs, frame IDs, CDP sessions,
@@ -570,7 +615,7 @@ pattern into their own integration and continue treating this document plus the
570
615
  runtime manifest as authoritative.
571
616
 
572
617
  The shared example launches an absolute executable path without a shell,
573
- negotiates protocol 1.1, discovers tools at runtime, and maps a host work scope
618
+ negotiates protocol 1.1 through 1.2, discovers tools at runtime, and maps a host work scope
574
619
  to a Workspace plus its active invocation to a renewable Lease. It never stores
575
620
  an active tab, frame, Observation, ref, credential, rule, Artifact, or event
576
621
  cursor on disk. Host tool-call IDs become Command and idempotency identities;
@@ -595,7 +640,7 @@ and stale tools cannot continue issuing calls.
595
640
 
596
641
  `tools/list` is generated from the canonical schemas used for argument and
597
642
  result validation, and production filtering prevents unwired tools from being
598
- advertised. The current bridge supports discovery, connect, open, all-tab
643
+ advertised. The current bridge supports discovery, connect, Profile routing, open, all-tab
599
644
  inventory, observe/read/search/find, scroll and dropdown actions, annotated
600
645
  screenshots, core actions, scoped frames, explicit dialogs, cookies, auth,
601
646
  network observation/rules, eval, PDF, protected upload import, scoped download
@@ -0,0 +1,197 @@
1
+ # Profile Context Routing Plan
2
+
3
+ Status: **In progress**
4
+ Target: Browser Pilot `v0.3.0`, protocol `1.2`
5
+ Source of truth: `docs/architecture/browser-pilot-platform-spec.md`
6
+
7
+ ## Goal
8
+
9
+ Make one Browser Pilot connection correctly inventory and control ordinary tabs
10
+ from every live Chrome Profile context, while ensuring every new managed target
11
+ is created in an explicitly resolved Profile context. The design remains
12
+ Agent-neutral, extension-free, transient, and compatible with existing
13
+ single-Profile CLI workflows.
14
+
15
+ ## Real Chrome Findings
16
+
17
+ The implementation contract is based on a read-only and transient-target probe
18
+ against a user-authorized Chrome endpoint:
19
+
20
+ - one browser-level CDP endpoint exposed two ordinary Profile contexts and all
21
+ eligible tabs from both contexts;
22
+ - ordinary Profile contexts were present on `TargetInfo.browserContextId` but
23
+ absent from `Target.getBrowserContexts`;
24
+ - `Target.createTarget({ browserContextId })` was not reliable for ordinary
25
+ Profile contexts and could return `Failed to find browser context`;
26
+ - dispatching `Target.createTarget` through a target session did not inherit the
27
+ target's Profile context;
28
+ - an isolated-world `window.open` with `userGesture: true` created a target in
29
+ the representative tab's exact Profile context;
30
+ - popup window features created a separate normal Chrome window, and subsequent
31
+ `Target.createTarget({ windowId })` calls stayed in that Profile context;
32
+ - a temporary `chrome://version` target could map a runtime context to Chrome
33
+ `Local State`, but doing that is user-visible and therefore is not permitted
34
+ during passive Profile discovery.
35
+
36
+ No production logic may assume behavior contradicted by these findings.
37
+
38
+ ## Model
39
+
40
+ ### BrowserEndpoint
41
+
42
+ The existing `BrowserInstance` represents one running Chromium browser endpoint:
43
+ product/channel, user-data root, CDP endpoint, process identity, connection
44
+ generation, and connection state. A Chrome Profile directory is not a separate
45
+ BrowserInstance when it shares that endpoint.
46
+
47
+ ### ProfileContext
48
+
49
+ A `ProfileContext` is one live regular browser context observed on an endpoint.
50
+ It has:
51
+
52
+ - a public `profileContextId`, opaque to clients;
53
+ - an internal raw CDP browser-context ID, never exposed;
54
+ - the owning BrowserInstance and connection generation;
55
+ - a connection-scoped neutral label;
56
+ - an optional verified display name and Profile directory;
57
+ - current total and eligible tab counts;
58
+ - one or more bounded representative targets for routing and user recognition.
59
+
60
+ `profileContextId` is valid only for its browser connection generation. The
61
+ Broker retains stale public IDs long enough to return `profile_context_stale`,
62
+ but never routes them to a new raw context.
63
+
64
+ ### Workspace Selection
65
+
66
+ `BrowserWorkspace.selectedProfileContextId` is transient Broker memory. It is
67
+ updated by explicit selection and by switching to a target whose context is
68
+ known. It is never written as a global default and is cleared logically by
69
+ browser reconnect because the old ID becomes stale.
70
+
71
+ ### ManagedTabSet Binding
72
+
73
+ Each ManagedTabSet is permanently bound to at most one ProfileContext. A
74
+ Workspace may own multiple ManagedTabSets, one per Profile context used for
75
+ managed work. The first target binds an unbound set; opening managed work in a
76
+ different context creates another set. Workspace cleanup closes every managed
77
+ set and still leaves user tabs open.
78
+
79
+ ### ControlledTarget Context
80
+
81
+ Every managed, managed-popup, and user target records a public
82
+ `profileContextId`. Popup adoption requires the popup context to equal its
83
+ managed ancestor's ManagedTabSet context.
84
+
85
+ ## Protocol 1.2
86
+
87
+ Protocol 1.2 adds:
88
+
89
+ - `browser.profiles.list`;
90
+ - `browser.profiles.select`;
91
+ - optional `profileContextId` input on `browser.open`;
92
+ - `profileContextId` on every `browser.tabs.list` target;
93
+ - selected Profile context on Workspace results;
94
+ - Profile context binding on ManagedTabSet results;
95
+ - `profile_selection_required`, `profile_context_stale`, and
96
+ `profile_context_unavailable` stable errors.
97
+
98
+ `browser.profiles.list` is a Workspace-scoped read-only tool. It returns bounded
99
+ Profile summaries and representative eligible tabs. It never opens, navigates,
100
+ attaches to, or focuses a target merely to discover a display name.
101
+
102
+ `browser.profiles.select` accepts only a public `profileContextId`. Human CLI
103
+ selectors such as index, neutral label, or verified display name are resolved
104
+ client-side from a fresh list. The machine protocol never accepts an ambiguous
105
+ free-form selector. Selecting a Profile clears the Lease's logical active-target
106
+ anchor without releasing control or changing Chrome focus, so the next new
107
+ managed target uses that explicit Workspace selection.
108
+
109
+ ## Selection Algorithm
110
+
111
+ When `browser.open` needs a new target, resolve exactly one Profile context in
112
+ this order:
113
+
114
+ 1. an explicit, current-generation `profileContextId`;
115
+ 2. the current Lease's active target context;
116
+ 3. the Workspace's current, valid selection;
117
+ 4. the only currently available Profile context;
118
+ 5. otherwise fail with `profile_selection_required` before browser dispatch.
119
+
120
+ Listing or controlling an existing target never requires prior Profile
121
+ selection. Supplying `profileContextId` while navigating an existing target is
122
+ valid only when it equals that target's context; Browser Pilot never moves a
123
+ physical target between Profiles.
124
+
125
+ `profile_selection_required` contains only bounded Profile summaries. It is a
126
+ normal structured result for an Agent host to turn into a user question. Direct
127
+ CLI JSON mode exits without reading interactive stdin.
128
+
129
+ ## Managed Target Creation
130
+
131
+ Creation is serialized by Workspace/Profile context and remains owned by the
132
+ managed-target janitor:
133
+
134
+ 1. If the context's ManagedTabSet already has a live window ID, create
135
+ `about:blank` with that `windowId` and verify the returned target context.
136
+ 2. For the first target, try browser-level creation with `newWindow: true` and
137
+ the raw browser-context ID. Verification is mandatory because Chrome support
138
+ varies by regular Profile context.
139
+ 3. If Chrome rejects or misroutes that call, attach to a bounded representative
140
+ page target in the selected context, create an isolated world, and call
141
+ `window.open("about:blank", "_blank", <fixed popup window features>)` with a
142
+ debugger user gesture.
143
+ 4. Identify exactly one new target by the pre-dispatch target set, opener ID,
144
+ Profile context, page type, and blank URL. Ambiguous or mismatched targets are
145
+ closed and the operation fails.
146
+ 5. Explicitly adopt the verified target into the janitor before registering it
147
+ publicly, so Broker crash cleanup still closes it.
148
+ 6. Read and retain its window ID, bind the ManagedTabSet, register the opaque
149
+ target, then navigate through the normal target actor.
150
+
151
+ The fallback does not execute page-defined JavaScript, inject DOM content, read
152
+ page data, or navigate the representative user tab. It may cause ordinary
153
+ focus/blur effects inherent to Chrome opening a new window. Failure after any
154
+ possible target creation returns `unknown_outcome` unless the Broker proves all
155
+ candidate targets were closed.
156
+
157
+ ## Compatibility
158
+
159
+ - Protocol 1.0/1.1 clients continue to list and control existing tabs.
160
+ - In a single available context, their existing `browser.open` behavior remains
161
+ automatic.
162
+ - In multiple contexts, an active target remains an unambiguous anchor.
163
+ - With multiple contexts and no anchor, older clients receive a structured
164
+ failure and Browser Pilot never silently chooses the first context.
165
+ - New Profile fields on existing results are additive; raw CDP IDs remain
166
+ private.
167
+ - The current CLI negotiates protocol 1.2, while the Broker continues to accept
168
+ 1.0 and 1.1.
169
+
170
+ ## Delivery
171
+
172
+ - [x] P0: probe ordinary multi-Profile Chrome behavior.
173
+ - [x] P1: freeze model, protocol, selection, and creation rules.
174
+ - [x] P2: implement Profile context registry, target propagation, Workspace
175
+ selection, multiple ManagedTabSets, and janitor adoption.
176
+ - [x] P3: implement `bp profiles`, `bp profile`, and `bp open --profile`.
177
+ - [x] P4: update stdio integration guidance, adapters, skill, and release docs.
178
+ - [x] P5: add protocol, dual-Profile, concurrency, reconnect, cleanup, and
179
+ compatibility tests.
180
+ - [ ] P6: run real Chrome acceptance and publish `v0.3.0-rc.1`.
181
+
182
+ ## Acceptance
183
+
184
+ - All eligible user tabs across live Profile contexts appear in one inventory.
185
+ - Existing user tabs can be controlled without selecting a Profile first.
186
+ - No multi-context open silently selects a first Profile.
187
+ - Explicit, active-target, Workspace, and single-context resolution follow the
188
+ specified order.
189
+ - Every managed target and popup matches its ManagedTabSet Profile context.
190
+ - Two Workspaces may select different contexts and create targets concurrently
191
+ without cross-routing.
192
+ - Reconnect invalidates old Profile IDs, target mappings, frames, and refs.
193
+ - Passive listing creates no target and never focuses or navigates a user tab.
194
+ - Janitor EOF/SIGKILL cleanup closes fallback-created managed windows but never
195
+ user tabs.
196
+ - Protocol 1.1 clients retain existing-tab control and cannot open ambiguously.
197
+ - Single-Profile CLI and fixture behavior remains compatible.
@@ -0,0 +1,49 @@
1
+ # Browser Pilot 0.3.0-rc.1
2
+
3
+ This release candidate adds extension-free multi-Profile routing on one
4
+ authorized Chrome endpoint. Browser Pilot now inventories eligible user tabs
5
+ from every live Profile context and keeps those tabs directly controllable.
6
+ Profile selection is used only to place new managed work; it is not a permission
7
+ grant and does not require another Chrome Allow dialog.
8
+
9
+ Protocol 1.2 adds `browser.profiles.list`, `browser.profiles.select`, opaque
10
+ connection-generation-scoped `profileContextId` values, per-Profile
11
+ ManagedTabSets, and structured selection/staleness errors. Protocol 1.0 and 1.1
12
+ clients retain existing-tab control and never silently choose the first Profile
13
+ when new-target routing is ambiguous.
14
+
15
+ The CLI adds `bp profiles`, `bp profile <selector>`, and
16
+ `bp open --new --profile <selector>`. On a multi-Profile connection, `bp connect`
17
+ returns the live choices without opening an arbitrary Pilot window. Existing
18
+ single-Profile setup and direct Agent installation remain compatible.
19
+
20
+ Managed creation first verifies Chrome's returned Profile context. When regular
21
+ Profile contexts reject direct CDP creation, Browser Pilot uses a marked
22
+ isolated-world `window.open`, verifies its opener, URL, type, and Profile, then
23
+ adopts it into crash cleanup before public registration. Misrouted targets are
24
+ closed and verified absent; uncertain creation returns `unknown_outcome` and is
25
+ never automatically retried.
26
+
27
+ The stdio reference adapters now negotiate protocol 1.2, so Tenon, OpenClaw,
28
+ and other Agent products receive Profile tools through the same runtime manifest
29
+ without a Native SDK, MCP server, extension, or product-specific Browser Pilot
30
+ code.
31
+
32
+ This is an RC. The release gate includes protocol compatibility, dual-Profile
33
+ fault injection, CLI process tests, full unit and browser suites, stdio
34
+ conformance, distribution checks, and a controlled real Chrome acceptance run.
35
+
36
+ Automated release gates completed before tagging:
37
+
38
+ - TypeScript type checking passed.
39
+ - 264 Node unit, protocol, process, and adapter tests passed.
40
+ - 109 isolated Playwright core, compatibility, and network tests passed.
41
+ - The black-box stdio conformance suite passed all 13 checks.
42
+ - npm distribution verification passed for global, local project, and
43
+ product-bundled executable modes.
44
+ - The npm dry-run package contains the Profile routing plan and this release
45
+ note.
46
+
47
+ The controlled real Chrome dual-Profile acceptance is performed after installing
48
+ the published RC so it exercises the same package and Codex skill path available
49
+ to users.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-pilot-cli",
3
- "version": "0.2.2",
3
+ "version": "0.3.0-rc.1",
4
4
  "description": "CLI tool to control your browser via Chrome DevTools Protocol",
5
5
  "repository": "https://github.com/relixiaobo/browser-pilot",
6
6
  "type": "module",
@@ -40,7 +40,9 @@
40
40
  "docs/integration/stdio-bridge.md",
41
41
  "docs/integration/stdio-conformance.md",
42
42
  "docs/plans/browser-capability-evolution.md",
43
+ "docs/plans/profile-context-routing.md",
43
44
  "docs/plans/universal-agent-integration.md",
45
+ "docs/releases/v0.3.0-rc.1.md",
44
46
  "README.md",
45
47
  "LICENSE"
46
48
  ],