import { describe, expect, it } from "@effect/vitest"; import { Cause, Effect } from "effect"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import type { JSONRPCMessage, MessageExtraInfo } from "@modelcontextprotocol/sdk/types.js"; import { defaultMcpResource } from "@executor-js/host-mcp"; import type { ExecutionEngine, ExecutionResult, ResumeResponse } from "@executor-js/execution"; import { McpAgentSessionDOBase, type McpApprovalOwner, type McpSessionModelResumeResult, type SessionMeta, } from "./agent-session-durable-object"; class MemoryStorage { private readonly data = new Map(); alarm: number | undefined; readonly sql = { exec: () => [], }; async get(key: string): Promise { return this.data.get(key) as T | undefined; } async put(key: string, value: unknown): Promise { this.data.set(key, value); } async setAlarm(time: number | Date): Promise { this.alarm = typeof time === "number" ? time : time.getTime(); } async deleteAlarm(): Promise { this.alarm = undefined; } async delete(key: string | readonly string[]): Promise { if (typeof key === "string") { this.data.delete(key); return; } for (const entry of key) { this.data.delete(entry); } } async deleteAll(): Promise { this.data.clear(); } async list( options: { readonly prefix?: string; readonly limit?: number } = {}, ): Promise> { const rows = new Map(); for (const [key, value] of this.data) { if (options.prefix && !key.startsWith(options.prefix)) continue; rows.set(key, value as T); if (options.limit && rows.size >= options.limit) break; } return rows; } async blockConcurrencyWhile(callback: () => T | Promise): Promise { return callback(); } get id(): { readonly name: string } { return { name: "streamable-http:session-reconnect" }; } get storage(): MemoryStorage { return this; } waitUntil(_promise: Promise): void {} } type HarnessSession = { alarm: () => Promise; ctx: MemoryStorage; dbHandle: { readonly end: () => void } | null; engine: ExecutionEngine | null; getSessionId: () => string; initialized: boolean; lastActivityMs: number; maxPausedSessionIdleMs: () => number; onStart: () => Promise; pendingApprovalLeases: Map; props: Record; runMcpAgentOnStart: () => Promise; server?: McpServer; sessionMeta: SessionMeta; sessionTimeoutMs: () => number; resumeExecutionForModel: ( executionId: string, identity: McpApprovalOwner, response: ResumeResponse, ) => Promise; validateMcpSessionOwner: (identity: { readonly accountId: string; readonly organizationId: string; }) => Promise<"ok" | "not_found" | "forbidden" | "terminated">; }; class StaleCloseTransport implements Transport { onclose?: () => void; onerror?: (error: Error) => void; onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void; async start(): Promise {} async close(): Promise {} async send(_message: JSONRPCMessage): Promise {} } class RestoredTransport implements Transport { onclose?: () => void; onerror?: (error: Error) => void; onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void; async start(): Promise {} async close(): Promise { this.onclose?.(); } async send(_message: JSONRPCMessage): Promise {} } const makeServer = () => new McpServer({ name: "executor-test", version: "1.0.0" }); const makeDeferred = (): { readonly promise: Promise; readonly resolve: () => void } => { let resolve: () => void = () => undefined; const promise = new Promise((settle) => { resolve = settle; }); return { promise, resolve }; }; type ResumeCall = { readonly executionId: string; readonly response: ResumeResponse; }; const completed = (result: unknown): ExecutionResult => ({ status: "completed", result: { result }, }); const makeEngine = ( resultForResume: (executionId: string, response: ResumeResponse) => ExecutionResult | null = () => completed("resume-result"), ): { readonly calls: ResumeCall[]; readonly engine: ExecutionEngine } => { const calls: ResumeCall[] = []; return { calls, engine: { execute: () => Effect.succeed({ result: "execute-result" }), executeWithPause: () => Effect.succeed(completed("execute-result")), resume: (executionId, response) => Effect.sync(() => { calls.push({ executionId, response }); return resultForResume(executionId, response); }), getPausedExecution: () => Effect.succeed(null), pausedExecutionCount: () => Effect.succeed(0), hasPausedExecutions: () => Effect.succeed(false), getDescription: Effect.succeed("test engine"), }, }; }; const approval = { action: "accept", content: { approved: true }, } satisfies ResumeResponse; const makeHarnessSession = async (): Promise => { const sessionId = "session-reconnect"; const sessionMeta: SessionMeta = { organizationId: "org-1", organizationName: "Org 1", userId: "user-1", resource: defaultMcpResource, }; const storage = new MemoryStorage(); const server = makeServer(); await server.connect(new StaleCloseTransport()); const session = Object.create(McpAgentSessionDOBase.prototype) as HarnessSession; session.ctx = storage; session.dbHandle = { end: () => undefined }; session.engine = makeEngine().engine; session.getSessionId = () => sessionId; session.initialized = true; session.lastActivityMs = Date.now() - 10; session.maxPausedSessionIdleMs = () => 1_000; session.pendingApprovalLeases = new Map(); session.props = {}; session.server = server; session.sessionMeta = sessionMeta; session.sessionTimeoutMs = () => 1; session.runMcpAgentOnStart = async () => { const restored = session.server ?? makeServer(); session.server = restored; await restored.connect(new RestoredTransport()); session.engine = makeEngine().engine; session.initialized = true; }; return session; }; describe("McpAgentSessionDOBase transport restore", () => { it("restores a same-session request after idle disposal leaves a stale server transport", async () => { const session = await makeHarnessSession(); await session.alarm(); await expect( session.validateMcpSessionOwner({ accountId: "user-1", organizationId: "org-1" }), ).resolves.toBe("ok"); }); it("single-flights concurrent same-session restore after idle disposal", async () => { const session = await makeHarnessSession(); const firstRestoreEntered = makeDeferred(); const finishRestore = makeDeferred(); let onStartCalls = 0; let restoredServer: McpServer | undefined; session.runMcpAgentOnStart = async () => { onStartCalls += 1; const restored = session.server ?? makeServer(); restoredServer ??= restored; session.server = restored; firstRestoreEntered.resolve(); await finishRestore.promise; await restored.connect(new RestoredTransport()); session.initialized = true; }; await session.alarm(); const first = session.validateMcpSessionOwner({ accountId: "user-1", organizationId: "org-1", }); const second = session.validateMcpSessionOwner({ accountId: "user-1", organizationId: "org-1", }); await firstRestoreEntered.promise; await Promise.resolve(); finishRestore.resolve(); await expect(Promise.all([first, second])).resolves.toEqual(["ok", "ok"]); expect(onStartCalls).toBe(1); expect(session.server).toBe(restoredServer); }); it("single-flights SDK onStart callers with same-session restore", async () => { const session = await makeHarnessSession(); const firstStartEntered = makeDeferred(); const finishStart = makeDeferred(); let onStartCalls = 0; session.runMcpAgentOnStart = async () => { onStartCalls += 1; const restored = session.server ?? makeServer(); session.server = restored; firstStartEntered.resolve(); await finishStart.promise; await restored.connect(new RestoredTransport()); session.initialized = true; }; await session.alarm(); const restore = session.validateMcpSessionOwner({ accountId: "user-1", organizationId: "org-1", }); const sdkStart = session.onStart(); await firstStartEntered.promise; await Promise.resolve(); finishStart.resolve(); await expect(Promise.all([restore, sdkStart])).resolves.toEqual(["ok", undefined]); expect(onStartCalls).toBe(1); }); it("single-flights model resume restore with SDK onStart", async () => { const session = await makeHarnessSession(); const firstStartEntered = makeDeferred(); const finishStart = makeDeferred(); const restoredEngine = makeEngine(() => completed("model-result")); let onStartCalls = 0; session.runMcpAgentOnStart = async () => { onStartCalls += 1; const restored = session.server ?? makeServer(); session.server = restored; firstStartEntered.resolve(); await finishStart.promise; await restored.connect(new RestoredTransport()); session.engine = restoredEngine.engine; session.initialized = true; }; await session.alarm(); const resume = session.resumeExecutionForModel( "exec-model", { accountId: "user-1", organizationId: "org-1" }, approval, ); const sdkStart = session.onStart(); await firstStartEntered.promise; await Promise.resolve(); finishStart.resolve(); const [resumeResult] = await Promise.all([resume, sdkStart]); expect(resumeResult).toMatchObject({ status: "result", result: { structuredContent: { status: "completed", result: "model-result", }, }, }); expect(onStartCalls).toBe(1); expect(restoredEngine.calls).toEqual([{ executionId: "exec-model", response: approval }]); }); });