You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Web runs: the Chrome extension creates the cloud session through claude.ai's repo picker (#1328) (#1693)
* Web runs: the Chrome extension creates the cloud session through claude.ai's repo picker (#1328)
A session created through the page's repository picker is repo-bound and can push and
open its pull request; `claude --cloud` has, on some accounts, produced a bundle upload that
never could (#1320). So a web run now asks its daemon for an extension-created session first.
Daemon: the session start-queue (`bridge-starts.ts`, re-landed from #1330) behind two faces on
the one token — the extension's `GET /_bridge/start` (claim-on-read) + `POST /_bridge/started`,
and the run's `POST /_web-start` + `GET /_web-start/<id>` (`web-start-endpoints.ts`), which
answers 409 at once when no extension has called within three minutes. The daemon hands every
spawned run its own URL in `TF_DAEMON_URL`.
Driver: `CloudDriver` takes `extension: { daemonUrl, token }` (wired by the CLI from the env
and the registry token); after the anchor push and trust check it posts the request — repo from
the GitHub remote, the pushed ref, the whole hand-off prompt — and polls until created, then
reports exactly as the CLI path does. 409/404, no GitHub remote or an unpushed ref fall back
to the CLI's cloud mode with a notice; an extension that tried and failed fails the turn with
its note.
Extension 0.11.0 (daemon lockstep): the worker claims requests on the answer beat, one at a
time, opens claude.ai/code pinned and inactive, and the content script drives the repo chip's
searchable list, verifies the branch chip reads the pushed ref before sending, types the prompt,
sends, and reads the session id off the address. A created session's tab is a watched tab; a
failed attempt's tab is closed and the note says what the page lacked. Harness cases cover
the drive, the wrong-branch refusal and the missing-picker report.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Extension: wait for the repo chip or picker to render, and treat a remembered repo's chip as the picker (#1328 dogfood 1)
First live attempt: the page had remembered the last repo picked, so its chips (the-framework / main, as comboboxes)
were already there — but they render after the composer, and createSession read the page before they did, then looked
for a "Select repo" trigger that no longer exists once a repo is remembered.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Extension: drive the new-session page as it actually is — combobox chips in order, visible options only, React-safe search (#1328 dogfood 2)
Run 2 clicked the repo chip itself as if it were a list entry (its text is the bare repo name) and then found no
chip "afterwards". Read from the live page: the chips are combobox buttons in the order repo, branch, add; a picker
is a dialog with a role=combobox search input and a listbox of options; a closed picker's options stay in the DOM,
so only visible options are entries, and the trigger is never one. The branch list does offer a ref pushed seconds
earlier (checked live), so verifying the chip before sending is enough.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Extension: wait for the picker's list to load before filtering it, fall back to the whole list, and say what the list showed on failure (#1328 dogfood 3)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Harness: a list that already holds the entry is not filtered first
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Extension: read chip and entry labels through the page's icon-font glyphs (#1328 dogfood 4)
In the content script's world the labels carry private-use glyphs — the chips read "\ue048the-framework" and
"\ue078main", the chosen entry "framework/the-framework\ue03b" — so no exact match ever fired. The mirror already
strips those; the control labels now do too.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: FEATURES-SPEC.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -165,6 +165,7 @@ happens while nobody is at the keyboard.
165
165
- Saved remote devices: run an agent on another machine's daemon — dashboard here, agents there
166
166
- Run on a fresh GitHub Actions runner
167
167
- Run on a Claude Code cloud session
168
+
- A web run's cloud session is created by the Chrome extension in your own browser, through claude.ai's repository picker — repo-bound, so it can push and open its pull request; the CLI's cloud mode only when no extension is around
168
169
- Chrome extension bridging claude.ai questions back to the dashboard
169
170
- A cloud session's conversation mirrored into the run view, turn by turn, as it is written
170
171
- Answer a cloud agent's question from the dashboard (typed back into claude.ai) — the same gate panel a local agent gets, multi-select and stop options included, listed with every other open question
Copy file name to clipboardExpand all lines: packages/chrome-extension/SPEC.md
+15Lines changed: 15 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -23,6 +23,7 @@ Four parts: the content script (the page half — reads claude.ai, types answers
23
23
-**A stranded question's round trip** - the content script extracts the parked question from the page, the service worker reports it to the daemon, the dashboard shows it as an ordinary gate; the answer is queued, collected, typed into the composer, submitted, and the outcome acknowledged.
24
24
-**The question crosses whole, so it is answered like a local one** - the block's own shape travels with it, which is what lets the dashboard offer the recommended option, several answers at once, and a pick that hands the session back.
25
25
-**Only what the session offered** - the daemon composes what is typed out of labels of the parked question's own options and nothing else, an answer stays withdrawable until the extension collects it, and the extension otherwise only observes.
26
+
-**It creates the sessions the daemon asks for** - a web run's cloud session is created here, on claude.ai's own new-session page with the repository and branch the run named, so it is bound to the repository and can push and open its pull request; the branch is verified before anything is sent.
26
27
-**Tabs nobody has to think about** - the daemon publishes which cloud sessions to watch; the extension keeps one pinned, inactive tab per session (opt-in), closes its own stale tabs, and never reopens one the user closed.
27
28
-**The trust boundary** - the bridge token and all daemon traffic live in the service worker; the content script, which shares its tab with claude.ai, holds no secret and calls no daemon.
28
29
-**Version lockstep** - every daemon call states the extension's version, and a daemon expecting another refuses it outright, naming both versions; the two halves must ship the same number.
@@ -50,6 +51,20 @@ See `## User story`, second and third items.
50
51
51
52
Three properties bound the write path. The daemon refuses to queue an answer unless every label picked is one of the parked question's own options — exactly one of them unless the question allows several — and it composes the text to be typed itself, so the only thing the bridge can ever put in a composer is built from what the session offered, never free text from the browser. An answer stays withdrawable until the extension collects it, and that window is the only time withdrawing means anything. And the extension acts only on delivery: everything else it does is read-only, and its one manual write control — a "Fill composer (does not send)" button on its in-page panel — fills without submitting, proving the write path exists without the extension ever speaking for the user.
52
53
54
+
### It creates the sessions the daemon asks for
55
+
56
+
#### User story
57
+
58
+
A web run wants a cloud session that can push its work and open a pull request. Such a session is created through claude.ai's repository picker, in the user's own signed-in browser — which is exactly where this extension runs.
59
+
60
+
#### Business logic
61
+
62
+
The service worker asks the daemon for the next session to create, at the same beat it looks for answers. Given one, it opens claude.ai's new-session page in a pinned, inactive tab and hands the request — repository, branch, prompt — to the content script there, which chooses the repository, makes sure the branch chip reads the requested branch, types the prompt and sends. The session id is read from the page's address once it becomes a session, and reported back to the daemon along with a note of what was clicked; a failure reports what the page lacked instead. One creation runs at a time. A created session's tab is kept as a watched tab like any other; a failed attempt's tab is closed.
63
+
64
+
#### Rationale
65
+
66
+
The session is created through the same controls a person would use, in their own browser, on their own account. Every selector is a guess about a page that is not ours, so the page half reports what it saw rather than insisting — the first failure is meant to be diagnosable from the run's log.
Copy file name to clipboardExpand all lines: packages/chrome-extension/background.SPEC.md
+15Lines changed: 15 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,6 +21,7 @@ A `web`-target agent hands its task to a cloud session and ends; nothing streams
21
21
-**Tabs open themselves for watched sessions** - one pinned, inactive tab per cloud session the daemon lists, so the bridge does not depend on the user happening to be on claude.ai.
22
22
-**A tab the user closed is not reopened** - closing the tab for a session dismisses that one session, and only that one.
23
23
-**Tabs the extension opened are also closed by it** - once the daemon stops watching a session, its tab goes, so the browser does not accumulate pinned tabs forever.
24
+
-**Sessions are created one at a time** - the worker claims the daemon's next session request on the answer beat, opens the new-session page in its own pinned tab, has the content script drive it, and reports the session it became; a created session's tab becomes a watched tab, a failed attempt's tab is closed, and a second request waits for the first to finish.
24
25
-**Every attempt records why it did nothing** - the outcome of the last tab sweep is kept so the options page can state the reason instead of leaving the user guessing.
25
26
26
27
## Business logic
@@ -149,6 +150,20 @@ A user who leaves the browser running for days should not end up with a pinned t
149
150
150
151
At the end of every sweep, tabs this extension opened for sessions the daemon no longer lists are closed. Only tabs the extension opened: a claude.ai session the user opened themselves is theirs to keep.
151
152
153
+
### Creating the session the daemon asked for
154
+
155
+
#### User story
156
+
157
+
A web run is waiting on the daemon for a cloud session that can push its work; the only thing that can create one is this extension, in the user's browser.
158
+
159
+
#### Business logic
160
+
161
+
On the answer beat, and once at start, the worker asks the daemon for the next session request. Given one, and none already in progress, it opens claude.ai's new-session page in a pinned, inactive tab, waits for the page to load, and hands the request to the content script there — retrying for a while, since the script may still be being injected. The content script's outcome is reported to the daemon under the request's id: success with the session id, or failure with the note of what the page lacked. A created session's tab is recorded as one the extension opened for that session, so it is watched and eventually closed like any other; a failed attempt's tab is closed at once. The outcome is kept where the options page can show it.
162
+
163
+
#### Rationale
164
+
165
+
Creation navigates a page, so two at once would race each other's controls; serial creation makes a fan-out of several runs a matter of throughput, not correctness. A report that fails to reach the daemon is not retried here: the daemon's claim expires on its own and the request is offered again.
166
+
152
167
### Waking up on a schedule rather than on a timer
Copy file name to clipboardExpand all lines: packages/chrome-extension/check.SPEC.md
+6Lines changed: 6 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,6 +27,12 @@ Typing the dashboard's answer back into the session:
27
27
- With no send button on the page, the answer is submitted with an Enter keypress instead.
28
28
- A page with no composer is refused with that as the stated reason, rather than the text being typed somewhere else.
29
29
30
+
Creating a session:
31
+
32
+
- On a synthetic new-session page built like the live one was observed to be — combobox chips for the repository and the branch, each opening a searchable list of options, a composer and a send button that turns the page into a session address — the branch is chosen, the prompt typed, send clicked, and the new session's id reported, in each of the three states the page opens in: the requested repository already remembered, another repository remembered (re-picked through its chip), and none remembered (picked through the select-repository control).
33
+
- When the branch list does not offer the requested branch, nothing is sent and the outcome names the branch.
34
+
- A page with no repository picker is refused naming that control, and the probe describes the page's controls without touching them.
35
+
30
36
The panel: it folds down to a compact "TF" tab, dropping its rows and its full title, and unfolds with the question's details intact.
0 commit comments