Every agent gets the live web: web_search and fetch_url by default
"Be ashamed to guess APIs blindly; be proud to look things up carefully" — until now the agents had no way to look anything up: no search, no fetch, so market research, API usage, and competitive claims were all invented from model memory. agent-web-tools.ts, carried by every agent in every run (same pattern as the workspace tools, no grant needed): - web_search: keyless DuckDuckGo HTML backend; parses titles, unwraps the /l/?uddg= redirect hrefs, pairs snippets; reports honestly when nothing parses instead of fabricating results. Verified against the live endpoint. - fetch_url: readable-text extraction (dependency-free HTML-to-text with entity decoding), 20k-char page cap, 2MB body cap read in chunks, 30s timeout. - SSRF guard on both: hostnames resolve before connecting and loopback, private, link-local, CGNAT, and reserved ranges are refused — re-checked on every redirect hop, so a public page cannot bounce the agent into 127.0.0.1 or the cloud metadata address. IPv6 and v4-mapped forms covered. - The fetcher is injectable, so the test suite covers the guard, redirect policy, truncation, extraction, and the DuckDuckGo parser without touching the network.
This commit is contained in:
1 parent
26de3129b1
commit
80e70649c3
4 files changed
+405
-2
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import type { ProviderService } from "./provider-service";
|
||||
import { AGENT_TOOLS, dispatchAgentTool, type AgentToolContext, type AgentToolDeps } from "./agent-tools";
|
||||
import { executeWebTool, isWebTool, WEB_TOOLS } from "./agent-web-tools";
|
||||
import { executeWorkspaceTool, isWorkspaceTool, WORKSPACE_TOOLS } from "./agent-workspace-tools";
|
||||
import { canonicalMentionHandle } from "../../shared/mention-handles";
|
||||
import type { AgentTurnMessage, ToolDefinition } from "./provider-tool-turn";
|
||||
@@ -38,7 +39,7 @@ export interface AgentRunLoopDeps {
|
||||
* iteration cap is hit (which throws rather than silently truncating).
|
||||
*/
|
||||
export async function runAgentLoop(deps: AgentRunLoopDeps, input: AgentRunLoopInput): Promise<AgentRunLoopResult> {
|
||||
const allTools = [...AGENT_TOOLS, ...WORKSPACE_TOOLS, ...(input.externalTools ?? [])];
|
||||
const allTools = [...AGENT_TOOLS, ...WORKSPACE_TOOLS, ...WEB_TOOLS, ...(input.externalTools ?? [])];
|
||||
const messages: AgentTurnMessage[] = [{ role: "user", text: input.initialUserTurn }];
|
||||
let toolCallCount = 0;
|
||||
let lastRequestId = "";
|
||||
@@ -73,6 +74,8 @@ export async function runAgentLoop(deps: AgentRunLoopDeps, input: AgentRunLoopIn
|
||||
output = await executeComposioTool(deps.tools.db, input.orgId, input.toolContext.agent.id, call);
|
||||
} else if (isWorkspaceTool(call.name)) {
|
||||
output = await executeWorkspaceTool(input.toolContext, call);
|
||||
} else if (isWebTool(call.name)) {
|
||||
output = await executeWebTool(call);
|
||||
} else {
|
||||
output = dispatchAgentTool(deps.tools, input.toolContext, call, {
|
||||
announceTaskAssignments: shouldAnnounceTaskAssignment(call, turn.toolCalls),
|
||||
|
||||
Reference in new issue
Block a user