fix(browser-skill): route DSH usage through injected tools

This commit is contained in:
BB-fat
2026-08-17 07:04:36 +00:00
parent 51df868008
commit 51085b960b
4 changed files with 44 additions and 12 deletions
@@ -1,8 +1,8 @@
// Assemble the plugin's skill content: the dsh-specific prelude (committed in
// this package at skill/prelude.md) followed VERBATIM by the canonical CLI
// skill body (repo-root skill/SKILL.md — the same source crates/bsk-cli's
// build.rs mirrors, so the two can never drift apart). The original's YAML
// frontmatter is stripped: name/description are supplied by the registration.
// Assemble the plugin's skill content: a dsh-specific routing prelude, the
// canonical CLI skill body verbatim (repo-root skill/SKILL.md — the same source
// crates/bsk-cli's build.rs mirrors), then a dsh routing reminder postlude. The
// original's YAML frontmatter is stripped: name/description are supplied by the
// registration.
// Output: src/skill-content.generated.ts (gitignored, built before tsdown and
// before vitest) so the markdown ships inlined in the bundle and registration
// is a pure in-memory read — never a per-step disk access.
@@ -14,6 +14,7 @@ const here = dirname(fileURLToPath(import.meta.url));
const pkg = join(here, "..");
const prelude = readFileSync(join(pkg, "skill", "prelude.md"), "utf8");
const upstream = readFileSync(join(pkg, "..", "..", "skill", "SKILL.md"), "utf8");
const postlude = readFileSync(join(pkg, "skill", "postlude.md"), "utf8");
function stripFrontmatter(markdown) {
if (!markdown.startsWith("---\n")) return markdown;
@@ -23,16 +24,16 @@ function stripFrontmatter(markdown) {
const NAME = "browser-skill";
const DESCRIPTION =
"Browser automation against the user's logged-in Chromium through this plugin's browser_* " +
"Browser automation against the user's logged-in Chromium through this plugin's injected browser_* tools; never invoke bsk through bash or shell. " +
"tools (bsk engine): visit and read pages, fill forms, click through flows, scrape data, " +
"smoke-test UI changes. Use when a task needs a real browser; requires the bsk CLI and the " +
"browser-skill extension connected.";
const content = `${prelude.trim()}\n\n${stripFrontmatter(upstream).trim()}\n`;
const content = `${prelude.trim()}\n\n${stripFrontmatter(upstream).trim()}\n\n${postlude.trim()}\n`;
const banner =
"// GENERATED by scripts/build-skill-content.mjs — do not edit.\n" +
"// Sources: skill/prelude.md (this package) + ../../../skill/SKILL.md (repo root).\n";
"// Sources: skill/prelude.md + ../../../skill/SKILL.md + skill/postlude.md.\n";
writeFileSync(
join(pkg, "src", "skill-content.generated.ts"),
@@ -0,0 +1,10 @@
---
## DSH routing reminder
The CLI-oriented reference above describes behavior, not the execution mechanism in
DeepSeek Harness. For every browser operation, call the injected `browser_*` model
tool. Do not translate these examples into `bash` commands and do not execute `bsk`
directly; the plugin invokes `bsk` internally. In particular, always begin with
`browser_session_start`, pass its returned session id to subsequent `browser_*`
tools, and finish with `browser_session_stop`.
@@ -1,9 +1,25 @@
# browser-skill (dsh edition)
## DSH tool-routing requirement (highest priority)
This skill is running inside DeepSeek Harness, where the plugin has injected
structured `browser_*` model tools. **Always call those injected `browser_*` tools
for browser work. Never use `bash`, `shell`, `exec`, or another tool to invoke the
`bsk` CLI directly.** The CLI command names shown below are only conceptual
mappings for understanding the tools; they are not instructions to run commands.
If a `browser_*` tool is available, using the CLI directly is incorrect because it
bypasses plugin session ownership, observation UI, tool cards, and cleanup.
Use the injected tool whose name matches the operation, for example:
`browser_session_start`, `browser_navigate`, `browser_observe`, `browser_snapshot`,
`browser_click`, `browser_fill`, `browser_press`, `browser_screenshot`, and
`browser_session_stop`. Pass arguments as the tool schema specifies (not CLI flags).
Drive the user's **real Chromium browser** (logins, cookies, Agent Window isolation)
through this plugin's `browser_*` tools. Each tool spawns `bsk <cmd> --json` — the
same engine the canonical CLI skill documents — so the workflow, judgment rules, and
safety constraints are identical.
through this plugin's `browser_*` tools. Each injected tool is implemented by the
plugin using the `bsk` engine internally — the model must not invoke that engine
itself. The workflow, judgment rules, and safety constraints remain identical to
the canonical CLI skill.
> **Tool availability**: invoking this skill (which you just did) makes the plugin's
> `browser_*` tools available for the rest of the session — they are injected on
@@ -26,9 +26,14 @@ describe("registerBskSkill", () => {
expect(skill.name).toBe("browser-skill");
expect(typeof skill.description).toBe("string");
expect(String(skill.description)).toContain("browser_*");
// body = dsh prelude + canonical CLI skill body
// body = dsh prelude + canonical CLI skill body + dsh postlude
const content = String(skill.content);
expect(content).toContain("browser_observe");
expect(String(skill.description)).toContain("never invoke bsk through bash or shell");
expect(content).toContain("Always call those injected `browser_*` tools");
expect(content).toContain("Never use `bash`, `shell`, `exec`");
expect(content).toContain("## DSH routing reminder");
expect(content.trimEnd()).toMatch(/always begin with[\s\S]*`browser_session_start`[\s\S]*`browser_session_stop`\.$/);
expect(content).toContain("Owned sessions only");
expect(content).toContain("Mandatory workflow");
expect(content).toContain("Refs invalidate after navigation");