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.
- package/README.md +15 -6
- package/dist/cli.js +177 -8
- package/dist/daemon.js +806 -72
- package/dist/managed-target-janitor.js +6 -1
- package/docs/architecture/browser-pilot-platform-spec.md +83 -12
- package/docs/integration/stdio-bridge.md +53 -8
- package/docs/plans/profile-context-routing.md +197 -0
- package/docs/releases/v0.3.0-rc.1.md +49 -0
- package/package.json +3 -1
|
@@ -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
|
|
175
|
-
process-stable identity, product,
|
|
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.
|
|
182
|
-
default and are recreated after Broker
|
|
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.
|
|
191
|
-
|
|
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.
|
|
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,
|
|
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
|
-
|
|
904
|
-
|
|
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":
|
|
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,
|
|
173
|
-
|
|
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
|
|
411
|
-
|
|
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.
|
|
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
|
],
|