Client half of the PiP observation window, wired to the host ObservationService over the /bsk-observation HTTP+SSE seam: - ObservationClientStore: initial state fetch + SSE increments + on-demand thumbnail blob loading (session-authorized readAttachment, same path as the toolview) + interrupt POST. All I/O injectable. - ObservationOverlay registered into the shell.overlay list seat (the sanctioned frame-wide surface; root is off-limits). Hidden with no owned sessions; appears on first start; vanishes when all stop. - Focus view: status row (green/grey/red dot + session + action + mm:ss ticking), breathing thumbnail (new frames fade in, failures keep the last frame with a warning badge, pre-navigate placeholder), and the action area. - Interrupt: single click, no confirm; greys into 'Interrupting…' and back; disabled when nothing is in flight; one-time tooltip explains the semantics (stops the current action only; the run continues). - Card: drag-move via the header, corner-handle resize (min 240x180, max 80% viewport), both clamped and kept for the page lifetime; collapsible status capsule. - PiP: Pop out (user gesture) opens Document PiP with the card's current size, portals the same content in, and clones the document's style nodes; pagehide falls back to the card with state intact; unsupported browsers hide the button; nothing auto-pops. Tests (+17, 89 total): store (fetch/SSE apply/malformed frames/ thumbnail lifecycle/interrupt wire/stop cleanup) and overlay (hidden-> visible lifecycle, status row, thumbnail via loader, interrupt disabled/active/hint-once, capsule, resize clamps, move clamps, PiP unsupported, PiP pop/fallback). Styling follows web-styling (dsw-alias tokens, CSS Modules, focus-visible, reduced-motion).
dsh-plugin-browserskill
A DeepSeek Harness (dsh) tool plugin that exposes
BrowserSkill (bsk) browser automation to the model.
Each tool maps to one bsk <cmd> --json invocation: the plugin spawns the bsk CLI, parses its
structured JSON output, and returns a canonical typed value. The bsk daemon, browser, and Chrome
extension keep owning the actual browser control — this package is a thin, well-typed bridge.
Tools
| Tool | bsk command | Purpose |
|---|---|---|
browser_session_start |
bsk session start |
Open an Agent Window session; optional initial URL, window size, and device emulation preset. Returns the session id and makes it the current session. |
browser_session_stop |
bsk session stop |
Stop a session (the current one by default) and close its Agent Window. Only plugin-created sessions can be stopped. |
browser_session_list |
— (registry only) | List the sessions this plugin created, marking the current one. Foreign daemon sessions are never shown. |
browser_navigate |
bsk navigate |
Navigate the active tab, with waitUntil / timeout control. |
browser_snapshot |
bsk snapshot |
Indented aria-tree snapshot with @eN refs for interaction tools. |
browser_observe |
bsk observe |
Semantic VOM observation (read-only) with @eN refs. |
browser_click |
bsk click |
Click a snapshot ref or CSS selector (button / click-count options). |
browser_fill |
bsk fill |
Fill an input / textarea / contenteditable (clears first by default). |
browser_press |
bsk press |
Dispatch a key or combo, optionally focusing a target first. |
browser_screenshot |
bsk screenshot |
PNG capture of the tab or a ref-cropped element; inlines the image when the deployment supports image input, otherwise returns a file path. |
browser_emulate |
bsk emulate |
Apply or clear mobile device emulation on the active tab. |
Multi-session model
One agent conversation can drive several browser sessions at once:
browser_session_startreturns the session id and makes it the current session.- Every operation tool accepts an optional
sessionargument. When omitted, the call acts on the current session (the one most recently started or used); when given, that session becomes current. - Every tool result echoes the session it actually acted on, so the model never has to guess.
- The number of concurrent sessions started through the plugin is capped (
maxSessions, default 5). - Unloading the plugin stops every session it started and kills in-flight bsk processes.
Ownership boundary: the bsk daemon may be shared with other agents, terminals, or dsh
instances. The plugin therefore only ever sees and operates on sessions it created itself —
an explicit session argument naming a foreign or unknown id is rejected, browser_session_list
shows plugin-created sessions only (no daemon-wide view), and stop/unload cleanup can never touch
a session owned by another program.
Installation
The plugin follows the standard dsh bundle layout (dsh.bundle manifest + cordis.patch.yml):
dsh plugin --profile <name> add <path-or-spec-of-this-package>
dsh --profile <name>
Prerequisite: the bsk CLI must be installed and on PATH, and the BrowserSkill Chrome extension
must be connected — see the BrowserSkill README. When bsk
is missing, tool calls fail with install guidance instead of a bare spawn error.
Configuration
All fields are optional and validated through the plugin's Schemastery Config:
# cordis.patch.yml override example
- insert:
- id: browserskill
name: dsh-plugin-browserskill
config:
bskPath: bsk # path to the bsk binary (default: resolve from PATH)
defaultTimeoutMs: 120000
maxSessions: 5
Behavior notes
- Cancellation: aborting a tool call (
exec.signal) kills the underlying bsk child process, matching BrowserSkill's cooperative tool-cancellation model. - UI cards: calls render as terminal cards (command line as title, output as the completed card). Screenshots additionally attach the image itself when the host mounts an attachment store and the active model route declares image input; otherwise the PNG path is returned.
- Web UI toolview (browser half): the package is dual-face.
dsh.client(platformweb) shipslib/client.js, which registers a keyedtool.call.toolviewview forbrowser_screenshot. The custom view keeps the terminal block (command + output) and, when the settled result carries an image block, resolves the durable attachment through the client session's authorizedreadAttachmentRPC and renders it with the sharedMessageImagethumbnail/lightbox atoms. Every otherbrowser_*tool keeps the stock terminal card. The bundle follows the dsh client contract: a CJS closure factory handed towindow.__ModuleLoader__.load, platform modules (react,dsh-client-ui-*) external, everything else inlined, CSS Modules compiled by lightningcss. - Errors: non-zero bsk exits surface the CLI's JSON error envelope (
code,message,hint) so the model gets the daemon's actionable guidance. - Long-running work (e.g.
bsk record) is not backgrounded viactx.jobsyet — tracked as a follow-up.
Development
pnpm install
pnpm --filter dsh-plugin-browserskill typecheck
pnpm --filter dsh-plugin-browserskill test # unit tests mock bsk; no browser needed
pnpm --filter dsh-plugin-browserskill build # tsdown -> lib/
License
MIT