mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
feat(dsh-plugin-browserskill): reveal tool schemas only after skill invocation
Progressive disclosure, final stage: with lazyTools on (default), the eleven browser_* tool schemas stay out of the system prompt — the browser-skill catalog entry is the sole advertisement. One successful skill invocation (model tool call via tools/result, or a /browser-skill user gesture via the session append feed) registers the suite for the process lifetime, idempotent on repeats; session resume is covered by scanning durable events for a past tool/call+tool/result pair (or skill-invocation message) on session entry and at apply time. lazyTools: false keeps the legacy always-on registration. Investigation (recorded in the ticket): tools/result carries normalized name/arguments with an isError discriminant; dsh 0.1 has no official global visibility switch (ctx.tools.restrict is agent-scoped and throws from a plain context), making conditional registration the intended pattern; per-step assembly picks the new suite up on the next step, and visibility changes are first-class inputs of tool-skill's catalog digest. registerTools now returns the combined disposer via a prototype-preserving tracking overlay; the skill prelude tells the model the browser_* tools unlock on skill load. Tests: +8 (hidden-before/reveal/idempotent/gesture/history hit+miss/dispose/ scanner pairing/two-state apply wiring). 124/124 green.
This commit is contained in:
@@ -26,6 +26,13 @@ export interface PluginConfig {
|
||||
observationEnabled: boolean;
|
||||
thumbnailIntervalMs: number;
|
||||
idleIntervalMs: number;
|
||||
/**
|
||||
* Lazy tool-schema injection: when true (default), the browser_* tools are
|
||||
* registered only after the `browser-skill` skill has been successfully
|
||||
* invoked (skill catalog entry advertises alone until then); when false,
|
||||
* the suite is registered at apply time (legacy always-on behavior).
|
||||
*/
|
||||
lazyTools: boolean;
|
||||
}
|
||||
|
||||
export interface ToolDeps {
|
||||
@@ -136,9 +143,22 @@ function abortError(): Error {
|
||||
return error;
|
||||
}
|
||||
|
||||
/** Register the full browser tool suite. */
|
||||
export function registerTools(deps: ToolDeps): void {
|
||||
const { ctx, registry } = deps;
|
||||
/** Register the full browser tool suite; returns the combined unregister disposer. */
|
||||
export function registerTools(deps: ToolDeps): () => void {
|
||||
// Track every registration through a prototype-preserving overlay so the
|
||||
// lazy-injection path can dispose the whole suite in one call.
|
||||
const disposers: (() => void)[] = [];
|
||||
const ctx = Object.create(deps.ctx) as ToolDeps["ctx"];
|
||||
const toolsOverlay = Object.create(deps.ctx.tools) as ToolDeps["ctx"]["tools"];
|
||||
const originalRegister = deps.ctx.tools.register;
|
||||
toolsOverlay.register = (definition: Parameters<typeof originalRegister>[0]) => {
|
||||
const dispose = originalRegister.call(deps.ctx.tools, definition);
|
||||
// Test doubles occasionally return the registry map instead of a disposer.
|
||||
if (typeof dispose === "function") disposers.push(dispose);
|
||||
return dispose;
|
||||
};
|
||||
ctx.tools = toolsOverlay;
|
||||
const { registry } = deps;
|
||||
|
||||
ctx.tools.register(
|
||||
defineTool({
|
||||
@@ -934,4 +954,7 @@ export function registerTools(deps: ToolDeps): void {
|
||||
presentResult: presentTerminalResult,
|
||||
}),
|
||||
);
|
||||
return () => {
|
||||
for (const dispose of disposers.splice(0)) dispose();
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user