import fixture, { PersonalAgent as ExecutionPersonalAgent, Conversation as ExecutionConversation, } from "./execution-worker"; import { getAgentByName, type Agent, type SubAgentClass } from "agents"; import { action } from "@cloudflare/think"; import type { Env } from "../../worker/personal-agent"; export { Sandbox } from "./execution-worker"; export class Conversation extends ExecutionConversation { private acquisitionDelay = false; private acquiring = false; private delayNextParent = false; configureScheduleAcquisition() { this.acquisitionDelay = true; } inspectScheduleAcquisition() { return this.acquiring; } getActions() { const actions = super.getActions(); return { ...actions, createSchedule: action({ ...actions.createSchedule.config, execute: async (input, ctx) => { this.delayNextParent = this.acquisitionDelay; this.acquisitionDelay = false; return actions.createSchedule.config.execute(input, ctx); }, }), }; } async parentAgent( cls: SubAgentClass, ): Promise> { const parent = await super.parentAgent(cls); if (this.delayNextParent) { this.delayNextParent = false; this.acquiring = true; await new Promise((resolve) => setTimeout(resolve, 1500)); this.acquiring = false; } return parent; } } export class PersonalAgent extends ExecutionPersonalAgent { private fault = ""; private waiting = false; async createTaskForConversation( ...args: Parameters ) { if (this.fault === "late-parent") { this.fault = ""; this.waiting = true; await new Promise((resolve) => setTimeout(resolve, 1500)); this.waiting = false; } const task = await super.createTaskForConversation(...args); if (this.fault === "lost-reply") { this.fault = ""; throw new Error("Fixture lost task reply PRIVATE-SCHEDULE-ERROR"); } return task; } async schedule( when: Date | string | number, callback: keyof this, payload?: T, options?: Parameters[3], ) { if (this.fault === "unavailable" && callback === "dispatchScheduledTask") throw new Error( "Fixture native schedule unavailable PRIVATE-SCHEDULE-ERROR", ); return super.schedule(when, callback, payload, options); } async scheduleFixture(action: string, input: Record) { if (action === "fault") { this.fault = input.mode as string; return; } if (action === "waiting") return this.waiting; // Test only, and never create a missing facet when observing deletion. if (!this.hasSubAgent(Conversation, input.conversationId as string)) return null; const child = await this.subAgent( Conversation, input.conversationId as string, ); if (action === "delay-acquisition") return child.configureScheduleAcquisition(); if (action === "acquiring") return child.inspectScheduleAcquisition(); throw new Error("Unknown schedule fixture action"); } } export default { async fetch(request, env, ctx) { const path = new URL(request.url).pathname; if (path.startsWith("/__schedule/")) { const personal = await getAgentByName( env.PersonalAgent as unknown as DurableObjectNamespace, "personal", ); return Response.json( (await personal.scheduleFixture( path.split("/").at(-1)!, await request.json(), )) ?? null, ); } return fixture.fetch(request, env, ctx); }, } satisfies ExportedHandler;