blob: ec53ee88aaef999a400a64311a4f295149494bcd [file]
import { z } from 'zod';
import type { MakaTool } from './tool-runtime.js';
const WEB_FETCH_TOOL_NAME = 'WebFetch';
export const WEB_FETCH_MODEL_OUTPUT_MAX_BYTES = 50 * 1024;
const WEB_FETCH_TRUNCATION_MARKER =
'\n\n…[WebFetch content truncated to fit the 50 KB model-output limit]';
const httpUrlSchema = z
.string()
.url()
.refine((value) => {
const protocol = new URL(value).protocol;
return protocol === 'http:' || protocol === 'https:';
}, 'URL must use HTTP or HTTPS.');
export interface WebFetchExecutor {
fetch(input: {
readonly url: string;
readonly sessionId: string;
readonly abortSignal?: AbortSignal;
}): Promise<string>;
}
/** Builds the model-facing tool while the host owns policy and transport. */
export function buildWebFetchTool(executor: WebFetchExecutor): MakaTool {
return {
name: WEB_FETCH_TOOL_NAME,
categoryHint: 'web_read',
displayName: 'Web fetch',
description:
'Read the main content of a specific HTTP or HTTPS URL. Use it when a concrete URL is already known.',
parameters: z.object({ url: httpUrlSchema }).strict(),
impl: async ({ url }, context) => {
const canonicalUrl = new URL(httpUrlSchema.parse(url)).toString();
const content = await executor.fetch({
url: canonicalUrl,
sessionId: context.sessionId,
...(context.abortSignal ? { abortSignal: context.abortSignal } : {}),
});
return truncateWebFetchOutput(content);
},
};
}
/** Removes network reading from the model-visible turn while privacy mode is active. */
export function routeWebFetchTools(
tools: readonly MakaTool[],
privacy: { readonly incognitoActive: boolean },
): MakaTool[] {
return privacy.incognitoActive
? tools.filter((tool) => tool.name !== WEB_FETCH_TOOL_NAME)
: [...tools];
}
function truncateWebFetchOutput(content: string): string {
if (Buffer.byteLength(content, 'utf8') <= WEB_FETCH_MODEL_OUTPUT_MAX_BYTES) return content;
const markerBytes = Buffer.byteLength(WEB_FETCH_TRUNCATION_MARKER, 'utf8');
const kept = Buffer.from(content, 'utf8')
.subarray(0, WEB_FETCH_MODEL_OUTPUT_MAX_BYTES - markerBytes)
.toString('utf8')
.replace(/�+$/, '');
return kept + WEB_FETCH_TRUNCATION_MARKER;
}