Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573export { Sandbox } from "../../worker/sandbox";import runtime from "../../worker/index";import { PersonalAgent as RuntimePersonalAgent, type Env,} from "../../worker/personal-agent";import { Conversation as RuntimeConversation } from "../../worker/conversation";import { callable, getAgentByName } from "agents";import { MockLanguageModelV3 } from "ai/test";import { tool } from "ai";import { action } from "@cloudflare/think";import type { ToolActivityDescriptor } from "../../worker/tool-activity";import { z } from "zod";import type { ModelConfiguration } from "../../worker/model-settings";import type { Secret } from "../../configuration/secrets";
export class PersonalAgent extends RuntimePersonalAgent { protected async generateConversationTitle(message: string) { this .sql`CREATE TABLE IF NOT EXISTS fixture_title_calls (message TEXT NOT NULL)`; this.sql`INSERT INTO fixture_title_calls VALUES (${message})`; if (message === "title:failure") throw new Error("Fixture title model failed"); if (message === "title:long-output") return "Useful ".repeat(20); if (message === "title:unsafe-output") return "Visit https://example.com/?token=private"; if (message === "title:bad-output") return "API key private-credential"; if (message === "title:opaque-output") return "Use abcdefghijklmnopqrst"; if (message === "title:prefixed-output") return "Use sk-short-value"; if (message === "title:persistence-failure") { this.sql`CREATE TRIGGER IF NOT EXISTS fixture_title_failure BEFORE UPDATE OF name ON flarebot_conversations WHEN NEW.name = 'Failed title persistence' BEGIN SELECT RAISE(ABORT, 'Fixture persistence failure'); END`; return "Failed title persistence"; } if (message === "title:slow") { await new Promise((resolve) => setTimeout(resolve, 1200)); return "Configure Flarebot locally"; } if (message.startsWith("title:")) return message.slice(6); return "New conversation"; }
fixtureEnableOpenRouter() { // Signed receipts are exercised by bridge.test.mjs. this .sql`INSERT OR IGNORE INTO flarebot_enabled_providers (provider) VALUES ('openrouter')`; }
fixtureLegacyKey() { this.sql`UPDATE flarebot_model_settings SET anthropic_key = ( SELECT encrypted_key FROM flarebot_provider_credentials WHERE provider = 'anthropic' ) WHERE singleton = 1`; this .sql`DELETE FROM flarebot_provider_credentials WHERE provider = 'anthropic'`; }
fixtureRetiredOpenCode() { this .sql`UPDATE flarebot_model_settings SET configuration = ${JSON.stringify({ provider: "opencode-go", model: "fixture-retired-model", })} WHERE singleton = 1`; this.sql`INSERT INTO flarebot_provider_credentials (provider, encrypted_key) SELECT 'opencode-go', encrypted_key FROM flarebot_provider_credentials WHERE provider = 'anthropic'`; this .sql`INSERT INTO flarebot_enabled_providers (provider) VALUES ('opencode-go')`; }
async fixtureDiagnostics(id: string, fail: boolean) { const child = await this.subAgent(Conversation, id); return child.fixtureDiagnostics(fail); } private failDeletion = false; private loseMemoryReply = false; private delayMemoryUpdate = false;
configureMemoryFault(mode: string) { this.loseMemoryReply = mode === "lost-reply"; this.delayMemoryUpdate = mode === "late-update"; }
async rememberForConversation( ...args: Parameters<RuntimePersonalAgent["rememberForConversation"]> ) { const fact = await super.rememberForConversation(...args); if (this.loseMemoryReply) { this.loseMemoryReply = false; throw new Error("Fixture lost memory reply after commit"); } return fact; }
async updateMemoryForConversation( ...args: Parameters<RuntimePersonalAgent["updateMemoryForConversation"]> ) { if (this.delayMemoryUpdate) { this.delayMemoryUpdate = false; await new Promise((resolve) => setTimeout(resolve, 1200)); } return super.updateMemoryForConversation(...args); }
inspectConversations() { this .sql`CREATE TABLE IF NOT EXISTS fixture_title_calls (message TEXT NOT NULL)`; return { titleCalls: this.sql`SELECT message FROM fixture_title_calls`, facets: this.listSubAgents(RuntimeConversation) .map((facet) => facet.name) .sort(), metadata: this .sql`SELECT id, status FROM flarebot_conversations ORDER BY id`, settingsStorage: this.sql`SELECT * FROM flarebot_model_settings`, credentialsStorage: this .sql`SELECT * FROM flarebot_provider_credentials ORDER BY provider`, enabledProvidersStorage: this .sql`SELECT * FROM flarebot_enabled_providers ORDER BY provider`, browserLeases: this.sql`SELECT * FROM flarebot_browser_leases`, }; }
async stageInterruptedCreation() { const id = crypto.randomUUID(); const now = new Date().toISOString(); this.sql`INSERT INTO flarebot_conversations VALUES (${id}, 'Interrupted creation', ${now}, ${now}, 'creating')`; await this.subAgent(RuntimeConversation, id); return id; }
failNextDeletion() { this.failDeletion = true; }
async deleteSubAgent( ...args: Parameters<RuntimePersonalAgent["deleteSubAgent"]> ) { if (this.failDeletion) { this.failDeletion = false; throw new Error("Fixture deletion failure"); } return super.deleteSubAgent(...args); }}
type ModelStream = Awaited< ReturnType<MockLanguageModelV3["doStream"]>>["stream"];type ModelChunk = ModelStream extends ReadableStream<infer Chunk> ? Chunk : never;
// Only this test entry replaces inference. No environment flag or development// authentication/model bypass is present in the customer runtime artifact.export class Conversation extends RuntimeConversation { @callable() fixtureModelCalls() { this .sql`CREATE TABLE IF NOT EXISTS fixture_model_calls (configuration TEXT, options TEXT)`; return this.sql<{ configuration: string; options: string; }>`SELECT * FROM fixture_model_calls`.map((row) => ({ configuration: JSON.parse(row.configuration), options: JSON.parse(row.options), })); }
fixtureDiagnostics(fail: boolean) { this.diagnosticSnapshot(); if (fail) { this.sql`DROP TABLE flarebot_diagnostics`; return []; } return this.sql`SELECT event FROM flarebot_diagnostics`; } protected createModel(configuration: ModelConfiguration, key?: Secret) { return new MockLanguageModelV3({ doStream: async ({ prompt, abortSignal, tools, providerOptions }) => { this.fixtureModelCalls(); this .sql`INSERT INTO fixture_model_calls VALUES (${JSON.stringify(configuration)}, ${JSON.stringify(providerOptions ?? {})})`; // Native recovery appends a synthetic continuation instruction. Select // the last fixture scenario from the real user history, not that prompt. const user = prompt.findLast( (message) => message.role === "user" && message.content.some( (part) => part.type === "text" && (part.text.startsWith("memory:") || part.text.startsWith("schedule:") || part.text.startsWith("memory-context ") || /^(slow-[a-z]+|second|tool|error|after-error|recover|configuration|instructions|credential-error|activity-[a-z]+)$/.test( part.text, )), ), ); const text = (user?.role === "user" ? user.content : []) .filter((part) => part.type === "text") .map((part) => part.text) .join("") ?? ""; const memoryCall: { tool: string; input: unknown; id?: string } | undefined = text.startsWith("memory:") ? JSON.parse(text.slice(7)) : text.startsWith("schedule:") ? JSON.parse(text.slice(9)) : undefined; const toolResult = prompt.at(-1)?.role === "tool"; if (text === "error") throw new Error("Fixture model unavailable"); if (text === "credential-error") throw Object.assign(new Error(`Provider echoed ${key?.reveal()}`), { statusCode: 401, responseBody: JSON.stringify({ name: "AiGatewayError", internalCode: 2009, message: key?.reveal(), }), responseHeaders: { "cf-ray": "0123456789abcdef-WAW", authorization: key?.reveal(), }, }); const attemptsKey = `fixture-attempts:${text}`; const attempts = ((await this.ctx.storage.get<number>(attemptsKey)) ?? 0) + 1; await this.ctx.storage.put(attemptsKey, attempts); const toolCall = (memoryCall || text === "tool" || text.startsWith("activity-")) && (!toolResult || (text === "activity-replay" && attempts === 2)) && (text !== "activity-recover" || attempts === 1); if ( toolCall && (tools?.length !== 14 || tools.some( (t) => ![ "shell", "browser_read", "web_search", "read_url", "remember", "createSchedule", "updateMemory", "forget", "recall", "fixtureEcho", "fixtureActivity", "fixtureProgress", "fixtureUnknown", "fixtureAction", ].includes(t.name), )) ) throw new Error("Unexpected enabled tools"); const slow = text.startsWith("slow") || (text === "recover" && attempts === 1); const tokens = toolCall ? [] : [ text === "recover" && attempts > 1 ? "Recovered " : "Reply ", text === "instructions" || text.startsWith("memory-context ") ? JSON.stringify( prompt .filter((message) => message.role === "system") .map((message) => message.content), ) : text === "configuration" ? `${configuration.provider}/${configuration.model}` : text, " complete", ]; const replayIdKey = "fixture-replay-id"; const replayId = (await this.ctx.storage.get<string>(replayIdKey)) ?? crypto.randomUUID(); if (text === "activity-replay") await this.ctx.storage.put(replayIdKey, replayId); const stream = new ReadableStream<ModelChunk>({ async start(controller) { const emit = (chunk: ModelChunk) => controller.enqueue(chunk); emit({ type: "stream-start", warnings: [] }); if (toolCall) { const count = text === "activity-many" ? 105 : text === "activity-parallel" ? 2 : 1; for (let index = 0; index < count; index++) { const toolCallId = memoryCall?.id ?? (text === "activity-replay" ? replayId : crypto.randomUUID()); const toolName = memoryCall?.tool ?? (text === "tool" ? "fixtureEcho" : text === "activity-action" ? "fixtureAction" : text === "activity-progress" ? "fixtureProgress" : text === "activity-unknown" ? "fixtureUnknown" : "fixtureActivity"); emit({ type: "tool-input-start", id: toolCallId, toolName }); if (text === "activity-pending") await new Promise((resolve) => setTimeout(resolve, 1000)); const input = memoryCall ? JSON.stringify(memoryCall.input) : text === "tool" ? '{"value":"native tool output"}' : JSON.stringify({ scenario: text, index: text === "activity-replay" ? attempts : index, secret: "fixture-private-credential", url: "https://user:password@example.com/private?token=secret#secret", }); emit({ type: "tool-input-delta", id: toolCallId, delta: input, }); emit({ type: "tool-input-end", id: toolCallId }); emit({ type: "tool-call", toolCallId, toolName, input }); } } else { emit({ type: "text-start", id: "text" }); for (let index = 0; index < tokens.length; index++) { if (abortSignal?.aborted) { controller.close(); return; } emit({ type: "text-delta", id: "text", delta: tokens[index] }); if (index === 0 && slow) await new Promise<void>((resolve) => { const timer = setTimeout( resolve, text === "recover" ? 30_000 : 1_500, ); abortSignal?.addEventListener( "abort", () => { clearTimeout(timer); resolve(); }, { once: true }, ); }); } emit({ type: "text-end", id: "text" }); } emit({ type: "finish", finishReason: { unified: toolCall ? "tool-calls" : "stop", raw: undefined, }, usage: { inputTokens: { total: 1, noCache: 1, cacheRead: 0, cacheWrite: 0, }, outputTokens: { total: 1, text: 1, reasoning: 0 }, }, }); controller.close(); }, }); return { stream }; }, }); }
protected getToolActivityDescriptors(): Record< string, ToolActivityDescriptor > { return { ...super.getToolActivityDescriptors(), fixtureEcho: { kind: "other", label: "Echo test", outputSummary: () => "Echo completed", }, fixtureActivity: { kind: "web", label: "Test research", inputSummary: (input) => (input as { scenario: string }).scenario === "activity-bounds" ? "Safe\n".repeat(100) : "Research requested", progress: () => ({ text: "Checking items", completed: 0, total: 1 }), outcome: (output) => (output as { ok: boolean }).ok ? "succeeded" : "failed", }, fixtureProgress: { kind: "shell", label: "Test progress", progress: (value) => ({ text: "Processing items", completed: (value as { completed: number }).completed, total: 1000, }), }, fixtureAction: { kind: "schedule", label: "Test action" }, }; }
getActions() { const memoryActions = super.getActions(); return { ...memoryActions, remember: action({ ...memoryActions.remember.config, execute: async (input, ctx) => { if (input.content === "Cancelled memory should not be saved.") await new Promise((resolve) => setTimeout(resolve, 1000)); return memoryActions.remember.config.execute(input, ctx); }, }), fixtureAction: action({ description: "Native failing test action", inputSchema: z.object({ scenario: z.string() }), execute: async () => { throw new Error("Fixture action failed"); }, }), }; }
getTools() { const inputSchema = z.object({ scenario: z.string(), index: z.number(), secret: z.string(), url: z.string(), }); return { ...super.getTools(), fixtureActivity: tool({ description: "Test activity outcomes", inputSchema, execute: async ({ scenario, index }, { abortSignal, toolCallId }) => { if (scenario === "activity-early") { this.reportToolProgress(toolCallId, { secret: "fixture-private-progress", }); await new Promise((resolve) => setTimeout(resolve, 800)); } if (scenario === "activity-error") throw new Error("Fixture execution failed"); if ( [ "activity-cancel", "activity-late", "activity-recover", "activity-parallel", "activity-detach", ].includes(scenario) || (scenario === "activity-replay" && index === 1) ) { await new Promise<void>((resolve, reject) => { const timer = setTimeout( resolve, scenario === "activity-recover" || scenario === "activity-replay" ? 30_000 : scenario === "activity-parallel" ? 350 + index * 750 : 1500, ); if (scenario !== "activity-late") abortSignal?.addEventListener( "abort", () => { clearTimeout(timer); reject(new Error("Fixture aborted")); }, { once: true }, ); }); } return { ok: scenario !== "activity-failure", secret: "fixture-private-output", }; }, }), fixtureProgress: tool({ description: "Test native preliminary outputs", inputSchema, execute: async function* () { for (let completed = 0; completed < 1000; completed++) { if (completed % 100 === 0) await new Promise((resolve) => setTimeout(resolve, 80)); yield { completed, secret: "fixture-private-progress" }; } yield { completed: 1000, secret: "fixture-private-output" }; }, }), fixtureUnknown: tool({ description: "Test generic fallback", inputSchema, execute: async () => ({ secret: "fixture-private-output" }), }), fixtureEcho: tool({ description: "Test the native server tool loop", inputSchema: z.object({ value: z.string() }), execute: async ({ value }) => ({ echoed: value }), }), }; }}
export default { async fetch(request, env, ctx) { const path = new URL(request.url).pathname; if (path.startsWith("/__fixture/")) { // This test Worker exports the extended fixture class under the stable // production binding. Namespace types are invariant across subclasses. const personal = await getAgentByName( env.PersonalAgent as unknown as DurableObjectNamespace<PersonalAgent>, "personal", ); if (path === "/__fixture/diagnostics") { const url = new URL(request.url); return Response.json( await personal.fixtureDiagnostics( url.searchParams.get("id")!, url.searchParams.get("fail") === "true", ), ); } if (path === "/__fixture/memory-fault") { await personal.configureMemoryFault( new URL(request.url).searchParams.get("mode") ?? "", ); return new Response(null, { status: 204 }); } if (path === "/__fixture/inspect") return Response.json(await personal.inspectConversations()); if ( path === "/__fixture/enable-openrouter" && request.method === "POST" ) { await personal.fixtureEnableOpenRouter(); return new Response(null, { status: 204 }); } if (path === "/__fixture/legacy-key") { await personal.fixtureLegacyKey(); return new Response(null, { status: 204 }); } if (path === "/__fixture/retired-opencode") { await personal.fixtureRetiredOpenCode(); return new Response(null, { status: 204 }); } if (path === "/__fixture/interrupted-create") return Response.json(await personal.stageInterruptedCreation()); if (path === "/__fixture/fail-delete") { await personal.failNextDeletion(); return new Response(null, { status: 204 }); } } return runtime.fetch(request, env, ctx); },} satisfies ExportedHandler<Env>;