feat: configure xdev prompt docs
This commit is contained in:
@@ -4036,6 +4036,28 @@ export const SETTINGS_SCHEMA = {
|
||||
},
|
||||
},
|
||||
|
||||
"tools.xdevDocs": {
|
||||
type: "enum",
|
||||
values: ["inline", "builtins", "catalog"] as const,
|
||||
default: "builtins",
|
||||
ui: {
|
||||
tab: "tools",
|
||||
group: "Discovery & MCP",
|
||||
label: "xd:// Prompt Docs",
|
||||
description:
|
||||
"Choose which mounted-device docs and schemas are inlined in the system prompt. Built-ins keeps core tools inline while MCP and extension tools stay on-demand.",
|
||||
options: [
|
||||
{ value: "inline", label: "All Devices", description: "Inline docs and schemas for every mounted device." },
|
||||
{
|
||||
value: "builtins",
|
||||
label: "Built-ins Only",
|
||||
description: "Inline built-in docs; fetch MCP and extension docs on demand.",
|
||||
},
|
||||
{ value: "catalog", label: "Catalog Only", description: "List every device; fetch all docs on demand." },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// MCP
|
||||
"mcp.enableProjectConfig": {
|
||||
type: "boolean",
|
||||
|
||||
@@ -2596,7 +2596,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
||||
cwd,
|
||||
additionalWorkspaceRoots: sessionManager.getAdditionalDirectories(),
|
||||
xdevTools: toolSession.xdevRegistry?.entries() ?? [],
|
||||
xdevDocs: toolSession.xdevRegistry?.docsAll() ?? "",
|
||||
xdevDocs: toolSession.xdevRegistry?.docsAll(settings.get("tools.xdevDocs")) ?? "",
|
||||
autoQaEnabled: !restrictToolNames && isAutoQaEnabled(settings),
|
||||
resolvedCustomPrompt: options.customSystemPrompt,
|
||||
skills: session?.skills ?? skills,
|
||||
|
||||
@@ -60,6 +60,9 @@ export const XDEV_KEEP_TOP_LEVEL: Record<string, true> = {
|
||||
*/
|
||||
export const XDEV_TRANSPORT_TOOLS: Record<string, true> = { read: true, write: true };
|
||||
|
||||
/** Controls which mounted-device docs are inlined into the system prompt. */
|
||||
export type XdevDocsMode = "inline" | "builtins" | "catalog";
|
||||
|
||||
/**
|
||||
* Whether an enabled tool is presented under `xd://` (rather than top-level)
|
||||
* while the `xd://` transport is active. Discoverable tools mount unless they
|
||||
@@ -273,11 +276,16 @@ export class XdevRegistry {
|
||||
* Dynamic mounts embed at most {@link EXTERNAL_DESCRIPTION_CAP} description
|
||||
* chars (schema always intact); `read xd://<tool>` returns the full text.
|
||||
*/
|
||||
docsAll(): string {
|
||||
docsAll(mode: XdevDocsMode = "inline"): string {
|
||||
const sections: string[] = [];
|
||||
const overflow: Tool[] = [];
|
||||
let used = 0;
|
||||
for (const tool of this.list()) {
|
||||
const isBuiltin = this.#builtins.has(tool.name);
|
||||
if (mode === "catalog" || (mode === "builtins" && !isBuiltin)) {
|
||||
overflow.push(tool);
|
||||
continue;
|
||||
}
|
||||
const descriptionCap = this.#dynamic.has(tool.name) ? XdevRegistry.EXTERNAL_DESCRIPTION_CAP : undefined;
|
||||
const docs = renderDocs(tool, "##", descriptionCap);
|
||||
if (docs.length > XdevRegistry.DOCS_PER_DEVICE_CAP || used + docs.length > XdevRegistry.DOCS_TOTAL_BUDGET) {
|
||||
|
||||
@@ -186,6 +186,7 @@ describe("read and write route xd:// device URLs", () => {
|
||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "write-xdev-docs-"));
|
||||
try {
|
||||
const session = xdevSession(tempDir);
|
||||
expect(session.settings.get("tools.xdevDocs")).toBe("builtins");
|
||||
await createTools(session);
|
||||
const mounted = session.xdevRegistry?.list() ?? [];
|
||||
expect(mounted.length).toBeGreaterThan(0);
|
||||
@@ -208,30 +209,37 @@ describe("read and write route xd:// device URLs", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("docsAll truncates external (dynamic-mount) descriptions to the cap; built-ins and read xd:// stay full", async () => {
|
||||
it("docsAll supports inline, builtins, and catalog prompt modes", async () => {
|
||||
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "write-xdev-external-"));
|
||||
try {
|
||||
const session = xdevSession(tempDir);
|
||||
expect(session.settings.get("tools.xdevDocs")).toBe("builtins");
|
||||
await createTools(session);
|
||||
const registry = session.xdevRegistry;
|
||||
if (!registry) throw new Error("expected xdev registry");
|
||||
const mounted = registry.list();
|
||||
|
||||
const longDescription = `LEDE ${"y".repeat(XdevRegistry.EXTERNAL_DESCRIPTION_CAP * 3)} TAIL`;
|
||||
const longDescription = "LEDE " + "y".repeat(XdevRegistry.EXTERNAL_DESCRIPTION_CAP * 3) + " TAIL";
|
||||
const external = Object.create(mounted[0]!) as (typeof mounted)[number];
|
||||
Object.defineProperty(external, "name", { value: "mcp_external_tool" });
|
||||
Object.defineProperty(external, "description", { value: longDescription });
|
||||
registry.reconcile([external]);
|
||||
|
||||
const docs = registry.docsAll();
|
||||
// External device: schema section present, description cut at the cap.
|
||||
expect(docs).toContain("## mcp_external_tool");
|
||||
expect(docs).toContain("LEDE ");
|
||||
expect(docs).not.toContain("TAIL");
|
||||
expect(docs).toContain("… (full docs: read xd://mcp_external_tool)");
|
||||
// Built-in devices keep their full curated description.
|
||||
expect(docs).toContain(mounted[0]!.description ?? "");
|
||||
// On-demand docs return the untruncated text.
|
||||
const inlineDocs = registry.docsAll("inline");
|
||||
expect(inlineDocs).toContain("## mcp_external_tool");
|
||||
expect(inlineDocs).toContain("LEDE ");
|
||||
expect(inlineDocs).not.toContain("TAIL");
|
||||
expect(inlineDocs).toContain("… (full docs: read xd://mcp_external_tool)");
|
||||
|
||||
const builtinsDocs = registry.docsAll("builtins");
|
||||
expect(builtinsDocs).toContain("## ");
|
||||
expect(builtinsDocs).not.toContain("## mcp_external_tool");
|
||||
expect(builtinsDocs).toContain("- xd://mcp_external_tool —");
|
||||
|
||||
const catalogDocs = registry.docsAll("catalog");
|
||||
expect(catalogDocs).not.toContain(`## ${mounted[0]!.name}`);
|
||||
expect(catalogDocs).toContain("- xd://");
|
||||
expect(catalogDocs).toContain("- xd://mcp_external_tool —");
|
||||
expect(registry.docs("mcp_external_tool")).toContain("TAIL");
|
||||
} finally {
|
||||
await removeWithRetries(tempDir);
|
||||
|
||||
Reference in New Issue
Block a user