mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
feat(dsh-plugin-browserskill): host-side ObservationService for the PiP overlay (Phase 1-A)
Per-session live observation for owned sessions only, feeding the upcoming client overlay: - State model: SessionObservation (sessionId/url/action/since/ thumbnailAttachmentId/lastError), one entry per owned session; added on start, removed on stop, cleared on dispose. - Instrumentation: the shared runBsk wrapper begins/ends an action (tool name mapped to a verb) around every model-facing call and tags the child with its session id; action end triggers an immediate thumbnail refresh. - Thumbnail loop: 1.5s cadence while active, 8s idle downclock, x3 failure backoff, silent frame retention on errors; captures run OUTSIDE the instrumentation (no action events, no current-pointer movement) and land in the attachment store by reference. - Interrupt: runner.killFor(tag) kills exactly the tagged in-flight bsk children — chat-Stop-equivalent semantics for the current or a named owned session; foreign ids and the no-in-flight case return false. - Remote seam: dsh 0.1's Typert Remote pipeline and forwarded-event allowlist are closed to out-of-tree packages, so state/events/ interrupt are served over the documented webServer route seam: GET /bsk-observation/state, GET /bsk-observation/events (SSE), POST /bsk-observation/interrupt; routes mount only when a webServer service exists (headless compositions skip them). - Config: observationEnabled (true), thumbnailIntervalMs (1500), idleIntervalMs (8000). Tests (+13, 72 total): state machine + event stream, cadence fast/idle/ backoff, headless no-store silence, observation-traffic isolation, interrupt routing (default/specified/none/foreign), HTTP route handlers, disabled-webServer no-op.
This commit is contained in:
@@ -3,11 +3,29 @@ import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import type { ToolDefinition, ToolRunContext } from "@deepseek-ai/dsh-tools";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { BskRunOptions, BskRunResult } from "../src/runner";
|
||||
import { ObservationService } from "../src/observation";
|
||||
import type { BskRunner, BskRunOptions, BskRunResult } from "../src/runner";
|
||||
import { SessionRegistry } from "../src/sessions";
|
||||
import { type PluginConfig, registerTools } from "../src/tools";
|
||||
|
||||
const CONFIG: PluginConfig = { bskPath: "bsk", defaultTimeoutMs: 120_000, maxSessions: 5 };
|
||||
const CONFIG: PluginConfig = {
|
||||
bskPath: "bsk",
|
||||
defaultTimeoutMs: 120_000,
|
||||
maxSessions: 5,
|
||||
observationEnabled: false,
|
||||
thumbnailIntervalMs: 1500,
|
||||
idleIntervalMs: 8000,
|
||||
};
|
||||
|
||||
/** An observation service with the feature off: instrumentation becomes a no-op. */
|
||||
function disabledObservation(deps: { ctx: unknown; runner: BskRunner; registry: SessionRegistry }) {
|
||||
return new ObservationService({
|
||||
ctx: deps.ctx as never,
|
||||
runner: deps.runner,
|
||||
registry: deps.registry,
|
||||
options: { enabled: false, thumbnailIntervalMs: 1500, idleIntervalMs: 8000 },
|
||||
});
|
||||
}
|
||||
|
||||
interface FakeCall {
|
||||
args: string[];
|
||||
@@ -58,6 +76,7 @@ function fakeRunner(responses: Record<string, unknown>) {
|
||||
};
|
||||
},
|
||||
killAll() {},
|
||||
killFor: () => 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -95,7 +114,14 @@ function setup(
|
||||
const { ctx, tools } = makeCtx(services);
|
||||
const { runner, calls } = fakeRunner(responses);
|
||||
const registry = new SessionRegistry(maxSessions);
|
||||
registerTools({ ctx: ctx as never, runner, registry, config: { ...CONFIG, maxSessions } });
|
||||
const config = { ...CONFIG, maxSessions };
|
||||
registerTools({
|
||||
ctx: ctx as never,
|
||||
runner: runner as BskRunner,
|
||||
registry,
|
||||
config,
|
||||
observation: disabledObservation({ ctx, runner: runner as BskRunner, registry }),
|
||||
});
|
||||
return { tools, calls, registry };
|
||||
}
|
||||
|
||||
@@ -542,8 +568,15 @@ describe("error and cancellation semantics", () => {
|
||||
throw Object.assign(new Error("spawn bsk ENOENT"), { code: "ENOENT" });
|
||||
},
|
||||
killAll() {},
|
||||
killFor: () => 0,
|
||||
};
|
||||
registerTools({ ctx: ctx as never, runner, registry, config: CONFIG });
|
||||
registerTools({
|
||||
ctx: ctx as never,
|
||||
runner,
|
||||
registry,
|
||||
config: CONFIG,
|
||||
observation: disabledObservation({ ctx, runner, registry }),
|
||||
});
|
||||
const start = tools.get("browser_session_start");
|
||||
await expect(start?.execute({}, makeExec())).rejects.toThrow(/BrowserSkill must be installed/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user