Follow-up from PR #312, flagged in review.
PR #312 moved the gate-review browser-launch decision server-side via the new `shouldLaunchReviewBrowser` predicate, gated on `isBrowserAttached(sessionId)`. The same pattern hasn't been applied to the question and direction sessions yet — they still have the old "agent flag is authoritative" shape.
Locations
-
`packages/haiku/src/server/tool-call.ts:683` — `createQuestionSession` await path
```ts
if (autoOpen && url) launchBrowserBestEffort(url, "Question session")
```
-
`packages/haiku/src/server/tool-call.ts:910` — `createDesignDirectionSession` await path
```ts
if (autoOpen && url) launchBrowserBestEffort(url, "Direction session")
```
Same failure mode as the gate path: if a SPA tab is already attached for the intent (live websocket), `open ` still fires and pops a duplicate tab. Agent has to detect-and-pass `auto_open: false`, which it misses.
Fix shape
Use `shouldLaunchReviewBrowser(autoOpen, url, sessionId)` (already exported from `tool-call.ts` for #312) at both call sites. Same three guards:
- `autoOpen` hard override
- `!url` short-circuit
- `!isBrowserAttached(sessionId)` live-websocket check
Add regression tests mirroring `should-launch-review-browser.test.mjs` for the question/direction surfaces if those sessions can be exercised similarly via `createSession` + `recordHeartbeat`.
Schema descriptions
The `auto_open` field on `HAIKU_AWAIT_VISUAL_ANSWER` and `HAIKU_AWAIT_DESIGN_DIRECTION` schemas in `packages/haiku/src/state/schemas/inputs/await-tools.ts` should get the same "hard override, not a duplicate-tab guard" treatment that the gate version got in #312.
Out of scope
Other surfaces using `launchBrowserBestEffort` (ad-hoc review at `tool-call.ts:497`) — those don't have a session ID with a websocket-attachment notion, so the pattern doesn't directly apply. Leave them alone unless we find a similar duplicate-tab complaint.
Follow-up from PR #312, flagged in review.
PR #312 moved the gate-review browser-launch decision server-side via the new `shouldLaunchReviewBrowser` predicate, gated on `isBrowserAttached(sessionId)`. The same pattern hasn't been applied to the question and direction sessions yet — they still have the old "agent flag is authoritative" shape.
Locations
`packages/haiku/src/server/tool-call.ts:683` — `createQuestionSession` await path
```ts
if (autoOpen && url) launchBrowserBestEffort(url, "Question session")
```
`packages/haiku/src/server/tool-call.ts:910` — `createDesignDirectionSession` await path
```ts
if (autoOpen && url) launchBrowserBestEffort(url, "Direction session")
```
Same failure mode as the gate path: if a SPA tab is already attached for the intent (live websocket), `open ` still fires and pops a duplicate tab. Agent has to detect-and-pass `auto_open: false`, which it misses.
Fix shape
Use `shouldLaunchReviewBrowser(autoOpen, url, sessionId)` (already exported from `tool-call.ts` for #312) at both call sites. Same three guards:
Add regression tests mirroring `should-launch-review-browser.test.mjs` for the question/direction surfaces if those sessions can be exercised similarly via `createSession` + `recordHeartbeat`.
Schema descriptions
The `auto_open` field on `HAIKU_AWAIT_VISUAL_ANSWER` and `HAIKU_AWAIT_DESIGN_DIRECTION` schemas in `packages/haiku/src/state/schemas/inputs/await-tools.ts` should get the same "hard override, not a duplicate-tab guard" treatment that the gate version got in #312.
Out of scope
Other surfaces using `launchBrowserBestEffort` (ad-hoc review at `tool-call.ts:497`) — those don't have a session ID with a websocket-attachment notion, so the pattern doesn't directly apply. Leave them alone unless we find a similar duplicate-tab complaint.