SessionManager.classifyNewTab's `initializing` branch relied on a
windowInitializing flag that start() cleared *before* the home tab's
chrome.tabs.onCreated event was dispatched (the event is asynchronous), so
the branch never ran in production and the home tab's classification was
subject to a timing race. Match the home tab by its tab id instead — the id
is already returned by ensureActiveTab and is reliable regardless of event
ordering.
Also refresh the overlay-bridge docstring: overlay.ready now makes the
background *push* the per-tab overlay state (and the Agent Window also
pushes on tab create / control-mode change), rather than a request/reply.
tool.tab_create increments pendingAgentTabCount *before* chrome.tabs.create
so the onCreated listener can tell an agent tab from a user-opened tab. When
the create failed, no onCreated event arrived and the counter leaked, so the
next tab the user opened was misclassified as agent-created — the exact
confusion the counter exists to prevent, a variant of issue #57.
handleTabCreate now releases the slot on failure via
SessionManager.releaseAgentTabPending. An abort (cancelled) is excluded: the
tab did open (and was cleaned up), so its onCreated still consumes the slot.
Added a regression test asserting that after a failed tab_create, a
user-opened tab is still classified as 'user' (not 'agent').
- session_stop: exclude tabs still tracked in agentCreatedTabs from the
release decision so a tab that failed to close is no longer mistaken for
a user tab (which previously kept the window open and leaked the tab).
- Distinguish user-created tabs from agent-created tabs in the Agent Window
via a pending-tab counter consumed by chrome.tabs.onCreated, so tabs the
user opens themselves (new-tab button / Cmd+T / open in new tab) are never
taken over by the BrowserSkill control mask.
- background overlay: decide per-tab state BEFORE showing controls
(overlayStateForTab + pushOverlayStateForTab) so a user tab receives hidden
on its first overlay.ready ping instead of flashing control then hiding.
- AgentWindowApi.ensureActiveTab now resolves with the activated/created tab
id instead of void, so SessionManager can record homeTabId. This fixes the
tsc failure flagged in review (the interface previously returned
Promise<void> while callers treated the result as number | null).
- Wire the production tabManagement.tabs / tabsQuery deps into
tool.session_stop, both in the dispatcher and in the disconnect cleanup.
They were never injected, which left the agent-tab cleanup and the
window-release path as dead code outside tests (behaviour identical to
main). Added a dispatcher-level integration test that fails without the
wiring: a surviving user tab must release the window, not close it.
dsh 0.1.0-rc.8 imports and registers the client bundle by export const name, so a leftover dsh-plugin-browserskill id fails to load the 0.1.1 web UI.
Co-authored-by: Cursor <cursoragent@cursor.com>
The exit code reflects RPC success only, not JS success. Document that programmatic callers must use --json and check the .ok field to detect JS errors. Addresses the discoverability gap raised in issue #133.
Agents were stopping on popup and status copy that looked like a hard incompatibility. Keep the connection usable, reframe exit 5 as an unknown method, and ask users to upgrade instead of aborting.
Co-authored-by: Cursor <cursoragent@cursor.com>
Register bundled browser instructions at agent scope, prioritize foreground commands over thumbnail capture, and unify cancellation and session cleanup.
browser_screenshot declared every capture as image/png regardless of the
actual bytes. On Chromium/Edge builds where captureVisibleTab returns JPEG
bytes, the stored attachment reference recorded mediaType "image/png" against
JPEG bytes, so dsh-attachment-local's read-time metadata verification fails
with ATTACHMENT_CORRUPT ("Stored attachment metadata does not match its
reference"), surfacing as code UNKNOWN and triggering endless LLM retries.
- image.ts: sniff the capture's magic bytes (PNG/JPEG/WebP/GIF) and declare
that real media type to attachments.saveImage, instead of always "image/png".
- tools.ts: assemble the tool result from ref.mediaType (the store's verified
type) rather than re-declaring "image/png" as const, and relax the output
schema's const to the accepted media-type enum.
- tests: add a regression case asserting JPEG capture bytes surface as
image/jpeg.
Co-Authored-By: DeepSeek Harness <noreply@deepseek.com>
Reject legacy JSON --output before recording starts, persist the completed Trace before writing the bundle so `bsk record stop --output <dir>` can retry after an export failure, and tell users to pass --url when https://example.com/ does not load.