{"version":3,"file":"fetch-handler.cjs","names":["handleCors","runOnRequest","callBeforeRequestMiddleware","runOnBeforeHandler","createJsonRequest","matchRoute","runOnResponse","runOnError","handleRunAgent","handleConnectAgent","handleStopAgent","handleGetRuntimeInfo","handleTranscribe","handleListThreads","handleSubscribeToThreads","handleDeleteThread","handleUpdateThread","handleArchiveThread","handleGetThreadMessages","parseMethodCall","expectString","addCorsHeaders"],"sources":["../../../../src/v2/runtime/core/fetch-handler.ts"],"sourcesContent":["/**\n * Framework-agnostic CopilotKit runtime handler.\n *\n * Returns a pure `(Request) => Promise<Response>` function that can be used\n * directly with Bun, Deno, Cloudflare Workers, Next.js App Router, or any\n * Fetch-native runtime — no framework dependency required.\n *\n * @example\n * ```typescript\n * import { CopilotRuntime, createCopilotRuntimeHandler } from \"@copilotkit/runtime/v2\";\n *\n * const handler = createCopilotRuntimeHandler({\n *   runtime: new CopilotRuntime({ agents: { ... } }),\n *   basePath: \"/api/copilotkit\",\n *   cors: true,\n * });\n *\n * // Bun\n * Bun.serve({ fetch: handler });\n *\n * // Deno\n * Deno.serve(handler);\n *\n * // Cloudflare Workers\n * export default { fetch: handler };\n * ```\n */\n\nimport type { CopilotRuntimeLike } from \"./runtime\";\nimport type { CopilotRuntimeHooks, RouteInfo, HookContext } from \"./hooks\";\nimport {\n  runOnRequest,\n  runOnBeforeHandler,\n  runOnResponse,\n  runOnError,\n} from \"./hooks\";\nimport type { CopilotCorsConfig } from \"./fetch-cors\";\nimport { handleCors, addCorsHeaders } from \"./fetch-cors\";\nimport { matchRoute } from \"./fetch-router\";\nimport {\n  callBeforeRequestMiddleware,\n  callAfterRequestMiddleware,\n} from \"./middleware\";\nimport { handleRunAgent } from \"../handlers/handle-run\";\nimport { handleConnectAgent } from \"../handlers/handle-connect\";\nimport { handleStopAgent } from \"../handlers/handle-stop\";\nimport { handleGetRuntimeInfo } from \"../handlers/get-runtime-info\";\nimport { handleTranscribe } from \"../handlers/handle-transcribe\";\nimport {\n  handleListThreads,\n  handleSubscribeToThreads,\n  handleUpdateThread,\n  handleArchiveThread,\n  handleDeleteThread,\n  handleGetThreadMessages,\n} from \"../handlers/handle-threads\";\nimport {\n  parseMethodCall,\n  createJsonRequest,\n  expectString,\n  type MethodCall,\n} from \"../endpoints/single-route-helpers\";\nimport { logger } from \"@copilotkit/shared\";\n\n/* ------------------------------------------------------------------------------------------------\n * Public types\n * --------------------------------------------------------------------------------------------- */\n\nexport interface CopilotRuntimeHandlerOptions {\n  runtime: CopilotRuntimeLike;\n\n  /**\n   * Optional base path for routing.\n   *\n   * When provided: strict prefix stripping. The handler strips this prefix from the\n   * URL pathname and matches the remainder against known routes.\n   *\n   * When omitted: suffix matching. The handler matches known route patterns as\n   * suffixes of the URL pathname.\n   */\n  basePath?: string;\n\n  /**\n   * Endpoint mode:\n   * - \"multi-route\" (default): Routes like POST /agent/:agentId/run, GET /info, etc.\n   * - \"single-route\": Single POST endpoint with JSON envelope { method, params, body }\n   */\n  mode?: \"multi-route\" | \"single-route\";\n\n  /**\n   * Optional CORS configuration.\n   * When not provided, no CORS headers are added (let the framework handle it).\n   * Set to true for permissive defaults, or provide an object.\n   */\n  cors?: boolean | CopilotCorsConfig;\n\n  /**\n   * Lifecycle hooks for request processing.\n   */\n  hooks?: CopilotRuntimeHooks;\n}\n\nexport type CopilotRuntimeFetchHandler = (\n  request: Request,\n) => Promise<Response>;\n\n/* ------------------------------------------------------------------------------------------------\n * Handler factory\n * --------------------------------------------------------------------------------------------- */\n\nexport function createCopilotRuntimeHandler(\n  options: CopilotRuntimeHandlerOptions,\n): CopilotRuntimeFetchHandler {\n  const { runtime, basePath, mode = \"multi-route\", cors, hooks } = options;\n\n  const corsConfig = resolveCorsConfig(cors);\n\n  return async (request: Request): Promise<Response> => {\n    const url = new URL(request.url, \"http://localhost\");\n    const path = url.pathname;\n    const requestOrigin = request.headers.get(\"origin\");\n\n    // Base hook context (route not yet known)\n    const baseCtx: HookContext = { request, path, runtime };\n\n    let route: RouteInfo | undefined;\n\n    try {\n      // 1. CORS preflight\n      if (corsConfig) {\n        const preflight = handleCors(request, corsConfig);\n        if (preflight) return preflight;\n      }\n\n      // 2. onRequest hook\n      request = await runOnRequest(hooks, { ...baseCtx, request });\n\n      // 3. Legacy beforeRequestMiddleware\n      try {\n        const maybeModified = await callBeforeRequestMiddleware({\n          runtime,\n          request,\n          path,\n        });\n        if (maybeModified) {\n          request = maybeModified;\n        }\n      } catch (mwError: unknown) {\n        logger.error(\n          { err: mwError, url: request.url, path },\n          \"Error running before request middleware\",\n        );\n        if (mwError instanceof Response) {\n          return maybeAddCors(mwError, corsConfig, requestOrigin);\n        }\n        throw mwError;\n      }\n\n      // 4. Route matching\n      let response: Response;\n\n      if (mode === \"single-route\") {\n        const resolved = await resolveSingleRoute(request, basePath, path);\n        route = resolved.route;\n        const { methodCall } = resolved;\n        // 5. onBeforeHandler hook\n        request = await runOnBeforeHandler(hooks, {\n          request,\n          path,\n          runtime,\n          route,\n        });\n        // 6. Wrap body for methods that need it, then dispatch\n        if (\n          route.method === \"agent/run\" ||\n          route.method === \"agent/connect\" ||\n          route.method === \"transcribe\"\n        ) {\n          request = createJsonRequest(request, methodCall.body);\n        }\n        response = await dispatchRoute(runtime, request, route);\n      } else {\n        // Multi-route: match URL pattern\n        const matched = matchRoute(path, basePath);\n        if (!matched) {\n          throw jsonResponse({ error: \"Not found\" }, 404);\n        }\n\n        // Validate HTTP method\n        const methodError = validateHttpMethod(request.method, matched);\n        if (methodError) {\n          route = matched;\n          throw methodError;\n        }\n\n        route = matched;\n\n        // 5. onBeforeHandler hook\n        request = await runOnBeforeHandler(hooks, {\n          request,\n          path,\n          runtime,\n          route,\n        });\n\n        // 6. Handler dispatch\n        response = await dispatchRoute(runtime, request, route);\n      }\n\n      // 7. onResponse hook\n      response = await runOnResponse(hooks, {\n        request,\n        response,\n        path,\n        runtime,\n        route,\n      });\n\n      // 8. CORS headers on response\n      response = maybeAddCors(response, corsConfig, requestOrigin);\n\n      // 9. Legacy afterRequestMiddleware (non-blocking)\n      // Clone the response so middleware can read the body without consuming\n      // the original stream that will be sent to the client.\n      callAfterRequestMiddleware({\n        runtime,\n        response: response.clone(),\n        path,\n      }).catch((error: unknown) => {\n        logger.error(\n          { err: error, url: request.url, path },\n          \"Error running after request middleware\",\n        );\n      });\n\n      return response;\n    } catch (error) {\n      // Short-circuit with thrown Response\n      if (error instanceof Response) {\n        const finalResponse = await runOnResponse(hooks, {\n          request,\n          response: error,\n          path,\n          runtime,\n          route: route ?? { method: \"info\" },\n        });\n        return maybeAddCors(finalResponse, corsConfig, requestOrigin);\n      }\n\n      // Run onError hook — wrapped so a throwing hook doesn't escape\n      try {\n        const errorResponse = await runOnError(hooks, {\n          request,\n          error,\n          path,\n          runtime,\n          route,\n        });\n\n        if (errorResponse) {\n          return maybeAddCors(errorResponse, corsConfig, requestOrigin);\n        }\n      } catch (hookError: unknown) {\n        logger.error(\n          { err: hookError, originalErr: error, url: request.url, path },\n          \"onError hook threw\",\n        );\n      }\n\n      logger.error(\n        { err: error, url: request.url, path },\n        \"Unhandled error in CopilotKit runtime handler\",\n      );\n\n      return maybeAddCors(\n        jsonResponse({ error: \"internal_error\" }, 500),\n        corsConfig,\n        requestOrigin,\n      );\n    }\n  };\n}\n\n/* ------------------------------------------------------------------------------------------------\n * Route dispatch\n * --------------------------------------------------------------------------------------------- */\n\nfunction dispatchRoute(\n  runtime: CopilotRuntimeLike,\n  request: Request,\n  route: RouteInfo,\n): Promise<Response> {\n  switch (route.method) {\n    case \"agent/run\":\n      return handleRunAgent({\n        runtime,\n        request,\n        agentId: route.agentId,\n      });\n    case \"agent/connect\":\n      return handleConnectAgent({\n        runtime,\n        request,\n        agentId: route.agentId,\n      });\n    case \"agent/stop\":\n      return handleStopAgent({\n        runtime,\n        request,\n        agentId: route.agentId,\n        threadId: route.threadId,\n      });\n    case \"info\":\n      return handleGetRuntimeInfo({ runtime, request });\n    case \"transcribe\":\n      return handleTranscribe({ runtime, request });\n    case \"threads/list\":\n      return handleListThreads({ runtime, request });\n    case \"threads/subscribe\":\n      return handleSubscribeToThreads({ runtime, request });\n    case \"threads/update\":\n      if (request.method.toUpperCase() === \"DELETE\") {\n        return handleDeleteThread({\n          runtime,\n          request,\n          threadId: route.threadId,\n        });\n      }\n      return handleUpdateThread({ runtime, request, threadId: route.threadId });\n    case \"threads/archive\":\n      return handleArchiveThread({\n        runtime,\n        request,\n        threadId: route.threadId,\n      });\n    case \"threads/messages\":\n      return handleGetThreadMessages({\n        runtime,\n        request,\n        threadId: route.threadId,\n      });\n  }\n}\n\ninterface SingleRouteResolution {\n  route: RouteInfo;\n  methodCall: MethodCall;\n}\n\nasync function resolveSingleRoute(\n  request: Request,\n  basePath: string | undefined,\n  pathname: string,\n): Promise<SingleRouteResolution> {\n  if (basePath) {\n    const normalizedBase =\n      basePath.length > 1 && basePath.endsWith(\"/\")\n        ? basePath.slice(0, -1)\n        : basePath;\n    if (!pathname.startsWith(normalizedBase)) {\n      throw jsonResponse({ error: \"Not found\" }, 404);\n    }\n  }\n\n  if (request.method !== \"POST\") {\n    throw jsonResponse({ error: \"Method not allowed\" }, 405, { Allow: \"POST\" });\n  }\n\n  const methodCall = await parseMethodCall(request);\n\n  let route: RouteInfo;\n  switch (methodCall.method) {\n    case \"agent/run\":\n      route = {\n        method: \"agent/run\",\n        agentId: expectString(methodCall.params, \"agentId\"),\n      };\n      break;\n    case \"agent/connect\":\n      route = {\n        method: \"agent/connect\",\n        agentId: expectString(methodCall.params, \"agentId\"),\n      };\n      break;\n    case \"agent/stop\":\n      route = {\n        method: \"agent/stop\",\n        agentId: expectString(methodCall.params, \"agentId\"),\n        threadId: expectString(methodCall.params, \"threadId\"),\n      };\n      break;\n    case \"info\":\n      route = { method: \"info\" };\n      break;\n    case \"transcribe\":\n      route = { method: \"transcribe\" };\n      break;\n  }\n\n  return { route, methodCall };\n}\n\n/* ------------------------------------------------------------------------------------------------\n * HTTP method validation\n * --------------------------------------------------------------------------------------------- */\n\nfunction validateHttpMethod(\n  httpMethod: string,\n  route: RouteInfo,\n): Response | null {\n  const method = httpMethod.toUpperCase();\n\n  switch (route.method) {\n    case \"info\":\n    case \"threads/list\":\n    case \"threads/messages\":\n      if (method === \"GET\") return null;\n      return jsonResponse({ error: \"Method not allowed\" }, 405, {\n        Allow: \"GET\",\n      });\n\n    case \"threads/update\":\n      if (method === \"PATCH\" || method === \"DELETE\") return null;\n      return jsonResponse({ error: \"Method not allowed\" }, 405, {\n        Allow: \"PATCH, DELETE\",\n      });\n\n    default:\n      if (method === \"POST\") return null;\n      return jsonResponse({ error: \"Method not allowed\" }, 405, {\n        Allow: \"POST\",\n      });\n  }\n}\n\n/* ------------------------------------------------------------------------------------------------\n * Helpers\n * --------------------------------------------------------------------------------------------- */\n\nfunction resolveCorsConfig(\n  cors: boolean | CopilotCorsConfig | undefined,\n): CopilotCorsConfig | null {\n  if (!cors) return null;\n  if (cors === true) return {};\n  return cors;\n}\n\nfunction maybeAddCors(\n  response: Response,\n  config: CopilotCorsConfig | null,\n  requestOrigin: string | null,\n): Response {\n  if (!config) return response;\n  return addCorsHeaders(response, config, requestOrigin);\n}\n\nfunction jsonResponse(\n  body: unknown,\n  status: number,\n  extraHeaders?: Record<string, string>,\n): Response {\n  return new Response(JSON.stringify(body), {\n    status,\n    headers: { \"Content-Type\": \"application/json\", ...extraHeaders },\n  });\n}\n"],"mappings":"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"}