docs(skill): trim agent instructions to a leaner core

The CLI skill had grown to 20.8 KB of command tables and flag catalogs that
`bsk --help` already answers at runtime, so every agent paid for them on every
task. Rewrite both skills around the durable rules an agent cannot rediscover
-- session lifecycle, the observe/act/observe loop, the Agent Window boundary,
and the stop-on-success contract -- and delegate flag details to the CLI's own
help output. The CLI skill drops to 6.9 KB (-67%) and the DSH skill to 5.1 KB.

Keep the parts that help discovery has no answer for: the literal
`[hover first: ...]` observation marker, the `request-help` outcome values, the
complete command-name inventory, and the required flags agents guess wrong
(`fill --value`, `select --value`, `screenshot --out`, `emulate --device`).
Also bind `--no-focus` explicitly to `bsk session start`, since a bare mention
led agents to pass it to `bsk navigate`.

Validated with 36 agent runs of the `core` eval suite against a real browser,
comparing the old and new instructions with everything else held constant.
Pass rate did not regress (the lean version was better on `form-controls`) and
token cost per task came out even. The inventory and flag signatures cut
`--help` round-trips from 1.7 to 0.7 per task and removed the invented
`bsk back` / `bsk forward` calls the first draft produced.

Tighten the DSH size assertion to a range so the budget is bounded from both
sides and accidental truncation fails the test.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
drakezhang
2026-09-01 18:39:19 +08:00
co-authored by Cursor
parent 536d3a9844
commit 59d6533eff
4 changed files with 266 additions and 676 deletions
+66 -109
View File
@@ -5,142 +5,99 @@ description: Browser automation through six injected domain tools.
# browser-skill for DeepSeek Harness
Drive the user's real Chromium browser through the six structured domain tools provided by this
plugin. The browser keeps the user's existing logins and cookies while isolating automation in an
Agent Window. Every call must include the tool's `action` field; other fields depend on that action.
Drive the user's logged-in Chromium through this plugin's structured browser tools. Automation is
isolated in an Agent Window; user-window tabs remain protected unless explicitly borrowed.
## Tool availability
Loading this skill reveals six tools for the rest of the conversation:
Loading this skill reveals the browser tools for the rest of the conversation. All browser work
must use the injected tools directly. Do not invoke another process to control the browser. Direct
tool calls preserve session ownership, the live observation window, cancellation, attachments, and
cleanup.
- `browser_session` owns session lifecycle.
- `browser_page` handles navigation and lifecycle waits.
- `browser_inspect` reads semantic, visual, console, and network state.
- `browser_interact` performs normal page interactions.
- `browser_tabs` manages Agent Window tabs and temporary user-tab borrowing.
- `browser_assist` handles human help, window size, and device emulation.
The six tools are:
- `browser_session`: start, stop, or list plugin-owned sessions.
- `browser_page`: navigate, back, forward, reload, or wait for navigation.
- `browser_inspect`: observe, snapshot, html, screenshot, console, or network.
- `browser_interact`: click, hover, fill, select, or press.
- `browser_tabs`: list, create, select, close, borrow, or return tabs.
- `browser_assist`: resize the Agent Window, emulate a device, or request help from the user.
Every call includes an `action`. Treat each loaded tool schema as authoritative for its actions and
parameters; do not guess fields. All browser work must use the injected tools directly so session
ownership, cancellation, attachments, observation UI, and cleanup remain intact. Do not invoke
another process to control the browser.
## Mandatory workflow
Every browser task follows this lifecycle:
1. Call `browser_session` with `action: "start"` and retain its returned `sessionId`.
2. Use that session for every later call. Pass `session` explicitly when more than one exists.
3. Call `browser_session` with `action: "stop"` when the goal is reached or an error ends the task.
Treat cleanup like a finally path unless the user explicitly asks to keep the session open.
Conceptual sequence:
Every task owns a bounded plugin session:
```text
browser_session({ action: "start", url: "https://example.com" })
browser_inspect({ action: "observe", session: sessionId })
browser_interact({ action: "click", session: sessionId, target: "@e4" })
browser_inspect({ action: "observe", session: sessionId })
browser_session({ action: "start", ... })
... use the returned sessionId for browser work ...
browser_session({ action: "stop", session: sessionId })
```
The plugin owns only sessions it starts. Never guess or reuse a session id from another program.
When `session` is omitted, most actions use the current owned session, but explicit ids are safer.
Pass the session explicitly when more than one exists. Never guess or reuse an id owned by another
program. Stop in a finally-style path on success and failure unless the user explicitly asks to keep
the session open. Stopping also returns borrowed tabs.
## Stop when the goal is met
## Work toward one observable goal
Browser work is bounded. Define an observable success condition, take the shortest path, and stop
as soon as success is visible. Do not refresh or click after success. If an action fails twice
without progress, pause instead of brute-forcing. If state is ambiguous, make at most one fresh
observation and then ask the user.
- Derive a concrete success condition from the user's request.
- Take the shortest purposeful path: observe, act, then make at most one observation to confirm an
ambiguous result.
- Once success is visible, do not click, refresh, navigate, switch tabs, or perform extra checks.
- If a human-only step appears or two attempts make no progress, request help instead of
brute-forcing.
## Observe and interact
## Observe, act, observe
Use `browser_inspect` with `action: "observe"` as the primary semantic page view. It returns roles,
states, text, and `@eN` refs. Use `action: "snapshot"` when a static accessibility tree is more useful.
Prefer a fresh ref over a raw CSS selector for interactions.
Use `browser_inspect` action `observe` as the primary semantic page view. It returns roles, states,
text, and `@eN` refs. Prefer fresh refs over raw selectors. Refs invalidate after navigation and may
also become stale after large DOM changes, so observe again before the next interaction.
Refs invalidate after navigation and may become stale after large DOM changes. Observe again after
navigation or a meaningful DOM update before using another ref. If an observation identifies a
hover-triggered surface, hover the trigger, observe again, then interact with the revealed control.
Use `browser_interact` for click, hover, fill, select, and key actions. An observation marks a
hover-only surface as `@e1 button "Products" [hover first: Shoes | Bags]`. The listed items are
labels, not usable refs: hover the trigger, observe again, then act on the revealed item's own ref.
Do not click the trigger itself unless the user wants the trigger's action.
Use `browser_interact` as follows:
Escalate reading only as needed:
- `action: "click"`: requires `target`; optionally accepts `button` and `clickCount`.
- `action: "hover"`: requires `target`; optionally accepts `modifiers`, `settleMs`, and `timeoutMs`.
- `action: "fill"`: requires `target` and `value`; `noClear` appends instead of replacing.
- `action: "select"`: requires `target` and a non-empty `values` array. Values are option value
attributes, not necessarily the visible labels.
- `action: "press"`: requires `key`; optionally focus `target` first and set `holdMs`.
1. `observe` for normal understanding and interaction refs.
2. `snapshot` when a stricter static accessibility tree is more useful.
3. `html` for exact markup or hidden metadata that semantic views cannot provide.
4. `screenshot` for layout, styling, canvas, images, or requested visual evidence.
## Navigate and wait
Do not start with raw HTML or screenshots merely to discover ordinary controls. When interaction is
needed, obtain a fresh observation before acting on screenshot or HTML findings.
Use `browser_page` with these actions:
Use `browser_page` for purposeful navigation, history, reload, or a lifecycle wait. Avoid speculative
waits when no navigation is expected. After any page change, discard old refs and observe again.
- `navigate` requires `url` and optionally accepts `waitUntil` and `timeoutMs`.
- `back` and `forward` move one history entry and optionally accept lifecycle wait fields.
- `reload` refreshes the current tab; set `hard: true` only when bypassing cache is necessary.
- `wait` waits for an expected navigation after an interaction that did not already wait.
## Respect the Agent Window boundary
Avoid speculative waits when no navigation is expected. After any page change, discard old refs and
observe again.
Use `browser_tabs` to list returned tab ids before selecting, closing, borrowing, or returning tabs.
Borrow a user tab only for the immediate task, and return it as soon as that step is complete. Never
invent a tab id or keep a personal tab borrowed across unrelated work.
## Reading priority
## Ask the human when needed
Escalate page reading only as needed:
Use `browser_assist` action `request-help` for login, captcha, OTP, payment confirmation, consent, or
another step the user must complete. Give a precise prompt and highlight fresh targets when concrete
controls are involved. Use completion criteria only for a clear stable success signal.
1. `browser_inspect` with `action: "observe"` for normal understanding and refs.
2. The same tool with `action: "snapshot"` for a stricter static tree.
3. `action: "html"` for exact markup that semantic views cannot provide; keep `maxBytes` bounded and
use only a fresh ref when scoping a subtree.
4. `action: "screenshot"` for layout, styling, canvas, or genuinely visual evidence. A fresh `ref`
crops to an element; omit it for the visible tab.
Resume only after the user continues or the criteria complete. Treat cancellation as rejection and
timeout as a blocker rather than retrying. Observe again after control returns before reasoning about
the new state or using refs.
Do not start with raw HTML or a screenshot merely to discover ordinary controls.
The same tool can resize the Agent Window or emulate a device when the task requires visual or
responsive testing. Emulation is scoped to one tab.
## Tabs and the Agent Window boundary
## Debug and recover without wandering
Use `browser_tabs` with these actions:
Use `browser_inspect` console or network actions only for relevant, bounded, read-only diagnostics.
Continue from returned sequence cursors instead of rereading the same buffer.
- `list` returns visible tabs; `scope: "user"` finds a user tab before borrowing.
- `create` makes an Agent Window tab and optionally accepts `url`, `active`, and `index`.
- `select` focuses an Agent Window tab and requires a returned `tabId`.
- `close` closes an Agent Window tab and requires a returned `tabId`.
- `borrow` moves a user tab into the Agent Window and requires a listed user `tabId`.
- `return` restores a borrowed tab and requires that borrowed `tabId`.
- Stale ref: observe again and retry the intended action once.
- Unknown tab: list tabs instead of guessing.
- Unknown session: list owned sessions or start one; never try foreign ids.
- Timeout: inspect current state before deciding whether one longer purposeful wait is useful.
- Unrecoverable failure: report the blocker and stop the owned session.
Return borrowed tabs as soon as the immediate task finishes. Stopping the session is only fallback
cleanup. Never invent a tab id.
## Human help and display controls
Use `browser_assist` with `action: "request-help"` for login, captcha, OTP, payment confirmation,
consent, or another human-only step. `prompt` is required. Optional `targets` highlight fresh refs or
selectors. `completionCriteria` can detect explicit stable success through URL, selector, or text
conditions. After the user continues or criteria complete, observe again before reasoning about the
new state.
Use `action: "resize"` with `width` and `height` to resize the outer Agent Window. Use
`action: "emulate"` with a device preset or explicit width and height; `mobile: true` also requires
dimensions. Use `off: true` alone to clear emulation. Emulation is per tab.
## Read-only diagnostics
Use `browser_inspect` with `action: "console"` for buffered logs and exceptions, and
`action: "network"` for response and failure metadata. Both accept `tabId`, `since`, `limit`, and
`maxTextChars`; continue with the returned sequence cursor rather than rereading the same entries.
Console additionally accepts `includeStack`.
Arbitrary page script evaluation and interaction recording are intentionally unsupported. Do not
invent tool names or route around that limitation.
## Error handling and final checklist
On a stale ref, observe again and retry once. On an unknown tab, list tabs instead of guessing. On
an unknown session, list owned sessions or start one; never try foreign ids. On timeout, decide
whether one longer purposeful wait is justified, and never immediately repeat a failed history
action without first checking page state.
Before finishing, verify that the observable goal is met or the blocker is clear, borrowed tabs are
returned when practical, the owned session is stopped, and no extra page action occurs after
success.
Arbitrary page-script evaluation and interaction recording are intentionally unsupported. Do not
invent tools or route around those limits.
@@ -47,7 +47,10 @@ describe("registerBskSkill", () => {
expect(content).toContain("Mandatory workflow");
expect(content).toContain("Refs invalidate after navigation");
expect(content).toContain("evaluation and interaction recording are intentionally unsupported");
expect(content.length).toBeGreaterThan(6_000);
// Keep the lazily injected instructions inside a bounded prompt budget,
// while the lower bound catches accidental truncation of the guidance.
expect(content.length).toBeGreaterThan(3_000);
expect(content.length).toBeLessThan(6_000);
expect(skill.source).toBe("bundled");
// Source frontmatter is registration metadata and must not leak into the body.
expect(content.startsWith("---")).toBe(false);