import { runWeb } from "../../worker/web-errors"; export { Sandbox } from "../../worker/sandbox"; import { browserRace } from "./browser-races"; import runtime, { PersonalAgent as FixturePersonalAgent, Conversation as FixtureConversation, } from "./think-worker"; import type { Env } from "../../worker/personal-agent"; import { createWebTools } from "../../worker/web-tools"; import { searchWeb } from "../../worker/web-search"; import { searchResponse } from "./search-response"; import { createFetchTools } from "@cloudflare/think/tools/fetch"; export class PersonalAgent extends FixturePersonalAgent {} const originalFetch = globalThis.fetch; const calls: string[] = []; let bodyAborts = 0; globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => { const url = new URL( typeof input === "string" ? input : input instanceof URL ? input.href : input.url, ); if (!url.hostname.endsWith(".fixture.example.com")) return originalFetch(input, init); calls.push(url.href); const type = url.pathname === "/binary" ? "application/pdf" : url.pathname === "/plain" || url.pathname === "/large" ? "text/plain" : "text/html"; if (url.pathname === "/redirect") return new Response(null, { status: 302, headers: { location: "https://other.fixture.example.com/html" }, }); if (url.pathname === "/private") return new Response(null, { status: 302, headers: { location: "http://127.0.0.1/secret" }, }); if (url.pathname === "/loop") return new Response(null, { status: 302, headers: { location: url.href } }); if (url.pathname === "/error") return new Response("private-error-marker", { status: 429 }); if (url.pathname === "/slow") return new Response( new ReadableStream({ start(controller) { controller.enqueue(new TextEncoder().encode("Slow ")); const abort = () => { bodyAborts++; controller.error(new DOMException("Aborted", "AbortError")); }; init?.signal?.addEventListener("abort", abort, { once: true }); if (init?.signal?.aborted) abort(); }, }), { headers: { "content-type": "text/html" } }, ); const html = "Fish & Chips 🐟

Research title

A < B & C. Café 🐟

Another paragraph.

"; return new Response( url.pathname === "/large" ? "🐟".repeat(200_000) : url.pathname === "/plain" ? "Plain evidence" : html, { headers: { "content-type": type } }, ); }) as typeof fetch; const searches: { model: string; input: Record; options: unknown; }[] = []; let searchAborts = 0, searchBodyCancels = 0; const fixtureAI = { async run(model: string, input: Record, options: AiOptions) { searches.push({ model, input, options: { ...options, signal: !!options.signal }, }); const query = input.input; if (query === "slow" || query === "timeout") return new Promise((resolve, reject) => { const timer = setTimeout(() => resolve(new Response("late")), 120_000); const abort = () => { clearTimeout(timer); searchAborts++; reject(options.signal?.reason); }; options.signal?.addEventListener("abort", abort, { once: true }); if (options.signal?.aborted) abort(); }); if (query === "slow-body") return new Response( new ReadableStream({ start(controller) { controller.enqueue(new TextEncoder().encode('{"status":')); }, cancel() { searchBodyCancels++; }, }), ); if (typeof query === "string" && /^http-\d+$/.test(query)) return new Response("private-error-marker", { status: Number(query.slice(5)), }); if (query === "throws") throw new Error("private-error-marker"); if (query === "malformed") return new Response("not JSON private-error-marker"); if (query === "oversized") return new Response("x".repeat(256_001)); const body: any = searchResponse( "https://source.fixture.example.com/html", query === "long" ? "🐟".repeat(200) : "Fixture & research", query === "long" ? "🐟".repeat(4_000) : "Generated search summary, not page text.", ); if (query === "empty") body.output = [ { type: "web_search_call", status: "completed", action: { type: "search", sources: [] }, }, ]; if (query === "unexpected") body.output = []; if (query === "incomplete") body.status = "incomplete"; if (query === "failed-search") body.output[0].status = "failed"; if (query === "searching-only") body.output[0].status = "searching"; if (query === "mixed-search" || query === "mixed-empty") { if (query === "mixed-empty") { body.output[0].action.sources = []; body.output.pop(); } body.output.splice(1, 0, { type: "web_search_call", status: "searching", action: { type: "search", sources: [ { type: "url", url: "https://unfinished.fixture.example.com/" }, ], }, }); } if (query === "missing-sources") { delete body.output[0].action.sources; body.output.pop(); } if (query === "refusal") body.output[1].content = [ { type: "refusal", refusal: "private-error-marker" }, ]; if (query === "normal") { body.output[1].content[0].annotations.push( { type: "url_citation", url: "https://source.fixture.example.com/html#duplicate", title: "Duplicate", }, ...[ "http://127.0.0.1/secret", "javascript:alert(1)", "https://user:secret@example.com", "/relative", ].map((url) => ({ type: "url_citation", url, title: "Unsafe" })), ); } if (query === "many") body.output[0].action.sources.push( ...Array.from({ length: 8 }, (_, i) => ({ type: "url", url: `https://source.fixture.example.com/${i}`, })), ); if (query === "unsafe-only") { body.output.pop(); body.output[0].action.sources = [ { type: "url", url: "http://127.0.0.1/secret" }, ]; } return Response.json(body); }, } as unknown as Ai; export class Conversation extends FixtureConversation { getTools() { return { ...super.getTools(), ...createWebTools( { fetch() { throw new Error("Search must not acquire a browser"); }, }, fixtureAI, (promise) => this.ctx.waitUntil(promise), this.browserOptions(), ), }; } } export default { async fetch( request: Request, env: Env, ctx: ExecutionContext, ) { const url = new URL(request.url); if (url.pathname === "/__fixture/browser-race") return Response.json( await browserRace(url.searchParams.get("scenario") ?? "success"), ); if (url.pathname === "/__fixture/web-status") return Response.json({ calls, bodyAborts, searches, searchAborts, searchBodyCancels, }); if (url.pathname === "/__fixture/search-pre-abort") { const controller = new AbortController(); controller.abort(); return Response.json( await runWeb(searchWeb(fixtureAI, "normal", 5), controller.signal), ); } if (url.pathname === "/__fixture/native-fetch-timeout") { const native = createFetchTools({ allowlist: ["https://**"], timeoutMs: 100, response: "text", }).fetch_url; const result = await native.execute!( { url: "https://source.fixture.example.com/slow" }, { toolCallId: "timeout", messages: [], context: undefined }, ); return Response.json({ result, bodyAborts }); } return runtime.fetch(request, env, ctx); }, };