fix(dsh-plugin-browserskill): serialize per-session commands (observation vs tool calls)

The daemon accepts only one unfinished command per session; the
thumbnail loop racing a model tool call produced 'session already has
an unfinished command' failures on both sides (found by the Phase 1-C
e2e: navigate/snapshot/click all errored while captures kept running).
Add a KeyedExecutor: every command for a session — tool calls and
observation captures alike — runs FIFO; queued tasks reject early on
abort, running tasks keep the runner's signal-driven kill.
This commit is contained in:
BB-fat
2026-08-14 06:41:49 +00:00
parent 2955260d11
commit bc5d9610a3
6 changed files with 93 additions and 9 deletions
@@ -14,6 +14,7 @@ import type { Context } from "@deepseek-ai/cordis";
import Schema from "@deepseek-ai/schemastery";
import { ObservationService } from "./observation";
import { registerObservationRoutes } from "./observation-http";
import { KeyedExecutor } from "./queue";
import { type BskRunner, createBskRunner } from "./runner";
import { SessionRegistry } from "./sessions";
import { type PluginConfig, registerTools } from "./tools";
@@ -65,10 +66,12 @@ export function apply(
};
const runner = options.runnerFactory?.(resolved.bskPath) ?? createBskRunner(resolved.bskPath);
const registry = new SessionRegistry(resolved.maxSessions);
const queue = new KeyedExecutor();
const observation = new ObservationService({
ctx,
runner,
registry,
queue,
options: {
enabled: resolved.observationEnabled,
thumbnailIntervalMs: resolved.thumbnailIntervalMs,
@@ -76,7 +79,7 @@ export function apply(
},
});
registerTools({ ctx, runner, registry, config: resolved, observation });
registerTools({ ctx, runner, registry, config: resolved, observation, queue });
// Route registration rides ctx.inject: the webServer service may be provided
// AFTER this plugin loads, and in headless compositions it never appears (the
// callback simply never runs, leaving the rest of the plugin unaffected).
@@ -121,6 +124,7 @@ export function apply(
export type { ObservationEvent, ObservationOptions, SessionObservation } from "./observation";
export { ObservationService } from "./observation";
export { registerObservationRoutes } from "./observation-http";
export { KeyedExecutor } from "./queue";
export type { BskRunner, BskRunResult, SpawnImpl } from "./runner";
export { BskError, createBskRunner } from "./runner";
export { SessionRegistry } from "./sessions";