momo-gen
dsh-browser-pilot
Browser automation tools for the DeepSeek Harness (DSH) agent - a self-contained Cordis agent-preset plugin (Playwright).
- Stars
- 1
- Language
- TypeScript
- Created
- Aug 14, 2026
- Updated
- Aug 14, 2026
Introduction
dsh-browser-pilot
Browser automation for the DeepSeek Harness (DSH) agent — a self-contained Cordis agent-preset plugin that gives the agent safe, guard-railed control of a real web browser (navigate, click, type, screenshot, extract) via Playwright.
🌏 中文文档 · English
It is designed to be published and shared: clone this repository into your DSH user preset root,
install one dependency, and your next session gains a full set of browser_* tools on top of the
standard coding agent.
The agent can operate the browser, but only through explicit safety rails — URL scheme allow-listing, output-path sandboxing, and no host-side code execution. See Security model and
SECURITY.md.
Demo
A DSH agent driving the browser end-to-end — browser_navigate → browser_type → browser_click:

The result of that interaction (the agent filled the input, clicked, and captured the page):

Features
- 14 model tools exposing the full browser surface:
browser_navigate,browser_snapshot,browser_text,browser_html,browser_click,browser_type,browser_press,browser_select,browser_screenshot,browser_pdf,browser_wait,browser_evaluate,browser_status,browser_close. - Cross-browser: Chromium, Firefox, and WebKit (Chromium by default).
- Safe by default:
javascript:/data:/file:/chrome:/about:confignavigation is rejected; screenshots and PDFs are confined to the session workspace; arbitrary page JavaScript (browser_evaluate) is off until you opt in. - Zero harness-internal dependencies: the plugin imports only
playwrightand Node built-ins, so it loads from a relative path inside the preset without needing the harness's own@deepseek-ai/*packages. - Lifecycle-correct: the browser is a lazy singleton owned by the plugin and is closed when the preset unmounts.
Tech stack
| Layer | Choice | Notes |
|---|---|---|
| Language | TypeScript 5 (strict) → ES2022 ESM | type-safe, compiled with tsc |
| Runtime | Node.js ≥ 18 | "type": "module" |
| Engine | Playwright (Chromium / Firefox / WebKit) | the only runtime dependency |
| Host integration | Cordis plugin (apply(ctx, config)), registered via ctx.tools.register | no isolate realm needed — publishes no service |
| Distribution | DSH agent preset (a directory with agent.cordis.yml + preset.yml) | referenced by relative path ./dist/index.js |
| Tests | node:test + node:assert (built-in) | unit (guards) + integration (real Chromium) |
| Build | tsc, no bundler | output into dist/ (built, not committed) |
| CI | GitHub Actions | build + test + audit |
| License | MIT |
Quick start
1. Install
Clone this repository into your DSH user preset root (the directory name becomes the preset id):
git clone https://github.com/<you>/dsh-browser-pilot `
"$env:DSH_HOME/.agent-presets/browser-pilot"
cd "$env:DSH_HOME/.agent-presets/browser-pilot"
npm install
npm run build
npm run install-browsers # downloads Chromium once (shared cache)
$env:DSH_HOME defaults to ~/.dsh (on Windows: C:\Users\<you>\.dsh).
国内用户建议用镜像 (China users: use a mirror) — downloading the browser via the npmmirror CDN is an order of magnitude faster than the official CDN (verified). Set this before
npm run install-browsers:$env:PLAYWRIGHT_DOWNLOAD_HOST='https://cdn.npmmirror.com/binaries/playwright'
2. Start a session on the preset
Create a new DSH session and pick the browser-pilot preset (or set it as your default in the
preset picker). The agent now has the browser_* tools.
3. Try it
Ask the agent, for example:
Open https://example.com, tell me the page title, then take a screenshot.
The agent will call browser_navigate, browser_snapshot, and browser_screenshot in sequence.
Tool reference
| Tool | Purpose |
|---|---|
browser_navigate(url, waitUntil?) | Open a URL (scheme-guarded). Returns URL + title. |
browser_snapshot(maxChars?) | URL + title + visible body text (truncated). |
browser_text(selector) | innerText of the first element matching a CSS selector. |
browser_html() | Full page HTML (truncated). |
browser_click(selector, timeout?) | Click the first matching element. |
browser_type(selector, text) | Fill an input/textarea (replaces current value). |
browser_press(selector, key) | Press a key (Enter, Tab, Escape, …) on an element. |
browser_select(selector, value) | Select a <select> option. |
browser_screenshot(filename?, fullPage?) | Save a PNG/JPG/WebP into the workspace; returns the path. |
browser_pdf(filename?) | Save the page as PDF (headless Chromium). |
browser_wait(ms) | Wait a number of milliseconds. |
browser_evaluate(expression) | Run a JS expression in the page (opt-in). |
browser_status() | Whether the browser is running, plus URL + title. |
browser_close() | Close the browser; a later call relaunches it. |
Configuration
Configuration lives in the preset row in agent.cordis.yml:
- id: tool-browser
name: ./dist/index.js
config:
browser: chromium # chromium | firefox | webkit
headless: true # set false for a visible window
timeout: 30000 # per-action timeout in ms
allowEvaluate: false # enable browser_evaluate (runs JS in the page)
allowFile: false # allow file:// navigation (reads local files)
allowedSchemes: # extra schemes beyond http/https
- http:
- https:
All keys are optional and default to the safe values shown above.
Security model
The plugin is "arbitrary browser operation" with guard rails, not bare-metal access. Three invariants, all covered by unit tests:
- Scheme allow-list —
browser_navigaterejectsjavascript:,data:,file:(unless opted in),chrome:,vbscript:, and everyabout:URL except the inertabout:blank. A bare host is treated ashttps://. - Output-path sandbox —
browser_screenshot/browser_pdfresolve the filename against the session workspace and reject..traversal, absolute escape, NUL bytes, and disallowed extensions. - No host execution — the plugin never spawns a shell or evaluates code in the Node host.
browser_evaluateruns only in the page context and only whenallowEvaluate: true.
Full threat model and residual risks: SECURITY.md.
Development
npm install
npm run build # compile src/ → dist/
npm test # unit + integration tests (needs `npm run install-browsers` first)
npm run typecheck # strict type check only
npm run audit # dependency vulnerability scan
The integration tests spin up a local HTTP server and drive a real headless Chromium against it — no external network required.
Repository layout
├── agent.cordis.yml # full preset composition (standard + this plugin row)
├── preset.yml # preset display metadata
├── src/ # the plugin (guard.ts, browser.ts, index.ts)
├── test/ # unit + integration tests
├── dist/ # compiled plugin (loaded via ./dist/index.js; built, not committed)
├── .github/ # CI workflow
├── package.json # build/test scripts; playwright dependency
├── SECURITY.md # threat model and disclosure
├── CONTRIBUTING.md # contribution guide
├── CHANGELOG.md # release history
├── README.zh.md # 中文文档
└── LICENSE # MIT