feat(FN-3713): add web builtin tool opt-in for AI agents

Added agent permission policy model with persistence in core, and wired web-builtin tool opt-in enabling planning and synthesis web tools in the dashboard with corresponding docs and a changeset. Also added an inline fast-mode toggle wired into peer executor state, retry logic for cluster task-ID ov

Fusion-Task-Id: FN-3713
This commit is contained in:
Fusion
2026-05-07 12:03:33 -07:00
committed by gsxdsm
parent 51c6ddb9a5
commit 81da75f661
10 changed files with 83 additions and 3 deletions

View File

@@ -694,6 +694,40 @@ describe("createFnAgent", () => {
expect(createSessionArgs.customTools.map((tool) => tool.name)).toContain("fn_list_agents");
});
it("does not allow extra builtin tools in readonly sessions by default", async () => {
const { createFnAgent } = await import("../pi.js");
await createFnAgent({
cwd: "/tmp",
systemPrompt: "test",
tools: "readonly",
});
const createSessionArgs = createAgentSessionMock.mock.calls[0]?.[0] as { tools?: string[] };
expect(createSessionArgs.tools).toBeUndefined();
});
it("passes opt-in builtin web tool allowlist to createAgentSession", async () => {
const { createFnAgent } = await import("../pi.js");
await createFnAgent({
cwd: "/tmp",
systemPrompt: "test",
tools: "readonly",
builtinToolsAllowlist: ["WebSearch", "WebFetch"],
});
const createSessionArgs = createAgentSessionMock.mock.calls[0]?.[0] as { tools?: string[] };
expect(createSessionArgs.tools).toEqual(expect.arrayContaining([
"read",
"grep",
"find",
"ls",
"WebSearch",
"WebFetch",
]));
});
it("keeps caller customTools in coding sessions", async () => {
createCodingToolsMock.mockReturnValueOnce([{ name: "read" }, { name: "write" }] as any);
const customTool = {

View File

@@ -429,11 +429,15 @@ export interface FallbackModelUsedPayload {
timestamp?: string;
}
export type BuiltinWebToolName = "WebSearch" | "WebFetch";
export interface AgentOptions {
cwd: string;
systemPrompt: string;
tools?: "coding" | "readonly";
customTools?: ToolDefinition[];
/** Optional allowlist of builtin runtime web tools to keep enabled. */
builtinToolsAllowlist?: BuiltinWebToolName[];
onText?: (delta: string) => void;
onThinking?: (delta: string) => void;
onToolStart?: (name: string, args?: Record<string, unknown>) => void;
@@ -1243,7 +1247,7 @@ export async function createFnAgent(options: AgentOptions): Promise<AgentResult>
if (options.beforeSpawnSession) {
await options.beforeSpawnSession();
}
return createAgentSession({
const createSessionOptions: Parameters<typeof createAgentSession>[0] = {
cwd: options.cwd,
authStorage,
modelRegistry,
@@ -1253,7 +1257,18 @@ export async function createFnAgent(options: AgentOptions): Promise<AgentResult>
sessionManager,
settingsManager,
...(modelOverride ? { model: modelOverride } : {}),
});
};
if (options.builtinToolsAllowlist && options.builtinToolsAllowlist.length > 0) {
createSessionOptions.tools = [
...new Set([
...customToolList.map((tool) => tool.name),
...options.builtinToolsAllowlist,
]),
];
}
return createAgentSession(createSessionOptions);
};
const emitFallbackUsed = async (triggerPoint: "session-creation" | "prompt-time"): Promise<void> => {

View File

@@ -35,6 +35,9 @@ describe("LLMSynthesisProvider", () => {
);
expect(promptWithFallbackMock).toHaveBeenCalled();
expect(createFnAgentMock).toHaveBeenCalledWith(expect.objectContaining({
builtinToolsAllowlist: ["WebSearch", "WebFetch"],
}));
expect(result.citations).toEqual(["https://a"]);
expect(result.confidence).toBe(0.8);
expect(disposeMock).toHaveBeenCalled();

View File

@@ -6,6 +6,7 @@ import { ResearchProviderError } from "../types.js";
const log = createLogger("research:llm-synthesis");
const DEFAULT_TIMEOUT_MS = 120_000;
const SYNTHESIS_BUILTIN_WEB_TOOLS = ["WebSearch", "WebFetch"] as const;
const LARGE_MODEL_CONTEXT_CHARS = 100_000;
const SMALL_MODEL_CONTEXT_CHARS = 30_000;
@@ -54,6 +55,7 @@ export class LLMSynthesisProvider implements ResearchProvider {
const { session } = await createFnAgent({
cwd: this.options.projectRoot,
tools: "readonly",
builtinToolsAllowlist: [...SYNTHESIS_BUILTIN_WEB_TOOLS],
systemPrompt: "You synthesize research findings into concise, cited outputs.",
defaultProvider: modelSelection.provider,
defaultModelId: modelSelection.modelId,