fix(server): reticle_lease falls back to a connected tab when no managed browser launches - #871
Conversation
…ged browser launches Several reporters on reticlehq#691 hit a Chromium the pool could not launch -- missing binary, a missing shared-library dependency, a build-revision mismatch -- and worked around it by hand, driving an already-open tab instead. That worked, because a lease is what this daemon recommends when a human's tab is hidden or throttled: refusing outright the moment the managed browser is unavailable strands the agent with no path forward even when a driveable, connected session was sitting right there. Both places reticle_lease{action:"acquire"} can currently fail before ever producing a lease -- the Chromium preflight, and the launch itself inside pool.acquire -- now check for a live non-leased tab on the same project first. When one exists, it's handed back instead of throwing, via a new noManagedBrowserFallback() that returns the existing sessionId with a noManagedBrowser: { sessionId, reason } field naming why no managed browser was used. This is explicitly NOT a lease -- nothing was acquired from the pool -- so expiresInMs is now optional in the output schema (unused in this branch) and the field's own description says reused/expiresInMs don't apply here. Silent otherwise: with no live tab available, both paths throw the same clean errors they always did. alreadyOpen is now sampled once at the top of the handler (previously computed only after a successful acquire, for the advisory preferExisting hint) so both failure points can see it before deciding whether to throw. Addresses the fallback ask from reticlehq#691. The build-revision-mismatch messaging and the remaining Windows-specific reports in that issue stay open -- different changes, not attempted here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Bhumika <guravbhumika808@gmail.com>
|
Closing this as a duplicate of #861, which covers the same ask of #691 and was opened 11 hours earlier — and the collision is partly my fault. When I closed your webpack PR I pointed you at "unclaimed work" without checking that #691's fallback ask already had a PR against it. That was careless of me and it cost you an evening. Two things, because "duplicate" on its own would be unfair here. Your envelope is the better one and I am going to ask for it. #861 signals the fallback with Your PR description also states the severity better than theirs does — that a lease is what the daemon actively recommends when a tab is hidden or throttled, so refusing at exactly that moment strands the agent with no path forward. I have quoted that on #861. You have now written three good PRs today and had two closed as duplicates. That is a bad ratio and it is a signal about our process, not your work: nothing in this repo made it discoverable that #680 and #691's fallback were already taken. I am fixing that by checking the open-PR list by symptom before pointing anyone at anything. #870 is yours and it is live — rebased, approved, and merging on green. If you want another, say so and I will find you one that I have actually verified is unclaimed first. |
What & why
Several reporters on #691 hit a Chromium the pool could not launch — the binary missing, a shared-library dependency missing, a build-revision mismatch — and worked around it by hand: driving an already-open tab instead of the lease. That worked, and the issue names it directly as the third ask: "Fall back to the connected tab rather than failing outright when no managed browser can be launched."
It matters because a lease is what this daemon actively recommends when a human's tab is hidden or throttled. Refusing outright the moment the managed browser is unavailable strands the agent with no options, even when a driveable, connected session was sitting right there the whole time.
What changed
reticle_lease{action:"acquire"}inpackages/server/src/tools/lease-tools.tshas two places it can currently fail before ever producing a lease:browserProbe) — throws"Chromium is not installed...".pool.acquire()itself throwing — caught and rethrown as"could not open <url> — is the app running there?".Both now check for a live, non-leased tab on the same project (
liveTabFor, which already existed and backs thepreferExistingadvisory hint) before giving up. When one exists, it's handed back via a newnoManagedBrowserFallback()helper instead of throwing — the response carries the existing session's id plus a newnoManagedBrowser: { sessionId, reason }field explaining exactly why no managed browser was used.This is explicitly not a lease — nothing was acquired from the pool, so
reused/expiresInMsdon't describe anything real.expiresInMsis now.optional()in the output schema (only in this fallback branch; every normal acquire still returns it), and its description says so. I checked for anything else relying onexpiresInMsalways being present —drive-attach.tsalready treats it as optional (expiresInMs?: number), so this aligns with existing consumer code rather than introducing a new inconsistency.Silent otherwise: with no live tab available, both paths throw the exact same clean errors they always did — nothing changes for the common case.
One structural change worth flagging:
alreadyOpen(the live-tab lookup) is now sampled once at the top of the handler, before either failure point, rather than only after a successful acquire (where it previously only fed the advisorypreferExistinghint). Same value, computed earlier so both failure branches can see it.What I deliberately did NOT attempt
playwright-core, and getting it wrong would ship an unverified regex classifier.doctorclaiming Chromium missing after the requested version was installed) — these need a Windows repro I don't have reliable access to reproduce confidently.Both are real, separate pieces of #691 and better left to someone who can verify them against an actual failure.
How it was verified
lease-tools.test.tspass (30 pre-existing + 4 new): falls back when Chromium is absent and a tab is open; still throws the clean install error when Chromium is absent and no tab is open; falls back when the launch itself fails and a tab is open; still throws the clean nav error when the launch fails and no tab is open.tsc -b --force(typecheck) — clean.eslinton every changed file — clean.prettier --checkon every changed file — clean.Gates run
pnpm lint && pnpm typecheck && pnpm test:unit(~2 min — always) — ran scoped to@reticlehq/server: 34/34 inlease-tools.test.ts, typecheck clean, lint clean, prettier clean.pnpm test:e2e(~8 min) — this touches the tool surface (reticle_lease's output shape gained a field), so this tier normally applies too. Flagging honestly: I could not run the full e2e battery in this environment (needs a real Chromium + booted fixture apps). The change is additive-only for the normal-acquire path (existing fields unchanged, new field only appears in the new fallback branch), so it shouldn't disturb existing e2e assertions on lease output, but this is worth a maintainer e2e run before merge.pnpm gate:install— not applicable, no install-path code touched.pnpm test:e2e:desktop— not applicable, no desktop code touched.Checklist
git commit -s)any, no free strings, no non-null!console.logor internal tracking codes left in the diffCHANGELOG.mdupdated under[Unreleased]Addresses the fallback ask of #691 (not closing it — the build-mismatch messaging and the Windows-specific reports stay open).
🤖 Generated with Claude Code