fix(dsh-plugin): stabilize skill loading and browser sessions

Register bundled browser instructions at agent scope, prioritize foreground commands over thumbnail capture, and unify cancellation and session cleanup.
This commit is contained in:
drakezhang
2026-08-27 19:59:20 +08:00
parent 554861d6b1
commit 1a70d2200e
10 changed files with 501 additions and 92 deletions
@@ -19,7 +19,7 @@ import { registerObservationRoutes } from "./observation-http";
import { KeyedExecutor } from "./queue";
import { type BskRunner, createBskRunner } from "./runner";
import { SessionRegistry } from "./sessions";
import { registerBskSkill } from "./skill";
import { armAgentScopedBskSkill, registerBskSkill } from "./skill";
import { type PluginConfig, registerTools } from "./tools";
export const name = "dsh-plugin-browserskill";
@@ -95,6 +95,11 @@ export function apply(
// ONLY model-visible advertisement — the tool suite reveals itself on a
// successful skill invocation (or a session whose history already has one).
const unregisterSkill = registerBskSkill(ctx);
// A same-name CLI skill left in ~/.agents is discovered in the nearer preset
// layer and shadows the global registration above. Re-register through every
// exact agent context at startup so DSH always loads the browser_* protocol
// instructions; the shared CLI skill remains untouched for other agents.
const disarmAgentSkill = armAgentScopedBskSkill(ctx);
const removeSuite = resolved.lazyTools
? armLazyTools(ctx, () =>
registerTools({ ctx, runner, registry, config: resolved, observation, queue }),
@@ -135,17 +140,15 @@ export function apply(
ctx.effect(() => {
return () => {
removeSuite();
disarmAgentSkill();
unregisterSkill();
removeRoutes();
disarmArchiveCleanup();
observation.dispose();
runner.killAll();
const stops = registry
.ownedIds()
.map((sessionId) =>
runner.run(["session", "stop", sessionId], { timeoutMs: 15_000 }).catch(() => {}),
);
return Promise.all(stops).then(() => {});
.map((sessionId) => observation.stopSession(sessionId).catch(() => false));
return Promise.all(stops).then(() => observation.dispose());
};
});
}