{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "openai-agents-sdk-demo",
  "title": "OpenAI Agents SDK Demo",
  "description": "A complete OpenAI Agents SDK workspace that bridges official Agents runs into AI SDK UI messages, with tools, guardrails, handoffs, MCP docs, sandbox inspection, tracing, and voice transport lanes.",
  "dependencies": [
    "@ai-sdk/react",
    "@base-ui/react",
    "@modelcontextprotocol/sdk",
    "@openai/agents",
    "@openai/agents-extensions",
    "@phosphor-icons/react",
    "ai",
    "class-variance-authority",
    "lucide-react",
    "openai",
    "zod"
  ],
  "registryDependencies": [
    "utils",
    "badge",
    "breadcrumb",
    "button",
    "card",
    "collapsible",
    "dialog",
    "textarea",
    "https://elements.ai-sdk.dev/api/registry/attachments.json",
    "https://elements.ai-sdk.dev/api/registry/confirmation.json",
    "https://elements.ai-sdk.dev/api/registry/conversation.json",
    "https://elements.ai-sdk.dev/api/registry/message.json",
    "https://elements.ai-sdk.dev/api/registry/reasoning.json",
    "https://elements.ai-sdk.dev/api/registry/shimmer.json",
    "https://elements.ai-sdk.dev/api/registry/sources.json",
    "https://elements.ai-sdk.dev/api/registry/tool.json"
  ],
  "files": [
    {
      "path": "registry/openai-agents-sdk-demo/app/api/demos/openai-agents-sdk-demo/mcp/route.ts",
      "content": "import { handleOpenAiAgentsSdkDemoMcpRequest } from \"@/lib/openai-agents-sdk-demo/server/demo-mcp-server\";\n\nexport const runtime = \"nodejs\";\n\nexport function DELETE(request: Request) {\n  return handleOpenAiAgentsSdkDemoMcpRequest(request);\n}\n\nexport function GET(request: Request) {\n  return handleOpenAiAgentsSdkDemoMcpRequest(request);\n}\n\nexport function POST(request: Request) {\n  return handleOpenAiAgentsSdkDemoMcpRequest(request);\n}\n",
      "type": "registry:page",
      "target": "app/api/demos/openai-agents-sdk-demo/mcp/route.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/app/api/demos/openai-agents-sdk-demo/realtime/client-secrets/route.ts",
      "content": "import { handleOpenAiAgentsSdkDemoVoiceClientSecretRequest } from \"@/lib/openai-agents-sdk-demo/server/voice-realtime\";\n\nexport const runtime = \"nodejs\";\n\nexport function POST(request: Request) {\n  return handleOpenAiAgentsSdkDemoVoiceClientSecretRequest(request);\n}\n",
      "type": "registry:page",
      "target": "app/api/demos/openai-agents-sdk-demo/realtime/client-secrets/route.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/app/api/demos/openai-agents-sdk-demo/realtime/sip/route.ts",
      "content": "import { handleOpenAiAgentsSdkDemoSipRequest } from \"@/lib/openai-agents-sdk-demo/server/voice-sip-route\";\n\nexport const runtime = \"nodejs\";\n\nexport function POST(request: Request) {\n  return handleOpenAiAgentsSdkDemoSipRequest(request);\n}\n",
      "type": "registry:page",
      "target": "app/api/demos/openai-agents-sdk-demo/realtime/sip/route.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/app/api/demos/openai-agents-sdk-demo/realtime/twilio/incoming-call/route.ts",
      "content": "import { handleOpenAiAgentsSdkDemoTwilioIncomingCallRequest } from \"@/lib/openai-agents-sdk-demo/server/voice-twilio-route\";\n\nexport const runtime = \"nodejs\";\n\nexport function GET(request: Request) {\n  return handleOpenAiAgentsSdkDemoTwilioIncomingCallRequest(request);\n}\n\nexport function POST(request: Request) {\n  return handleOpenAiAgentsSdkDemoTwilioIncomingCallRequest(request);\n}\n",
      "type": "registry:page",
      "target": "app/api/demos/openai-agents-sdk-demo/realtime/twilio/incoming-call/route.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/app/api/demos/openai-agents-sdk-demo/route.ts",
      "content": "import { handleOpenAiAgentsSdkDemoRequest } from \"@/lib/openai-agents-sdk-demo/server/runtime\";\n\nexport const runtime = \"nodejs\";\n\nexport function POST(request: Request) {\n  return handleOpenAiAgentsSdkDemoRequest(request);\n}\n",
      "type": "registry:page",
      "target": "app/api/demos/openai-agents-sdk-demo/route.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/app/demos/openai-agents-sdk-demo/page.tsx",
      "content": "import { OpenAiAgentsSdkDemoScreen } from \"@/components/openai-agents-sdk-demo/openai-agents-sdk-demo-screen\";\n\nexport const dynamic = \"force-dynamic\";\n\nexport default function OpenAiAgentsSdkDemoPage() {\n  return <OpenAiAgentsSdkDemoScreen />;\n}\n",
      "type": "registry:page",
      "target": "app/demos/openai-agents-sdk-demo/page.tsx"
    },
    {
      "path": "registry/openai-agents-sdk-demo/components/demo-breadcrumb.tsx",
      "content": "import {\n  Breadcrumb,\n  BreadcrumbItem,\n  BreadcrumbLink,\n  BreadcrumbList,\n  BreadcrumbPage,\n  BreadcrumbSeparator,\n} from \"@/components/ui/breadcrumb\";\nimport { cn } from \"@/lib/utils\";\nimport { ArrowLeft } from \"lucide-react\";\n\ninterface DemoBreadcrumbProps {\n  className?: string;\n  title: string;\n}\n\nexport function DemoBreadcrumb({ className, title }: DemoBreadcrumbProps) {\n  return (\n    <Breadcrumb>\n      <BreadcrumbList\n        className={cn(\n          \"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\",\n          className\n        )}\n      >\n        <BreadcrumbItem>\n          <BreadcrumbLink\n            aria-label=\"Back to demos\"\n            className=\"-ml-1 inline-flex items-center gap-1.5 text-muted-foreground transition-colors hover:text-foreground\"\n            href=\"/\"\n          >\n            <ArrowLeft aria-hidden=\"true\" className=\"size-3.5 shrink-0\" />\n            <span>Demo</span>\n          </BreadcrumbLink>\n        </BreadcrumbItem>\n        <BreadcrumbSeparator className=\"text-muted-foreground\">\n          /\n        </BreadcrumbSeparator>\n        <BreadcrumbItem>\n          <BreadcrumbPage className=\"font-normal text-muted-foreground\">\n            {title}\n          </BreadcrumbPage>\n        </BreadcrumbItem>\n      </BreadcrumbList>\n    </Breadcrumb>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/demo-breadcrumb.tsx"
    },
    {
      "path": "registry/openai-agents-sdk-demo/components/demo-chat/use-demo-chat.ts",
      "content": "\"use client\";\n\nimport { Chat, useChat } from \"@ai-sdk/react\";\nimport { type ChatStatus, DefaultChatTransport, type UIMessage } from \"ai\";\nimport { useState } from \"react\";\n\ninterface CreateDemoChatOptions {\n  api: string;\n}\n\ninterface UseDemoChatWithApiOptions extends CreateDemoChatOptions {\n  createChat?: never;\n}\n\ninterface UseDemoChatWithFactoryOptions<TMessage extends UIMessage> {\n  api?: never;\n  createChat: () => Chat<TMessage>;\n}\n\ntype UseDemoChatOptions<TMessage extends UIMessage> =\n  | UseDemoChatWithApiOptions\n  | UseDemoChatWithFactoryOptions<TMessage>;\n\nexport function createDemoChat<TMessage extends UIMessage = UIMessage>({\n  api,\n}: CreateDemoChatOptions) {\n  return new Chat<TMessage>({\n    transport: new DefaultChatTransport({\n      api,\n    }),\n  });\n}\n\nexport function isDemoChatBusyStatus(status: ChatStatus) {\n  return status === \"submitted\" || status === \"streaming\";\n}\n\nexport function useDemoChat<TMessage extends UIMessage = UIMessage>(\n  options: UseDemoChatOptions<TMessage>\n) {\n  const [chat] = useState(() => {\n    if (options.createChat) {\n      return options.createChat();\n    }\n\n    return createDemoChat<TMessage>({ api: options.api });\n  });\n  const controller = useChat({ chat });\n  const hasMessages = controller.messages.length > 0;\n  const isBusy = isDemoChatBusyStatus(controller.status);\n\n  return {\n    ...controller,\n    chat,\n    hasMessages,\n    isBusy,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/demo-chat/use-demo-chat.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/components/demo-workspace-shell.tsx",
      "content": "import { Badge } from \"@/components/ui/badge\";\nimport { Card } from \"@/components/ui/card\";\nimport { cn } from \"@/lib/utils\";\nimport type { ReactNode } from \"react\";\n\nimport { DemoBreadcrumb } from \"@/components/demo-breadcrumb\";\n\ntype DemoWorkspaceHeaderFrame = \"header\" | \"card\";\n\ninterface DemoWorkspaceShellProps {\n  badges?: readonly ReactNode[];\n  breadcrumbClassName?: string;\n  breadcrumbTitle?: string;\n  children: ReactNode;\n  contentClassName?: string;\n  headerClassName?: string;\n  headerFrame?: DemoWorkspaceHeaderFrame;\n  maxWidthClassName?: string;\n  summary: ReactNode;\n  summaryClassName?: string;\n  title: string;\n  titleClassName?: string;\n  workspaceClassName?: string | null;\n}\n\nconst defaultHeaderClassName =\n  \"grid gap-4 border border-foreground/10 bg-background px-4 py-5 md:grid-cols-[minmax(0,1fr)_auto] md:items-end\";\n\nconst cardHeaderClassName =\n  \"grid gap-4 bg-background px-4 py-5 text-base text-foreground leading-normal md:grid-cols-[minmax(0,1fr)_auto] md:items-end\";\n\nexport function DemoWorkspaceShell({\n  badges = [],\n  breadcrumbClassName,\n  breadcrumbTitle,\n  children,\n  contentClassName,\n  headerClassName,\n  headerFrame = \"header\",\n  maxWidthClassName = \"max-w-7xl\",\n  summary,\n  summaryClassName = \"max-w-3xl\",\n  title,\n  titleClassName = \"max-w-3xl\",\n  workspaceClassName = \"lg:h-svh\",\n}: DemoWorkspaceShellProps) {\n  const headerContent = (\n    <>\n      <div className=\"space-y-2\">\n        <DemoBreadcrumb\n          className={breadcrumbClassName}\n          title={breadcrumbTitle ?? title}\n        />\n        <h1\n          className={cn(\"font-medium text-2xl tracking-tight\", titleClassName)}\n        >\n          {title}\n        </h1>\n        <p\n          className={cn(\n            \"text-muted-foreground text-sm/relaxed\",\n            summaryClassName\n          )}\n        >\n          {summary}\n        </p>\n      </div>\n\n      {badges.length > 0 ? (\n        <div className=\"flex flex-wrap items-center gap-2\">\n          {badges.map((badge, index) => (\n            <Badge key={String(index)} variant=\"outline\">\n              {badge}\n            </Badge>\n          ))}\n        </div>\n      ) : null}\n    </>\n  );\n\n  return (\n    <main className=\"min-h-svh bg-background text-foreground\">\n      <div\n        className={cn(\n          \"mx-auto flex w-full flex-col gap-6 px-4 py-6 md:px-6\",\n          maxWidthClassName,\n          contentClassName\n        )}\n      >\n        {headerFrame === \"card\" ? (\n          <Card className={cn(cardHeaderClassName, headerClassName)}>\n            {headerContent}\n          </Card>\n        ) : (\n          <header className={cn(defaultHeaderClassName, headerClassName)}>\n            {headerContent}\n          </header>\n        )}\n\n        {workspaceClassName ? (\n          <div className={workspaceClassName}>{children}</div>\n        ) : (\n          children\n        )}\n      </div>\n    </main>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/demo-workspace-shell.tsx"
    },
    {
      "path": "registry/openai-agents-sdk-demo/components/ai-elements/prompt-input.tsx",
      "content": "\"use client\";\n\nimport { CornerDownLeftIcon, LoaderCircleIcon, SquareIcon } from \"lucide-react\";\nimport type {\n  ComponentProps,\n  FormEvent,\n  HTMLAttributes,\n  ReactNode,\n  TextareaHTMLAttributes,\n} from \"react\";\nimport {\n  createContext,\n  useCallback,\n  useContext,\n  useMemo,\n  useState,\n} from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { cn } from \"@/lib/utils\";\n\ntype PromptStatus = \"error\" | \"ready\" | \"streaming\" | \"submitted\";\n\ninterface PromptInputContextValue {\n  setText: (text: string) => void;\n  text: string;\n}\n\ninterface PromptInputMessage {\n  text: string;\n}\n\ninterface PromptInputProps\n  extends Omit<HTMLAttributes<HTMLFormElement>, \"onSubmit\"> {\n  children: ReactNode;\n  onSubmit: (\n    message: PromptInputMessage,\n    event: FormEvent<HTMLFormElement>\n  ) => void | Promise<void>;\n}\n\ninterface PromptInputSubmitProps\n  extends Omit<ComponentProps<typeof Button>, \"children\" | \"type\"> {\n  onStop?: () => void;\n  status?: PromptStatus;\n}\n\ntype PromptInputClickEvent = Parameters<\n  Exclude<ComponentProps<typeof Button>[\"onClick\"], undefined>\n>[0];\n\nconst PromptInputContext = createContext<PromptInputContextValue | null>(null);\n\nfunction usePromptInputContext() {\n  const context = useContext(PromptInputContext);\n\n  if (!context) {\n    throw new Error(\n      \"PromptInput components must be used inside <PromptInput>.\"\n    );\n  }\n\n  return context;\n}\n\nexport function PromptInput({\n  children,\n  className,\n  onSubmit,\n  ...props\n}: PromptInputProps) {\n  const [text, setText] = useState(\"\");\n\n  const contextValue = useMemo(\n    () => ({\n      setText,\n      text,\n    }),\n    [text]\n  );\n\n  const handleSubmit = useCallback(\n    async (event: FormEvent<HTMLFormElement>) => {\n      event.preventDefault();\n\n      const nextText = text.trim();\n      if (!nextText) {\n        return;\n      }\n\n      const result = onSubmit({ text: nextText }, event);\n\n      try {\n        await result;\n        setText(\"\");\n      } catch {\n        // Keep the text for retry after a failed submit.\n      }\n    },\n    [onSubmit, text]\n  );\n\n  return (\n    <PromptInputContext.Provider value={contextValue}>\n      <form\n        className={cn(\"w-full\", className)}\n        onSubmit={handleSubmit}\n        {...props}\n      >\n        {children}\n      </form>\n    </PromptInputContext.Provider>\n  );\n}\n\nexport function PromptInputBody({\n  className,\n  ...props\n}: HTMLAttributes<HTMLDivElement>) {\n  return <div className={cn(\"grid gap-3\", className)} {...props} />;\n}\n\nexport function PromptInputFooter({\n  className,\n  ...props\n}: HTMLAttributes<HTMLDivElement>) {\n  return <div className={cn(className)} {...props} />;\n}\n\nexport function PromptInputTextarea({\n  className,\n  disabled,\n  onChange,\n  ...props\n}: TextareaHTMLAttributes<HTMLTextAreaElement>) {\n  const context = usePromptInputContext();\n\n  return (\n    <Textarea\n      className={cn(\"min-h-16 resize-none\", className)}\n      disabled={disabled}\n      onChange={(event) => {\n        context.setText(event.currentTarget.value);\n        onChange?.(event);\n      }}\n      value={context.text}\n      {...props}\n    />\n  );\n}\n\nexport function PromptInputSubmit({\n  className,\n  disabled,\n  onClick,\n  onStop,\n  size = \"icon\",\n  status = \"ready\",\n  variant = \"default\",\n  ...props\n}: PromptInputSubmitProps) {\n  const { text } = usePromptInputContext();\n  const isBusy = status === \"submitted\" || status === \"streaming\";\n  const isDisabled = Boolean(disabled) || (!isBusy && text.trim().length === 0);\n\n  let icon = <CornerDownLeftIcon className=\"size-4\" />;\n\n  if (status === \"submitted\") {\n    icon = <LoaderCircleIcon className=\"size-4 animate-spin\" />;\n  } else if (status === \"streaming\") {\n    icon = <SquareIcon className=\"size-4\" />;\n  }\n\n  return (\n    <Button\n      aria-label={isBusy ? \"Stop\" : \"Submit\"}\n      className={cn(className)}\n      disabled={isDisabled}\n      onClick={(event: PromptInputClickEvent) => {\n        if (isBusy && onStop) {\n          event.preventDefault();\n          onStop();\n          return;\n        }\n\n        onClick?.(event);\n      }}\n      size={size}\n      type={isBusy && onStop ? \"button\" : \"submit\"}\n      variant={variant}\n      {...props}\n    >\n      {icon}\n    </Button>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/ai-elements/prompt-input.tsx"
    },
    {
      "path": "registry/openai-agents-sdk-demo/components/openai-agents-sdk-demo/openai-agents-sdk-demo-runtime-inspector.ts",
      "content": "import type {\n  OpenAiAgentsSdkDemoMessage,\n  OpenAiAgentsSdkDemoMessageMetadata,\n} from \"@/lib/openai-agents-sdk-demo/message-metadata\";\nimport type { OpenAiAgentsSdkDemoAiSdkExtensionProfile } from \"@/lib/openai-agents-sdk-demo/server/extensions\";\nimport type { OpenAiAgentsSdkDemoGuideCoverage } from \"@/lib/openai-agents-sdk-demo/server/guide-coverage\";\nimport type { OpenAiAgentsSdkDemoTraceProfile } from \"@/lib/openai-agents-sdk-demo/server/tracing\";\nimport { hasOpenAiAgentsSdkDemoVisibleContent } from \"./openai-agents-sdk-demo-session\";\n\ntype MetadataKey = keyof OpenAiAgentsSdkDemoMessageMetadata;\n\nexport interface OpenAiAgentsSdkDemoRuntimeInspectorInput {\n  aiSdkExtensionProfile: OpenAiAgentsSdkDemoAiSdkExtensionProfile;\n  guideCoverage: OpenAiAgentsSdkDemoGuideCoverage[];\n  hasUsedVoiceGuide: boolean;\n  messages: OpenAiAgentsSdkDemoMessage[];\n  traceProfile: OpenAiAgentsSdkDemoTraceProfile;\n}\n\nexport interface OpenAiAgentsSdkDemoRuntimeInspector {\n  aiSdkModelAdapterStatus: string;\n  aiSdkUiBridgeStatus: string;\n  guideCoverageWithCurrentRun: OpenAiAgentsSdkDemoGuideCoverage[];\n  hasPendingApproval: boolean;\n  lastAiSdkExtensionSummary?: OpenAiAgentsSdkDemoMessageMetadata[\"aiSdkExtensionSummary\"];\n  lastApprovalSummary?: OpenAiAgentsSdkDemoMessageMetadata[\"approvalSummary\"];\n  lastContextSummary?: OpenAiAgentsSdkDemoMessageMetadata[\"contextSummary\"];\n  lastHandoffSummary?: OpenAiAgentsSdkDemoMessageMetadata[\"handoffSummary\"];\n  lastMcpSummary?: OpenAiAgentsSdkDemoMessageMetadata[\"mcpSummary\"];\n  lastResponseId?: string;\n  lastResultSummary?: OpenAiAgentsSdkDemoMessageMetadata[\"resultSummary\"];\n  lastSandboxSummary?: OpenAiAgentsSdkDemoMessageMetadata[\"sandboxSummary\"];\n  lastSessionSummary?: OpenAiAgentsSdkDemoMessageMetadata[\"sessionSummary\"];\n  lastStreamSummary?: OpenAiAgentsSdkDemoMessageMetadata[\"streamSummary\"];\n  lastTraceSummary?: OpenAiAgentsSdkDemoMessageMetadata[\"traceSummary\"];\n  traceIncludesSensitiveData: boolean;\n  usedGuardrailNames: Set<string>;\n  usedGuideIds: Set<string>;\n  usedToolNames: Set<string>;\n}\n\nfunction getLatestAssistantMessage(messages: OpenAiAgentsSdkDemoMessage[]) {\n  return [...messages]\n    .reverse()\n    .find((message) => message.role === \"assistant\");\n}\n\nfunction getLatestAssistantMetadata<K extends MetadataKey>(\n  messages: OpenAiAgentsSdkDemoMessage[],\n  key: K\n) {\n  return [...messages]\n    .reverse()\n    .find((message) => message.role === \"assistant\" && message.metadata?.[key])\n    ?.metadata?.[key];\n}\n\nexport function buildOpenAiAgentsSdkDemoRuntimeInspector({\n  aiSdkExtensionProfile,\n  guideCoverage,\n  hasUsedVoiceGuide,\n  messages,\n  traceProfile,\n}: OpenAiAgentsSdkDemoRuntimeInspectorInput): OpenAiAgentsSdkDemoRuntimeInspector {\n  const hasAssistantOutput = messages.some(\n    (message) =>\n      message.role === \"assistant\" &&\n      hasOpenAiAgentsSdkDemoVisibleContent(message)\n  );\n  const usedGuideIds = new Set(\n    messages.flatMap((message) => message.metadata?.usedGuideIds ?? [])\n  );\n  const usedToolNames = new Set(\n    messages.flatMap((message) => message.metadata?.usedToolNames ?? [])\n  );\n  const usedGuardrailNames = new Set(\n    messages.flatMap((message) => message.metadata?.usedGuardrailNames ?? [])\n  );\n  const lastAiSdkExtensionSummary = getLatestAssistantMetadata(\n    messages,\n    \"aiSdkExtensionSummary\"\n  );\n  const lastApprovalSummary = getLatestAssistantMetadata(\n    messages,\n    \"approvalSummary\"\n  );\n  const lastTraceSummary = getLatestAssistantMetadata(messages, \"traceSummary\");\n  const guideCoverageWithCurrentRun = guideCoverage.map((item) => {\n    const wasUsedThisRun =\n      ((item.id === \"agents\" || item.id === \"models\") && hasAssistantOutput) ||\n      usedGuideIds.has(item.id) ||\n      (item.id === \"voice-agents\" && hasUsedVoiceGuide);\n\n    if (!wasUsedThisRun) {\n      return item;\n    }\n\n    return {\n      ...item,\n      currentRunStatus: \"used-this-run\" as const,\n    };\n  });\n\n  return {\n    aiSdkModelAdapterStatus:\n      lastAiSdkExtensionSummary?.modelAdapterStatus ??\n      aiSdkExtensionProfile.modelAdapter.status,\n    aiSdkUiBridgeStatus:\n      lastAiSdkExtensionSummary?.uiBridgeStatus ??\n      aiSdkExtensionProfile.uiBridge.status,\n    guideCoverageWithCurrentRun,\n    hasPendingApproval: Boolean(lastApprovalSummary?.hasPendingApprovals),\n    lastAiSdkExtensionSummary,\n    lastApprovalSummary,\n    lastContextSummary: getLatestAssistantMetadata(messages, \"contextSummary\"),\n    lastHandoffSummary: getLatestAssistantMetadata(messages, \"handoffSummary\"),\n    lastMcpSummary: getLatestAssistantMetadata(messages, \"mcpSummary\"),\n    lastResponseId:\n      getLatestAssistantMessage(messages)?.metadata?.lastResponseId,\n    lastResultSummary: getLatestAssistantMetadata(messages, \"resultSummary\"),\n    lastSandboxSummary: getLatestAssistantMetadata(messages, \"sandboxSummary\"),\n    lastSessionSummary: getLatestAssistantMetadata(messages, \"sessionSummary\"),\n    lastStreamSummary: getLatestAssistantMetadata(messages, \"streamSummary\"),\n    lastTraceSummary,\n    traceIncludesSensitiveData:\n      lastTraceSummary?.traceIncludeSensitiveData ??\n      traceProfile.traceIncludeSensitiveData,\n    usedGuardrailNames,\n    usedGuideIds,\n    usedToolNames,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/openai-agents-sdk-demo/openai-agents-sdk-demo-runtime-inspector.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/components/openai-agents-sdk-demo/openai-agents-sdk-demo-screen.tsx",
      "content": "import { DemoWorkspaceShell } from \"@/components/demo-workspace-shell\";\n\nimport { getOpenAiAgentsSdkDemoRuntimeState } from \"@/lib/openai-agents-sdk-demo/server/runtime\";\nimport { OpenAiAgentsSdkDemoWorkspace } from \"@/components/openai-agents-sdk-demo/openai-agents-sdk-demo-workspace\";\n\nexport function OpenAiAgentsSdkDemoScreen() {\n  const runtimeState = getOpenAiAgentsSdkDemoRuntimeState();\n\n  return (\n    <DemoWorkspaceShell\n      badges={[runtimeState.statusLabel, runtimeState.chatModel]}\n      breadcrumbTitle=\"OpenAI Agents SDK\"\n      summary=\"This slice proves the narrowest official integration path: OpenAI Agents SDK on the server, the official AI SDK UI bridge in the route, and the existing chat workspace left intact.\"\n      title=\"Official OpenAI Agents backend, current AI SDK frontend\"\n      workspaceClassName={null}\n    >\n      <OpenAiAgentsSdkDemoWorkspace\n        aiSdkExtensionProfile={runtimeState.aiSdkExtensionProfile}\n        chatModel={runtimeState.chatModel}\n        contextProfile={runtimeState.contextProfile}\n        guardrailCatalog={runtimeState.guardrailCatalog}\n        guideCoverage={runtimeState.guideCoverage}\n        handoffCatalog={runtimeState.handoffCatalog}\n        isChatAvailable={runtimeState.isChatAvailable}\n        mcpCatalog={runtimeState.mcpCatalog}\n        mcpProfile={runtimeState.mcpProfile}\n        modelProfile={runtimeState.modelProfile}\n        nodeVersion={runtimeState.nodeVersion}\n        runProfile={runtimeState.runProfile}\n        sandboxProfile={runtimeState.sandboxProfile}\n        sessionProfile={runtimeState.sessionProfile}\n        setupMessage={runtimeState.setupMessage}\n        toolCatalog={runtimeState.toolCatalog}\n        traceProfile={runtimeState.traceProfile}\n        voiceProfile={runtimeState.voiceProfile}\n      />\n    </DemoWorkspaceShell>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/openai-agents-sdk-demo/openai-agents-sdk-demo-screen.tsx"
    },
    {
      "path": "registry/openai-agents-sdk-demo/components/openai-agents-sdk-demo/openai-agents-sdk-demo-session.ts",
      "content": "import type { ToolPart } from \"@/components/ai-elements/tool\";\nimport {\n  type FileUIPart,\n  isReasoningUIPart,\n  isToolUIPart,\n  type SourceUrlUIPart,\n  type UIMessage,\n} from \"ai\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"@/lib/openai-agents-sdk-demo/message-metadata\";\n\nexport interface OpenAiAgentsSdkDemoSourcePart {\n  sourceId: string;\n  title: string;\n  url: string;\n}\n\nexport interface OpenAiAgentsSdkDemoApprovalInputField {\n  label: string;\n  value: string;\n}\n\nconst markdownLinkPattern = /\\[([^\\]]+)\\]\\((https?:\\/\\/[^\\s)]+)\\)/g;\nconst reasoningSignalPlaceholder =\n  \"The model emitted a reasoning item for this turn, but the upstream stream did not expose revealable reasoning text.\";\nconst explicitUrlPattern = /\\bhttps?:\\/\\/[^\\s)]+/g;\nconst domainCitationPattern =\n  /\\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,}(?:\\/[^\\s),\\]]*)?/gi;\n\nfunction normalizeCitationUrl(value: string) {\n  const trimmed = value.trim().replace(/[),.;]+$/g, \"\");\n\n  if (trimmed.startsWith(\"http://\") || trimmed.startsWith(\"https://\")) {\n    return trimmed;\n  }\n\n  return `https://${trimmed}`;\n}\n\nfunction collectTextCitationSources(message: UIMessage) {\n  const text = getOpenAiAgentsSdkDemoMessageText(message);\n\n  if (text.trim().length === 0) {\n    return [] as OpenAiAgentsSdkDemoSourcePart[];\n  }\n\n  const sources = new Map<string, OpenAiAgentsSdkDemoSourcePart>();\n\n  for (const match of text.matchAll(markdownLinkPattern)) {\n    const [, label, rawUrl] = match;\n\n    if (typeof rawUrl !== \"string\") {\n      continue;\n    }\n\n    const url = normalizeCitationUrl(rawUrl);\n    const title = typeof label === \"string\" ? label.trim() : \"\";\n\n    sources.set(url, {\n      sourceId: url,\n      title: title || url,\n      url,\n    });\n  }\n\n  for (const match of text.matchAll(explicitUrlPattern)) {\n    const url = normalizeCitationUrl(match[0]);\n\n    if (sources.has(url)) {\n      continue;\n    }\n\n    sources.set(url, {\n      sourceId: url,\n      title: url.replace(/^https?:\\/\\//, \"\"),\n      url,\n    });\n  }\n\n  for (const match of text.matchAll(domainCitationPattern)) {\n    const url = normalizeCitationUrl(match[0]);\n\n    if (sources.has(url)) {\n      continue;\n    }\n\n    sources.set(url, {\n      sourceId: url,\n      title: url.replace(/^https?:\\/\\//, \"\"),\n      url,\n    });\n  }\n\n  return [...sources.values()].sort((left, right) => {\n    const leftIndex = text.indexOf(left.title);\n    const rightIndex = text.indexOf(right.title);\n\n    return leftIndex - rightIndex;\n  });\n}\n\nfunction getStringRecord(value: unknown) {\n  if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n    return null;\n  }\n\n  return value as Record<string, unknown>;\n}\n\nexport function getOpenAiAgentsSdkDemoMessageText(message: UIMessage) {\n  return message.parts\n    .filter((part) => part.type === \"text\")\n    .map((part) => part.text)\n    .join(\"\\n\");\n}\n\nexport function getOpenAiAgentsSdkDemoReasoningText(message: UIMessage) {\n  return message.parts\n    .filter(isReasoningUIPart)\n    .map((part) => part.text)\n    .join(\"\\n\\n\");\n}\n\nfunction hasOpenAiAgentsSdkDemoReasoningStreamSignal(message: UIMessage) {\n  const metadata = message.metadata as\n    | OpenAiAgentsSdkDemoMessageMetadata\n    | undefined;\n  const streamSummary = metadata?.streamSummary;\n\n  if (!streamSummary) {\n    return false;\n  }\n\n  return (\n    streamSummary.runItemEventNames.includes(\"reasoning_item_created\") ||\n    streamSummary.rawModelEventTypes.some((eventType) =>\n      eventType.toLowerCase().includes(\"reasoning\")\n    )\n  );\n}\n\nexport function getOpenAiAgentsSdkDemoRenderableReasoningText(\n  message: UIMessage\n) {\n  const reasoningText = getOpenAiAgentsSdkDemoReasoningText(message).trim();\n\n  if (reasoningText.length > 0) {\n    return reasoningText;\n  }\n\n  if (hasOpenAiAgentsSdkDemoReasoningStreamSignal(message)) {\n    return reasoningSignalPlaceholder;\n  }\n\n  return \"\";\n}\n\nexport function getOpenAiAgentsSdkDemoFileParts(message: UIMessage) {\n  return message.parts.filter(\n    (part): part is FileUIPart => part.type === \"file\"\n  );\n}\n\nexport function getOpenAiAgentsSdkDemoSourceParts(message: UIMessage) {\n  const explicitSources = message.parts\n    .filter(\n      (part): part is SourceUrlUIPart =>\n        part.type === \"source-url\" &&\n        typeof part.sourceId === \"string\" &&\n        typeof part.url === \"string\"\n    )\n    .map((part) => ({\n      sourceId: part.sourceId,\n      title: part.title?.trim() || part.url,\n      url: part.url,\n    }));\n\n  if (explicitSources.length > 0) {\n    return explicitSources;\n  }\n\n  return collectTextCitationSources(message);\n}\n\nexport function getOpenAiAgentsSdkDemoToolParts(message: UIMessage) {\n  return message.parts.filter(isToolUIPart) as ToolPart[];\n}\n\nexport function shouldRenderOpenAiAgentsSdkDemoReasoning(\n  message: UIMessage,\n  nextMessage?: UIMessage\n) {\n  const reasoningText =\n    getOpenAiAgentsSdkDemoRenderableReasoningText(message).trim();\n\n  if (reasoningText.length === 0) {\n    return false;\n  }\n\n  if (nextMessage?.role !== \"assistant\") {\n    return true;\n  }\n\n  const hasRespondedApproval = getOpenAiAgentsSdkDemoToolParts(message).some(\n    (part) => part.state === \"approval-responded\"\n  );\n\n  if (!hasRespondedApproval) {\n    return true;\n  }\n\n  return (\n    getOpenAiAgentsSdkDemoRenderableReasoningText(nextMessage).trim().length ===\n    0\n  );\n}\n\nexport function hasOpenAiAgentsSdkDemoVisibleContent(message: UIMessage) {\n  return (\n    getOpenAiAgentsSdkDemoMessageText(message).trim().length > 0 ||\n    getOpenAiAgentsSdkDemoRenderableReasoningText(message).trim().length > 0 ||\n    getOpenAiAgentsSdkDemoFileParts(message).length > 0 ||\n    getOpenAiAgentsSdkDemoSourceParts(message).length > 0 ||\n    getOpenAiAgentsSdkDemoToolParts(message).length > 0\n  );\n}\n\nexport function getOpenAiAgentsSdkDemoFailedTurnRetryText(\n  messages: UIMessage[]\n) {\n  const lastMessage = messages.at(-1);\n\n  if (lastMessage?.role === \"user\") {\n    const text = getOpenAiAgentsSdkDemoMessageText(lastMessage).trim();\n    return text.length > 0 ? text : null;\n  }\n\n  const previousMessage = messages.at(-2);\n\n  if (lastMessage?.role === \"assistant\" && previousMessage?.role === \"user\") {\n    const text = getOpenAiAgentsSdkDemoMessageText(previousMessage).trim();\n    return text.length > 0 ? text : null;\n  }\n\n  return null;\n}\n\nexport function getOpenAiAgentsSdkDemoRecoverableMessages<\n  TMessage extends UIMessage,\n>(messages: TMessage[]) {\n  const nextMessages = [...messages];\n  const lastMessage = nextMessages.at(-1);\n\n  if (lastMessage?.role === \"assistant\") {\n    nextMessages.pop();\n  }\n\n  if (nextMessages.at(-1)?.role === \"user\") {\n    nextMessages.pop();\n  }\n\n  return nextMessages;\n}\n\nexport function getOpenAiAgentsSdkDemoToolName(part: ToolPart) {\n  return part.type === \"dynamic-tool\"\n    ? part.toolName\n    : part.type.split(\"-\").slice(1).join(\"-\");\n}\n\nexport function getOpenAiAgentsSdkDemoToolDisplayState(\n  part: ToolPart,\n  { isMessageStreaming }: { isMessageStreaming: boolean }\n): ToolPart[\"state\"] {\n  if (\n    !isMessageStreaming &&\n    (part.state === \"input-available\" || part.state === \"input-streaming\")\n  ) {\n    return \"output-available\";\n  }\n\n  return part.state;\n}\n\nexport function getOpenAiAgentsSdkDemoApprovalInputFields(\n  part: ToolPart\n): OpenAiAgentsSdkDemoApprovalInputField[] {\n  const input = getStringRecord(part.input);\n\n  if (!input) {\n    return [];\n  }\n\n  const approvalFields = [\n    [\"audience\", \"Audience\"],\n    [\"company\", \"Company\"],\n    [\"summary\", \"Summary\"],\n  ] as const;\n\n  return approvalFields.flatMap(([key, label]) => {\n    const value = input[key];\n\n    if (typeof value !== \"string\" || value.trim().length === 0) {\n      return [];\n    }\n\n    return [\n      {\n        label,\n        value,\n      },\n    ];\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/openai-agents-sdk-demo/openai-agents-sdk-demo-session.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/components/openai-agents-sdk-demo/openai-agents-sdk-demo-voice-panel.tsx",
      "content": "\"use client\";\n\nimport type { RunToolApprovalItem } from \"@openai/agents\";\nimport {\n  OpenAIRealtimeWebRTC,\n  type RealtimeAgent,\n  RealtimeSession,\n  type RealtimeSessionConnectOptions,\n} from \"@openai/agents/realtime\";\nimport {\n  MicrophoneIcon,\n  MicrophoneSlashIcon,\n  PhoneDisconnectIcon,\n  SpeakerHighIcon,\n  StopCircleIcon,\n  WaveformIcon,\n} from \"@phosphor-icons/react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n} from \"@/components/ui/dialog\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport type { OpenAiAgentsSdkDemoVoiceProfile } from \"@/lib/openai-agents-sdk-demo/server/voice\";\nimport {\n  createOpenAiAgentsSdkDemoVoiceAgentBundle,\n  getOpenAiAgentsSdkDemoVoiceLaneProfile,\n} from \"@/lib/openai-agents-sdk-demo/voice-lane\";\nimport { getOpenAiAgentsSdkDemoVoiceHistorySummary } from \"./openai-agents-sdk-demo-voice-utils\";\nimport {\n  getOpenAiAgentsSdkDemoVoicePrimarySummary,\n  openAiAgentsSdkDemoWorkspaceLayout,\n  type OpenAiAgentsSdkDemoVoiceConnectionStatus as VoiceConnectionStatus,\n} from \"./openai-agents-sdk-demo-workspace-layout\";\n\ninterface OpenAiAgentsSdkDemoVoiceClientSecretResponse {\n  error?: string;\n  value?: string;\n}\n\ninterface PendingVoiceApproval {\n  agentName: string;\n  approvalItem: RunToolApprovalItem;\n  arguments: string | null;\n  toolName: string;\n  type: \"function_approval\" | \"mcp_approval_request\";\n}\n\nfunction getVoicePanelStatusLabel(status: VoiceConnectionStatus) {\n  if (status === \"connected\") {\n    return \"Connected\";\n  }\n\n  if (status === \"connecting\") {\n    return \"Connecting\";\n  }\n\n  if (status === \"error\") {\n    return \"Error\";\n  }\n\n  return \"Disconnected\";\n}\n\nfunction getVoicePanelStatusClassName(status: VoiceConnectionStatus) {\n  if (status === \"connected\") {\n    return \"border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300\";\n  }\n\n  if (status === \"connecting\") {\n    return \"border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\";\n  }\n\n  if (status === \"error\") {\n    return \"border-destructive/30 bg-destructive/10 text-destructive\";\n  }\n\n  return \"border-foreground/10 bg-muted/40 text-muted-foreground\";\n}\n\nfunction getVoiceSessionId() {\n  if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n    return crypto.randomUUID();\n  }\n\n  return `voice-${Date.now()}`;\n}\n\nfunction getErrorMessage(error: unknown) {\n  if (error instanceof Error) {\n    return error.message;\n  }\n\n  return \"Voice session setup failed.\";\n}\n\nexport function OpenAiAgentsSdkDemoVoicePanel({\n  onUsageChange,\n  voiceProfile,\n}: {\n  onUsageChange: (used: boolean) => void;\n  voiceProfile: OpenAiAgentsSdkDemoVoiceProfile;\n}) {\n  const laneProfile = getOpenAiAgentsSdkDemoVoiceLaneProfile();\n  const audioElementRef = useRef<HTMLAudioElement | null>(null);\n  const mediaStreamRef = useRef<MediaStream | null>(null);\n  const sessionRef = useRef<RealtimeSession | null>(null);\n  const [connectionStatus, setConnectionStatus] =\n    useState<VoiceConnectionStatus>(\"disconnected\");\n  const [errorMessage, setErrorMessage] = useState<string | null>(null);\n  const [historyItemCount, setHistoryItemCount] = useState(0);\n  const [isAudioPlaying, setIsAudioPlaying] = useState(false);\n  const [isAgentResponding, setIsAgentResponding] = useState(false);\n  const [isDialogOpen, setIsDialogOpen] = useState(false);\n  const [isMuted, setIsMuted] = useState(false);\n  const [lastAssistantTranscript, setLastAssistantTranscript] = useState<\n    string | null\n  >(null);\n  const [lastHandoffLabel, setLastHandoffLabel] = useState<string | null>(null);\n  const [lastEventType, setLastEventType] = useState<string | null>(null);\n  const [lastToolCallLabel, setLastToolCallLabel] = useState<string | null>(\n    null\n  );\n  const [lastUserTranscript, setLastUserTranscript] = useState<string | null>(\n    null\n  );\n  const [pendingApproval, setPendingApproval] =\n    useState<PendingVoiceApproval | null>(null);\n  const [realtimeEventFeed, setRealtimeEventFeed] = useState<string[]>([]);\n  const [availableMcpToolCount, setAvailableMcpToolCount] = useState(0);\n  const [textInput, setTextInput] = useState(\"\");\n  const [usedVoiceGuide, setUsedVoiceGuide] = useState(false);\n\n  useEffect(() => {\n    onUsageChange(usedVoiceGuide);\n  }, [onUsageChange, usedVoiceGuide]);\n\n  const closeCurrentVoiceSession = useCallback(() => {\n    sessionRef.current?.close();\n    sessionRef.current = null;\n\n    mediaStreamRef.current?.getTracks().forEach((track) => track.stop());\n    mediaStreamRef.current = null;\n\n    setConnectionStatus(\"disconnected\");\n    setIsAudioPlaying(false);\n    setIsAgentResponding(false);\n    setIsMuted(false);\n    setPendingApproval(null);\n  }, []);\n\n  useEffect(\n    () => () => {\n      sessionRef.current?.close();\n      mediaStreamRef.current?.getTracks().forEach((track) => track.stop());\n    },\n    []\n  );\n\n  const connectVoiceSession = useCallback(async () => {\n    if (voiceProfile.browserTransport.status !== \"configured\") {\n      setConnectionStatus(\"error\");\n      setErrorMessage(\n        \"OPENAI_API_KEY is missing. Voice Agents browser transport requires the realtime client-secret route to be configured first.\"\n      );\n\n      return;\n    }\n\n    if (\n      typeof navigator === \"undefined\" ||\n      !navigator.mediaDevices ||\n      !navigator.mediaDevices.getUserMedia\n    ) {\n      setConnectionStatus(\"error\");\n      setErrorMessage(\n        \"This browser does not expose navigator.mediaDevices.getUserMedia(), so the official WebRTC voice path cannot start.\"\n      );\n\n      return;\n    }\n\n    closeCurrentVoiceSession();\n    setConnectionStatus(\"connecting\");\n    setErrorMessage(null);\n\n    try {\n      const pushRealtimeEvent = (value: string) => {\n        setRealtimeEventFeed((current) => [value, ...current].slice(0, 8));\n      };\n      const clientSecretResponse = await fetch(\n        voiceProfile.browserTransport.routePath,\n        {\n          body: JSON.stringify({\n            sessionId: getVoiceSessionId(),\n          }),\n          headers: {\n            \"Content-Type\": \"application/json\",\n          },\n          method: \"POST\",\n        }\n      );\n      const clientSecretBody =\n        (await clientSecretResponse.json()) as OpenAiAgentsSdkDemoVoiceClientSecretResponse;\n\n      if (!(clientSecretResponse.ok && clientSecretBody.value)) {\n        throw new Error(\n          clientSecretBody.error ??\n            \"The realtime client-secret route did not return a usable ephemeral token.\"\n        );\n      }\n\n      const mediaStream = await navigator.mediaDevices.getUserMedia({\n        audio: true,\n      });\n      mediaStreamRef.current = mediaStream;\n\n      const audioElement =\n        audioElementRef.current ?? document.createElement(\"audio\");\n      audioElement.autoplay = true;\n\n      const transport = new OpenAIRealtimeWebRTC({\n        audioElement,\n        mediaStream,\n      });\n      const { primaryAgent } = createOpenAiAgentsSdkDemoVoiceAgentBundle();\n      const agent: RealtimeAgent = primaryAgent;\n      const session = new RealtimeSession(agent, {\n        config: {\n          audio: {\n            output: {\n              voice: voiceProfile.browserTransport.sessionVoice,\n            },\n          },\n        },\n        model: voiceProfile.browserTransport.sessionModel,\n        transport,\n      });\n\n      session.on(\"history_updated\", (history) => {\n        const summary = getOpenAiAgentsSdkDemoVoiceHistorySummary(history);\n\n        setHistoryItemCount(summary.historyItemCount);\n        setLastAssistantTranscript(summary.lastAssistantTranscript);\n        setLastUserTranscript(summary.lastUserTranscript);\n\n        if (summary.historyItemCount > 0) {\n          setUsedVoiceGuide(true);\n        }\n      });\n      session.on(\"transport_event\", (event) => {\n        setLastEventType(event.type);\n      });\n      session.on(\"agent_start\", () => {\n        setIsAgentResponding(true);\n        pushRealtimeEvent(\"agent_start\");\n      });\n      session.on(\"agent_end\", (_context, _agent, output) => {\n        setIsAgentResponding(false);\n        pushRealtimeEvent(\"agent_end\");\n\n        if (output.trim().length > 0) {\n          setLastAssistantTranscript(output);\n        }\n      });\n      session.on(\"agent_handoff\", (_context, fromAgent, toAgent) => {\n        setLastHandoffLabel(`${fromAgent.name} -> ${toAgent.name}`);\n        pushRealtimeEvent(\"agent_handoff\");\n      });\n      session.on(\"agent_tool_start\", (_context, _agent, tool) => {\n        setLastToolCallLabel(`${tool.name} running`);\n        pushRealtimeEvent(`agent_tool_start:${tool.name}`);\n      });\n      session.on(\"agent_tool_end\", (_context, _agent, tool) => {\n        setLastToolCallLabel(`${tool.name} completed`);\n        pushRealtimeEvent(`agent_tool_end:${tool.name}`);\n      });\n      session.on(\"tool_approval_requested\", (_context, agent, approval) => {\n        setPendingApproval({\n          agentName: agent.name,\n          arguments: approval.approvalItem.arguments ?? null,\n          approvalItem: approval.approvalItem,\n          toolName: approval.approvalItem.name ?? \"unknown_tool\",\n          type: approval.type,\n        });\n        pushRealtimeEvent(\n          `tool_approval_requested:${approval.approvalItem.name ?? \"unknown_tool\"}`\n        );\n      });\n      session.on(\"guardrail_tripped\", () => {\n        pushRealtimeEvent(\"guardrail_tripped\");\n      });\n      session.on(\"mcp_tools_changed\", (tools) => {\n        setAvailableMcpToolCount(tools.length);\n        pushRealtimeEvent(`mcp_tools_changed:${tools.length}`);\n      });\n      session.on(\"audio_start\", () => {\n        setIsAudioPlaying(true);\n        pushRealtimeEvent(\"audio_start\");\n      });\n      session.on(\"audio_stopped\", () => {\n        setIsAudioPlaying(false);\n        pushRealtimeEvent(\"audio_stopped\");\n      });\n      session.on(\"audio_interrupted\", () => {\n        setIsAudioPlaying(false);\n        pushRealtimeEvent(\"audio_interrupted\");\n      });\n      session.on(\"error\", ({ error }) => {\n        setConnectionStatus(\"error\");\n        setErrorMessage(getErrorMessage(error));\n        pushRealtimeEvent(\"error\");\n      });\n\n      sessionRef.current = session;\n\n      await session.connect({\n        apiKey: clientSecretBody.value,\n      } satisfies RealtimeSessionConnectOptions);\n\n      setConnectionStatus(\"connected\");\n      setIsMuted(session.muted ?? false);\n      setUsedVoiceGuide(true);\n    } catch (error) {\n      closeCurrentVoiceSession();\n      setConnectionStatus(\"error\");\n      setErrorMessage(getErrorMessage(error));\n    }\n  }, [closeCurrentVoiceSession, voiceProfile]);\n\n  const disconnectVoiceSession = useCallback(() => {\n    closeCurrentVoiceSession();\n    setErrorMessage(null);\n  }, [closeCurrentVoiceSession]);\n\n  const toggleMute = useCallback(() => {\n    const session = sessionRef.current;\n\n    if (!session) {\n      return;\n    }\n\n    const nextMuted = !(session.muted ?? false);\n\n    session.mute(nextMuted);\n    setIsMuted(session.muted ?? nextMuted);\n  }, []);\n\n  const interruptVoiceSession = useCallback(() => {\n    sessionRef.current?.interrupt();\n    setIsAudioPlaying(false);\n  }, []);\n\n  const sendTextTurn = useCallback(() => {\n    const session = sessionRef.current;\n    const value = textInput.trim();\n\n    if (!session || value.length === 0) {\n      return;\n    }\n\n    session.sendMessage(value);\n    setTextInput(\"\");\n    setUsedVoiceGuide(true);\n  }, [textInput]);\n\n  const approvePendingToolCall = useCallback(async () => {\n    const session = sessionRef.current;\n\n    if (!(session && pendingApproval)) {\n      return;\n    }\n\n    await session.approve(pendingApproval.approvalItem);\n    setPendingApproval(null);\n    setRealtimeEventFeed((current) =>\n      [`tool_approved:${pendingApproval.toolName}`, ...current].slice(0, 8)\n    );\n  }, [pendingApproval]);\n\n  const rejectPendingToolCall = useCallback(async () => {\n    const session = sessionRef.current;\n\n    if (!(session && pendingApproval)) {\n      return;\n    }\n\n    await session.reject(pendingApproval.approvalItem, {\n      message: \"The operator rejected this external publish request.\",\n    });\n    setPendingApproval(null);\n    setRealtimeEventFeed((current) =>\n      [`tool_rejected:${pendingApproval.toolName}`, ...current].slice(0, 8)\n    );\n  }, [pendingApproval]);\n\n  const providerStatusClassName =\n    voiceProfile.browserTransport.status === \"configured\"\n      ? \"border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300\"\n      : \"border-destructive/30 bg-destructive/10 text-destructive\";\n  const detailsSummary = getOpenAiAgentsSdkDemoVoicePrimarySummary({\n    connectionStatus,\n    errorMessage,\n    hasPendingApproval: Boolean(pendingApproval),\n  });\n\n  return (\n    <>\n      <div className=\"border-foreground/10 border-b p-4\">\n        <div className=\"flex flex-col gap-2\">\n          <div className=\"flex w-full min-w-0 flex-col gap-3 border border-foreground/10 bg-background px-3 py-3\">\n            <div className=\"flex w-full min-w-0 items-start justify-between gap-3\">\n              <div className=\"min-w-0 space-y-1\">\n                <p className=\"font-medium text-sm\">\n                  {openAiAgentsSdkDemoWorkspaceLayout.voiceEntryTitle}\n                </p>\n                <p className=\"text-muted-foreground text-xs leading-5\">\n                  {detailsSummary}\n                </p>\n              </div>\n              <Badge\n                className={getVoicePanelStatusClassName(connectionStatus)}\n                variant=\"outline\"\n              >\n                {getVoicePanelStatusLabel(connectionStatus)}\n              </Badge>\n            </div>\n\n            <div className=\"flex flex-wrap items-center gap-1.5\">\n              <Badge className={providerStatusClassName} variant=\"outline\">\n                {voiceProfile.browserTransport.status}\n              </Badge>\n              {pendingApproval ? (\n                <Badge\n                  className=\"border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n                  variant=\"outline\"\n                >\n                  Approval pending\n                </Badge>\n              ) : null}\n            </div>\n\n            <div className=\"grid grid-cols-2 gap-2\">\n              <Button\n                className=\"min-w-0 justify-start\"\n                disabled={connectionStatus === \"connecting\"}\n                onClick={() => void connectVoiceSession()}\n                size=\"sm\"\n                type=\"button\"\n                variant=\"outline\"\n              >\n                <MicrophoneIcon className=\"size-3.5 shrink-0\" />\n                <span className=\"truncate\">Connect</span>\n              </Button>\n              <Button\n                className=\"min-w-0 justify-start\"\n                disabled={connectionStatus !== \"connected\"}\n                onClick={toggleMute}\n                size=\"sm\"\n                type=\"button\"\n                variant=\"outline\"\n              >\n                {isMuted ? (\n                  <MicrophoneSlashIcon className=\"size-3.5 shrink-0\" />\n                ) : (\n                  <MicrophoneIcon className=\"size-3.5 shrink-0\" />\n                )}\n                <span className=\"truncate\">Mute</span>\n              </Button>\n              <Button\n                className=\"min-w-0 justify-start\"\n                disabled={connectionStatus !== \"connected\"}\n                onClick={interruptVoiceSession}\n                size=\"sm\"\n                type=\"button\"\n                variant=\"outline\"\n              >\n                <StopCircleIcon className=\"size-3.5 shrink-0\" />\n                <span className=\"truncate\">Interrupt</span>\n              </Button>\n              <Button\n                className=\"min-w-0 justify-start\"\n                disabled={\n                  connectionStatus !== \"connected\" &&\n                  connectionStatus !== \"error\"\n                }\n                onClick={disconnectVoiceSession}\n                size=\"sm\"\n                type=\"button\"\n                variant=\"outline\"\n              >\n                <PhoneDisconnectIcon className=\"size-3.5 shrink-0\" />\n                <span className=\"truncate\">Disconnect</span>\n              </Button>\n            </div>\n          </div>\n\n          <Button\n            className=\"w-full justify-center\"\n            onClick={() => setIsDialogOpen(true)}\n            size=\"sm\"\n            type=\"button\"\n            variant=\"outline\"\n          >\n            {openAiAgentsSdkDemoWorkspaceLayout.voiceDetailsButtonLabel}\n          </Button>\n        </div>\n      </div>\n\n      <Dialog onOpenChange={setIsDialogOpen} open={isDialogOpen}>\n        <DialogContent className=\"grid h-[min(90svh,960px)] max-h-[90svh] max-w-[min(960px,calc(100%-2rem))] grid-rows-[auto_minmax(0,1fr)] gap-0 overflow-hidden p-0 sm:max-w-[min(960px,calc(100%-2rem))]\">\n          <DialogHeader className=\"border-foreground/10 border-b px-5 py-4\">\n            <div className=\"flex flex-wrap items-start justify-between gap-3 pr-10\">\n              <div className=\"space-y-1\">\n                <DialogTitle>Voice Agents</DialogTitle>\n                <DialogDescription>\n                  Official browser path: <code>RealtimeSession</code> over{\" \"}\n                  <code>OpenAIRealtimeWebRTC</code>, backed by{\" \"}\n                  <code>{voiceProfile.browserTransport.routePath}</code>.\n                </DialogDescription>\n              </div>\n              <div className=\"flex flex-wrap items-center gap-2\">\n                <Badge\n                  className={getVoicePanelStatusClassName(connectionStatus)}\n                  variant=\"outline\"\n                >\n                  {getVoicePanelStatusLabel(connectionStatus)}\n                </Badge>\n                <Badge className={providerStatusClassName} variant=\"outline\">\n                  {voiceProfile.browserTransport.status}\n                </Badge>\n              </div>\n            </div>\n          </DialogHeader>\n\n          <div className=\"overflow-y-auto px-5 py-5\">\n            <div className=\"space-y-4\">\n              <div className=\"flex flex-wrap items-center gap-2\">\n                <Button\n                  disabled={connectionStatus === \"connecting\"}\n                  onClick={() => void connectVoiceSession()}\n                  size=\"sm\"\n                  type=\"button\"\n                  variant=\"outline\"\n                >\n                  <MicrophoneIcon className=\"size-3.5\" />\n                  Connect\n                </Button>\n                <Button\n                  disabled={connectionStatus !== \"connected\"}\n                  onClick={toggleMute}\n                  size=\"sm\"\n                  type=\"button\"\n                  variant=\"outline\"\n                >\n                  {isMuted ? (\n                    <MicrophoneSlashIcon className=\"size-3.5\" />\n                  ) : (\n                    <MicrophoneIcon className=\"size-3.5\" />\n                  )}\n                  {isMuted ? \"Unmute\" : \"Mute\"}\n                </Button>\n                <Button\n                  disabled={connectionStatus !== \"connected\"}\n                  onClick={interruptVoiceSession}\n                  size=\"sm\"\n                  type=\"button\"\n                  variant=\"outline\"\n                >\n                  <StopCircleIcon className=\"size-3.5\" />\n                  Interrupt\n                </Button>\n                <Button\n                  disabled={\n                    connectionStatus !== \"connected\" &&\n                    connectionStatus !== \"error\"\n                  }\n                  onClick={disconnectVoiceSession}\n                  size=\"sm\"\n                  type=\"button\"\n                  variant=\"outline\"\n                >\n                  <PhoneDisconnectIcon className=\"size-3.5\" />\n                  Disconnect\n                </Button>\n              </div>\n\n              <div className=\"grid gap-3 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]\">\n                <div className=\"space-y-3 border border-foreground/10 px-3 py-3 text-sm\">\n                  <div className=\"flex items-start justify-between gap-3\">\n                    <span className=\"text-muted-foreground\">Model</span>\n                    <span>{voiceProfile.browserTransport.sessionModel}</span>\n                  </div>\n                  <div className=\"flex items-start justify-between gap-3\">\n                    <span className=\"text-muted-foreground\">Voice</span>\n                    <span>{voiceProfile.browserTransport.sessionVoice}</span>\n                  </div>\n                  <div className=\"flex items-start justify-between gap-3\">\n                    <span className=\"text-muted-foreground\">Muted</span>\n                    <span>{isMuted ? \"yes\" : \"no\"}</span>\n                  </div>\n                  <div className=\"flex items-start justify-between gap-3\">\n                    <span className=\"text-muted-foreground\">\n                      Audio playback\n                    </span>\n                    <span>{isAudioPlaying ? \"active\" : \"idle\"}</span>\n                  </div>\n                  <div className=\"flex items-start justify-between gap-3\">\n                    <span className=\"text-muted-foreground\">Agent turn</span>\n                    <span>{isAgentResponding ? \"running\" : \"idle\"}</span>\n                  </div>\n                  <div className=\"flex items-start justify-between gap-3\">\n                    <span className=\"text-muted-foreground\">History items</span>\n                    <span>{historyItemCount}</span>\n                  </div>\n                  <div className=\"flex items-start justify-between gap-3\">\n                    <span className=\"text-muted-foreground\">Last handoff</span>\n                    <span className=\"max-w-[10rem] text-right\">\n                      {lastHandoffLabel ?? \"none yet\"}\n                    </span>\n                  </div>\n                  <div className=\"flex items-start justify-between gap-3\">\n                    <span className=\"text-muted-foreground\">Last tool</span>\n                    <span className=\"max-w-[10rem] text-right\">\n                      {lastToolCallLabel ?? \"none yet\"}\n                    </span>\n                  </div>\n                  <div className=\"flex items-start justify-between gap-3\">\n                    <span className=\"text-muted-foreground\">MCP tools</span>\n                    <span>{availableMcpToolCount}</span>\n                  </div>\n                  <div className=\"flex items-start justify-between gap-3\">\n                    <span className=\"text-muted-foreground\">\n                      Last transport event\n                    </span>\n                    <span className=\"max-w-[10rem] text-right\">\n                      {lastEventType ?? \"none yet\"}\n                    </span>\n                  </div>\n                </div>\n\n                <div className=\"space-y-3 border border-foreground/10 px-3 py-3 text-sm\">\n                  <div className=\"space-y-1\">\n                    <p className=\"text-muted-foreground text-xs uppercase tracking-[0.2em]\">\n                      Latest user transcript\n                    </p>\n                    <p className=\"min-h-12 text-sm/relaxed\">\n                      {lastUserTranscript ?? \"No user transcript yet.\"}\n                    </p>\n                  </div>\n                  <div className=\"space-y-1\">\n                    <p className=\"text-muted-foreground text-xs uppercase tracking-[0.2em]\">\n                      Latest assistant transcript\n                    </p>\n                    <p className=\"min-h-12 text-sm/relaxed\">\n                      {lastAssistantTranscript ??\n                        \"No assistant transcript yet.\"}\n                    </p>\n                  </div>\n                </div>\n              </div>\n\n              <div className=\"space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n                <div className=\"flex items-center justify-between gap-3\">\n                  <p className=\"text-muted-foreground text-xs uppercase tracking-[0.2em]\">\n                    Realtime tools and approvals\n                  </p>\n                  <Badge variant=\"outline\">\n                    {laneProfile.toolNames.length} tools\n                  </Badge>\n                </div>\n                <div className=\"flex flex-wrap gap-2\">\n                  {laneProfile.toolNames.map((toolName) => (\n                    <Badge key={toolName} variant=\"outline\">\n                      {toolName}\n                    </Badge>\n                  ))}\n                </div>\n                {pendingApproval ? (\n                  <div className=\"space-y-2 border border-amber-500/30 bg-amber-500/10 px-3 py-3\">\n                    <p className=\"font-medium text-sm\">\n                      Pending approval: {pendingApproval.toolName}\n                    </p>\n                    <p className=\"text-muted-foreground text-xs\">\n                      Agent: {pendingApproval.agentName} · Kind:{\" \"}\n                      {pendingApproval.type}\n                    </p>\n                    <p className=\"text-xs/relaxed\">\n                      {pendingApproval.arguments ??\n                        \"No tool arguments captured.\"}\n                    </p>\n                    <div className=\"flex flex-wrap gap-2\">\n                      <Button\n                        onClick={() => void approvePendingToolCall()}\n                        size=\"sm\"\n                        type=\"button\"\n                        variant=\"outline\"\n                      >\n                        Approve\n                      </Button>\n                      <Button\n                        onClick={() => void rejectPendingToolCall()}\n                        size=\"sm\"\n                        type=\"button\"\n                        variant=\"outline\"\n                      >\n                        Reject\n                      </Button>\n                    </div>\n                  </div>\n                ) : (\n                  <p className=\"text-muted-foreground text-xs\">\n                    No pending approval. Ask the voice agent to publish a\n                    research summary to trigger the official approval event.\n                  </p>\n                )}\n              </div>\n\n              <div className=\"space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n                <p className=\"text-muted-foreground text-xs uppercase tracking-[0.2em]\">\n                  Suggested smoke prompts\n                </p>\n                <div className=\"flex flex-wrap gap-2\">\n                  {laneProfile.recommendedSmokePrompts.map((prompt) => (\n                    <button\n                      className=\"border border-foreground/10 px-2 py-1 text-left text-xs transition hover:border-foreground/20\"\n                      key={prompt}\n                      onClick={() => setTextInput(prompt)}\n                      type=\"button\"\n                    >\n                      {prompt}\n                    </button>\n                  ))}\n                </div>\n              </div>\n\n              <div className=\"space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n                <div className=\"flex items-center justify-between gap-3\">\n                  <p className=\"text-muted-foreground text-xs uppercase tracking-[0.2em]\">\n                    Realtime event feed\n                  </p>\n                  <Badge variant=\"outline\">\n                    {laneProfile.transportEscapeHatch}\n                  </Badge>\n                </div>\n                <div className=\"flex flex-wrap gap-2\">\n                  {(realtimeEventFeed.length > 0\n                    ? realtimeEventFeed\n                    : laneProfile.emittedSessionEvents\n                  ).map((event) => (\n                    <Badge key={event} variant=\"outline\">\n                      {event}\n                    </Badge>\n                  ))}\n                </div>\n              </div>\n\n              <div className=\"space-y-2\">\n                <p className=\"text-muted-foreground text-xs\">\n                  You can speak after connecting, or send one text turn over the\n                  same realtime session for a quick deterministic smoke test.\n                </p>\n                <div className=\"flex flex-col gap-2 sm:flex-row\">\n                  <input\n                    className=\"min-w-0 flex-1 border border-foreground/10 bg-background px-3 py-2 text-sm outline-none transition focus:border-foreground/20\"\n                    disabled={connectionStatus !== \"connected\"}\n                    onChange={(event) => setTextInput(event.target.value)}\n                    onKeyDown={(event) => {\n                      if (event.key === \"Enter\" && !event.shiftKey) {\n                        event.preventDefault();\n                        sendTextTurn();\n                      }\n                    }}\n                    placeholder=\"Send a short text turn over RealtimeSession after connect.\"\n                    value={textInput}\n                  />\n                  <Button\n                    disabled={\n                      connectionStatus !== \"connected\" ||\n                      textInput.trim().length === 0\n                    }\n                    onClick={sendTextTurn}\n                    size=\"sm\"\n                    type=\"button\"\n                    variant=\"outline\"\n                  >\n                    <WaveformIcon className=\"size-3.5\" />\n                    Send text\n                  </Button>\n                </div>\n              </div>\n\n              {errorMessage ? (\n                <div className=\"border border-destructive/30 bg-destructive/10 px-3 py-3 text-destructive text-xs/relaxed\">\n                  {errorMessage}\n                </div>\n              ) : null}\n\n              <div className=\"flex flex-wrap items-center gap-2 text-xs\">\n                <Badge variant=\"outline\">\n                  <SpeakerHighIcon className=\"size-3.5\" />\n                  WebRTC audio\n                </Badge>\n                <Badge variant=\"outline\">\n                  <WaveformIcon className=\"size-3.5\" />\n                  RealtimeSession.connect({\"{ apiKey }\"})\n                </Badge>\n              </div>\n            </div>\n          </div>\n        </DialogContent>\n      </Dialog>\n\n      <audio autoPlay className=\"hidden\" ref={audioElementRef} />\n    </>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/openai-agents-sdk-demo/openai-agents-sdk-demo-voice-panel.tsx"
    },
    {
      "path": "registry/openai-agents-sdk-demo/components/openai-agents-sdk-demo/openai-agents-sdk-demo-voice-utils.ts",
      "content": "import { type RealtimeItem, utils } from \"@openai/agents/realtime\";\n\nexport interface OpenAiAgentsSdkDemoVoiceHistorySummary {\n  historyItemCount: number;\n  lastAssistantTranscript: string | null;\n  lastUserTranscript: string | null;\n}\n\nfunction getUserMessageText(item: Extract<RealtimeItem, { type: \"message\" }>) {\n  if (item.role !== \"user\") {\n    return null;\n  }\n\n  const value = item.content\n    .map((part) =>\n      part.type === \"input_text\" ? part.text : (part.transcript ?? \"\")\n    )\n    .join(\"\\n\")\n    .trim();\n\n  return value.length > 0 ? value : null;\n}\n\nfunction getAssistantMessageText(\n  item: Extract<RealtimeItem, { type: \"message\" }>\n) {\n  if (item.role !== \"assistant\") {\n    return null;\n  }\n\n  const transcript =\n    utils.getLastTextFromAudioOutputMessage(item) ??\n    item.content\n      .map((part) =>\n        part.type === \"output_text\" ? part.text : (part.transcript ?? \"\")\n      )\n      .join(\"\\n\")\n      .trim();\n\n  return transcript.length > 0 ? transcript : null;\n}\n\nexport function getOpenAiAgentsSdkDemoVoiceHistorySummary(\n  history: RealtimeItem[]\n): OpenAiAgentsSdkDemoVoiceHistorySummary {\n  let lastAssistantTranscript: string | null = null;\n  let lastUserTranscript: string | null = null;\n\n  for (const item of [...history].reverse()) {\n    if (item.type !== \"message\") {\n      continue;\n    }\n\n    if (!lastAssistantTranscript) {\n      lastAssistantTranscript = getAssistantMessageText(item);\n    }\n\n    if (!lastUserTranscript) {\n      lastUserTranscript = getUserMessageText(item);\n    }\n\n    if (lastAssistantTranscript && lastUserTranscript) {\n      break;\n    }\n  }\n\n  return {\n    historyItemCount: history.length,\n    lastAssistantTranscript,\n    lastUserTranscript,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/openai-agents-sdk-demo/openai-agents-sdk-demo-voice-utils.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/components/openai-agents-sdk-demo/openai-agents-sdk-demo-workspace-layout.ts",
      "content": "export type OpenAiAgentsSdkDemoVoiceConnectionStatus =\n  | \"connecting\"\n  | \"connected\"\n  | \"disconnected\"\n  | \"error\";\n\nexport const openAiAgentsSdkDemoWorkspaceLayout = {\n  voiceDetailsButtonLabel: \"Details\",\n  voiceEntrySurface: \"screen-rail\",\n  voiceEntryTitle: \"Voice\",\n  voiceDetailsSurface: \"dialog\",\n  workspaceHeightClassName: \"h-[100svh]\",\n} as const;\n\nexport function getOpenAiAgentsSdkDemoVoicePrimarySummary({\n  connectionStatus,\n  errorMessage,\n  hasPendingApproval,\n}: {\n  connectionStatus: OpenAiAgentsSdkDemoVoiceConnectionStatus;\n  errorMessage: string | null;\n  hasPendingApproval: boolean;\n}) {\n  if (connectionStatus === \"connected\") {\n    return hasPendingApproval ? \"Approval pending.\" : \"Live session.\";\n  }\n\n  if (connectionStatus === \"connecting\") {\n    return \"Connecting.\";\n  }\n\n  if (connectionStatus === \"error\") {\n    return errorMessage ? \"Retry required.\" : \"Voice failed.\";\n  }\n\n  return \"Browser lane.\";\n}\n",
      "type": "registry:component",
      "target": "@components/openai-agents-sdk-demo/openai-agents-sdk-demo-workspace-layout.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/components/openai-agents-sdk-demo/openai-agents-sdk-demo-workspace.tsx",
      "content": "\"use client\";\n\nimport { Chat } from \"@ai-sdk/react\";\nimport {\n  ArrowClockwiseIcon,\n  CaretDownIcon,\n  CheckCircleIcon,\n  RobotIcon,\n  StopIcon,\n  XCircleIcon,\n} from \"@phosphor-icons/react\";\nimport {\n  Attachment,\n  AttachmentInfo,\n  AttachmentPreview,\n  Attachments,\n} from \"@/components/ai-elements/attachments\";\nimport {\n  Confirmation,\n  ConfirmationAccepted,\n  ConfirmationAction,\n  ConfirmationActions,\n  ConfirmationRejected,\n  ConfirmationRequest,\n  ConfirmationTitle,\n} from \"@/components/ai-elements/confirmation\";\nimport {\n  Conversation,\n  ConversationContent,\n  ConversationEmptyState,\n  ConversationScrollButton,\n} from \"@/components/ai-elements/conversation\";\nimport {\n  Message,\n  MessageContent,\n  MessageResponse,\n} from \"@/components/ai-elements/message\";\nimport {\n  PromptInput,\n  PromptInputBody,\n  PromptInputFooter,\n  PromptInputSubmit,\n  PromptInputTextarea,\n} from \"@/components/ai-elements/prompt-input\";\nimport {\n  Reasoning,\n  ReasoningContent,\n  ReasoningTrigger,\n} from \"@/components/ai-elements/reasoning\";\nimport { Shimmer } from \"@/components/ai-elements/shimmer\";\nimport {\n  Source,\n  Sources,\n  SourcesContent,\n  SourcesTrigger,\n} from \"@/components/ai-elements/sources\";\nimport {\n  Tool,\n  ToolContent,\n  ToolHeader,\n  ToolInput,\n  ToolOutput,\n  type ToolPart,\n} from \"@/components/ai-elements/tool\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  type ChatAddToolApproveResponseFunction,\n  DefaultChatTransport,\n  lastAssistantMessageIsCompleteWithApprovalResponses,\n} from \"ai\";\nimport { type ReactNode, useEffect, useState } from \"react\";\n\nimport { useDemoChat } from \"@/components/demo-chat/use-demo-chat\";\nimport type { OpenAiAgentsSdkDemoMessage } from \"@/lib/openai-agents-sdk-demo/message-metadata\";\nimport type { OpenAiAgentsSdkDemoContextProfile } from \"@/lib/openai-agents-sdk-demo/server/context\";\nimport type { OpenAiAgentsSdkDemoAiSdkExtensionProfile } from \"@/lib/openai-agents-sdk-demo/server/extensions\";\nimport type { OpenAiAgentsSdkDemoGuardrailCatalogEntry } from \"@/lib/openai-agents-sdk-demo/server/guardrails\";\nimport type { OpenAiAgentsSdkDemoGuideCoverage } from \"@/lib/openai-agents-sdk-demo/server/guide-coverage\";\nimport type { OpenAiAgentsSdkDemoHandoffCatalogEntry } from \"@/lib/openai-agents-sdk-demo/server/handoffs\";\nimport type {\n  OpenAiAgentsSdkDemoMcpCatalogEntry,\n  OpenAiAgentsSdkDemoMcpProfile,\n} from \"@/lib/openai-agents-sdk-demo/server/mcp\";\nimport type { OpenAiAgentsSdkDemoModelProfile } from \"@/lib/openai-agents-sdk-demo/server/models\";\nimport type { OpenAiAgentsSdkDemoRunProfile } from \"@/lib/openai-agents-sdk-demo/server/running\";\nimport type { OpenAiAgentsSdkDemoSandboxProfile } from \"@/lib/openai-agents-sdk-demo/server/sandbox\";\nimport type { OpenAiAgentsSdkDemoSessionProfile } from \"@/lib/openai-agents-sdk-demo/server/sessions\";\nimport type { OpenAiAgentsSdkDemoToolCatalogEntry } from \"@/lib/openai-agents-sdk-demo/server/tools\";\nimport type { OpenAiAgentsSdkDemoTraceProfile } from \"@/lib/openai-agents-sdk-demo/server/tracing\";\nimport type { OpenAiAgentsSdkDemoVoiceProfile } from \"@/lib/openai-agents-sdk-demo/server/voice\";\nimport { buildOpenAiAgentsSdkDemoRuntimeInspector } from \"./openai-agents-sdk-demo-runtime-inspector\";\nimport {\n  getOpenAiAgentsSdkDemoApprovalInputFields,\n  getOpenAiAgentsSdkDemoFailedTurnRetryText,\n  getOpenAiAgentsSdkDemoFileParts,\n  getOpenAiAgentsSdkDemoMessageText,\n  getOpenAiAgentsSdkDemoRecoverableMessages,\n  getOpenAiAgentsSdkDemoRenderableReasoningText,\n  getOpenAiAgentsSdkDemoSourceParts,\n  getOpenAiAgentsSdkDemoToolDisplayState,\n  getOpenAiAgentsSdkDemoToolName,\n  getOpenAiAgentsSdkDemoToolParts,\n  shouldRenderOpenAiAgentsSdkDemoReasoning,\n} from \"./openai-agents-sdk-demo-session\";\nimport { OpenAiAgentsSdkDemoVoicePanel } from \"./openai-agents-sdk-demo-voice-panel\";\nimport { openAiAgentsSdkDemoWorkspaceLayout } from \"./openai-agents-sdk-demo-workspace-layout\";\n\nconst openAiAgentsSdkDemoSamplePrompts = [\n  \"Explain how this demo bridges the OpenAI Agents SDK run into AI SDK UI messages.\",\n  \"Run a short planning answer that uses the configured guide coverage and model profile.\",\n  \"Summarize what the demo proves about tools, handoffs, and tracing in one response.\",\n] as const;\n\nfunction ThinkingState() {\n  return <Shimmer className=\"text-sm\">Thinking...</Shimmer>;\n}\n\nfunction getApprovalInputPreview(part: ToolPart) {\n  if (typeof part.input === \"string\") {\n    return part.input;\n  }\n\n  try {\n    return JSON.stringify(part.input, null, 2);\n  } catch {\n    return String(part.input);\n  }\n}\n\ninterface OpenAiAgentsSdkDemoChatError {\n  id: string;\n  message: string;\n  retryText: string | null;\n}\n\nfunction OpenAiAgentsSdkDemoToolApproval({\n  onApprovalResponse,\n  part,\n}: {\n  onApprovalResponse: ChatAddToolApproveResponseFunction;\n  part: ToolPart;\n}) {\n  if (!(\"approval\" in part && part.approval)) {\n    return null;\n  }\n\n  const approval = part.approval;\n  const fields = getOpenAiAgentsSdkDemoApprovalInputFields(part);\n  const preview = getApprovalInputPreview(part);\n\n  return (\n    <Confirmation\n      approval={approval}\n      className=\"border-amber-500/30 bg-amber-500/5\"\n      state={part.state}\n    >\n      <ConfirmationTitle>Human approval checkpoint</ConfirmationTitle>\n      <ConfirmationRequest>\n        <div className=\"space-y-2 text-sm\">\n          <p>\n            Approve {getOpenAiAgentsSdkDemoToolName(part)} before the agent\n            continues?\n          </p>\n          {fields.length > 0 ? (\n            <div className=\"grid gap-2\">\n              {fields.map((field) => (\n                <div\n                  className=\"grid gap-1 rounded border border-foreground/10 px-3 py-2 sm:grid-cols-[7rem_minmax(0,1fr)]\"\n                  key={field.label}\n                >\n                  <span className=\"text-muted-foreground text-xs uppercase tracking-[0.16em]\">\n                    {field.label}\n                  </span>\n                  <span className=\"min-w-0 whitespace-pre-wrap break-words\">\n                    {field.value}\n                  </span>\n                </div>\n              ))}\n            </div>\n          ) : (\n            <pre className=\"overflow-x-auto whitespace-pre-wrap break-words text-muted-foreground text-xs\">\n              {preview}\n            </pre>\n          )}\n        </div>\n      </ConfirmationRequest>\n      <ConfirmationAccepted>\n        <CheckCircleIcon className=\"size-4\" />\n        <span>Approved. The agent can continue this run.</span>\n      </ConfirmationAccepted>\n      <ConfirmationRejected>\n        <XCircleIcon className=\"size-4\" />\n        <span>Rejected. The agent will continue with the denial result.</span>\n      </ConfirmationRejected>\n      <ConfirmationActions>\n        <ConfirmationAction\n          onClick={() =>\n            onApprovalResponse({\n              approved: false,\n              id: approval.id,\n              reason: \"Reviewer rejected the approval request.\",\n            })\n          }\n          variant=\"outline\"\n        >\n          <XCircleIcon className=\"size-3.5\" />\n          Reject\n        </ConfirmationAction>\n        <ConfirmationAction\n          onClick={() =>\n            onApprovalResponse({\n              approved: true,\n              id: approval.id,\n              reason: \"Reviewer approved the request.\",\n            })\n          }\n        >\n          <CheckCircleIcon className=\"size-3.5\" />\n          Approve\n        </ConfirmationAction>\n      </ConfirmationActions>\n    </Confirmation>\n  );\n}\n\nfunction OpenAiAgentsSdkDemoToolTrace({\n  isMessageStreaming,\n  part,\n}: {\n  isMessageStreaming: boolean;\n  part: ToolPart;\n}) {\n  const toolName = getOpenAiAgentsSdkDemoToolName(part);\n  const displayState = getOpenAiAgentsSdkDemoToolDisplayState(part, {\n    isMessageStreaming,\n  });\n  const hasRenderableOutput = Boolean(part.output || part.errorText);\n  const isSettledInputOnly =\n    displayState === \"output-available\" &&\n    (part.state === \"input-available\" || part.state === \"input-streaming\") &&\n    !hasRenderableOutput;\n\n  return (\n    <Tool>\n      {part.type === \"dynamic-tool\" ? (\n        <ToolHeader\n          state={displayState}\n          title={toolName}\n          toolName={toolName}\n          type={part.type}\n        />\n      ) : (\n        <ToolHeader state={displayState} title={toolName} type={part.type} />\n      )}\n      <ToolContent>\n        {part.input ? <ToolInput input={part.input} /> : null}\n        {isSettledInputOnly ? (\n          <p className=\"text-muted-foreground text-sm\">\n            Hosted tool completed without a renderable output payload.\n          </p>\n        ) : null}\n        <ToolOutput errorText={part.errorText} output={part.output} />\n      </ToolContent>\n    </Tool>\n  );\n}\n\nfunction OpenAiAgentsSdkDemoSources({\n  sources,\n}: {\n  sources: ReturnType<typeof getOpenAiAgentsSdkDemoSourceParts>;\n}) {\n  if (sources.length === 0) {\n    return null;\n  }\n\n  return (\n    <Sources>\n      <SourcesTrigger count={sources.length} />\n      <SourcesContent>\n        {sources.map((source) => (\n          <Source\n            href={source.url}\n            key={source.sourceId}\n            title={source.title}\n          />\n        ))}\n      </SourcesContent>\n    </Sources>\n  );\n}\n\nfunction OpenAiAgentsSdkDemoErrorMessage({\n  error,\n  onRetry,\n}: {\n  error: OpenAiAgentsSdkDemoChatError;\n  onRetry: (text: string) => void;\n}) {\n  return (\n    <Message from=\"assistant\" key={error.id}>\n      <MessageContent className=\"max-w-3xl\">\n        <div\n          className=\"space-y-3 border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm\"\n          role=\"alert\"\n        >\n          <div className=\"flex items-start gap-2 text-destructive\">\n            <XCircleIcon className=\"mt-0.5 size-4 shrink-0\" />\n            <div className=\"min-w-0 space-y-1\">\n              <p className=\"font-medium\">Agent turn failed</p>\n              <p className=\"whitespace-pre-wrap break-words text-destructive/90\">\n                {error.message}\n              </p>\n            </div>\n          </div>\n          {error.retryText ? (\n            <Button\n              onClick={() => onRetry(error.retryText ?? \"\")}\n              size=\"sm\"\n              type=\"button\"\n              variant=\"outline\"\n            >\n              <ArrowClockwiseIcon className=\"size-3.5\" />\n              Retry\n            </Button>\n          ) : null}\n        </div>\n      </MessageContent>\n    </Message>\n  );\n}\n\nfunction getImplementationLabel(\n  status: OpenAiAgentsSdkDemoGuideCoverage[\"implementationStatus\"]\n) {\n  if (status === \"implemented\") {\n    return \"Implemented\";\n  }\n\n  if (status === \"blocked\") {\n    return \"Blocked\";\n  }\n\n  return \"Not started\";\n}\n\nfunction getRunStatusLabel(\n  status: OpenAiAgentsSdkDemoGuideCoverage[\"currentRunStatus\"]\n) {\n  if (status === \"ready\") {\n    return \"Ready\";\n  }\n\n  if (status === \"used-this-run\") {\n    return \"Used this run\";\n  }\n\n  if (status === \"blocked\") {\n    return \"Blocked\";\n  }\n\n  return \"Not started\";\n}\n\nfunction getStatusClassName(\n  status:\n    | OpenAiAgentsSdkDemoGuideCoverage[\"currentRunStatus\"]\n    | OpenAiAgentsSdkDemoGuideCoverage[\"implementationStatus\"]\n) {\n  if (\n    status === \"implemented\" ||\n    status === \"ready\" ||\n    status === \"used-this-run\"\n  ) {\n    return \"border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300\";\n  }\n\n  if (status === \"blocked\") {\n    return \"border-destructive/30 bg-destructive/10 text-destructive\";\n  }\n\n  return \"border-foreground/10 bg-muted/40 text-muted-foreground\";\n}\n\nexport interface OpenAiAgentsSdkDemoWorkspaceProps {\n  aiSdkExtensionProfile: OpenAiAgentsSdkDemoAiSdkExtensionProfile;\n  chatModel: string;\n  contextProfile: OpenAiAgentsSdkDemoContextProfile;\n  guardrailCatalog: OpenAiAgentsSdkDemoGuardrailCatalogEntry[];\n  guideCoverage: OpenAiAgentsSdkDemoGuideCoverage[];\n  handoffCatalog: OpenAiAgentsSdkDemoHandoffCatalogEntry[];\n  isChatAvailable: boolean;\n  mcpCatalog: OpenAiAgentsSdkDemoMcpCatalogEntry[];\n  mcpProfile: OpenAiAgentsSdkDemoMcpProfile;\n  modelProfile: OpenAiAgentsSdkDemoModelProfile;\n  nodeVersion: string;\n  runProfile: OpenAiAgentsSdkDemoRunProfile;\n  sandboxProfile: OpenAiAgentsSdkDemoSandboxProfile;\n  sessionProfile: OpenAiAgentsSdkDemoSessionProfile;\n  setupMessage: string | null;\n  toolCatalog: OpenAiAgentsSdkDemoToolCatalogEntry[];\n  traceProfile: OpenAiAgentsSdkDemoTraceProfile;\n  voiceProfile: OpenAiAgentsSdkDemoVoiceProfile;\n}\n\nfunction getToolAvailabilityClassName(\n  availability: OpenAiAgentsSdkDemoToolCatalogEntry[\"availability\"]\n) {\n  if (availability === \"configured\") {\n    return \"border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300\";\n  }\n\n  if (availability === \"provider-blocked\") {\n    return \"border-destructive/30 bg-destructive/10 text-destructive\";\n  }\n\n  return \"border-foreground/10 bg-muted/40 text-muted-foreground\";\n}\n\nfunction InspectorBadge({\n  children,\n  className,\n}: {\n  children: ReactNode;\n  className?: string;\n}) {\n  return (\n    <Badge\n      className={cn(\n        \"h-auto min-w-0 max-w-full shrink items-start overflow-hidden whitespace-normal break-all px-2 py-1 text-left text-[11px] leading-tight\",\n        className\n      )}\n      variant=\"outline\"\n    >\n      {children}\n    </Badge>\n  );\n}\n\nfunction InspectorRow({ label, value }: { label: string; value: ReactNode }) {\n  return (\n    <div className=\"flex items-start justify-between gap-3\">\n      <span className=\"text-muted-foreground\">{label}</span>\n      <span className=\"min-w-0 max-w-[9rem] break-all text-right text-foreground text-xs leading-5\">\n        {value}\n      </span>\n    </div>\n  );\n}\n\nfunction InspectorCollapsible({\n  children,\n  defaultOpen = false,\n  summary,\n  title,\n}: {\n  children: ReactNode;\n  defaultOpen?: boolean;\n  summary: string;\n  title: string;\n}) {\n  return (\n    <Collapsible\n      className=\"border border-foreground/10\"\n      defaultOpen={defaultOpen}\n    >\n      <CollapsibleTrigger className=\"flex w-full items-center justify-between gap-3 px-3 py-2 text-left\">\n        <div className=\"min-w-0 space-y-0.5\">\n          <p className=\"text-sm\">{title}</p>\n          <p className=\"text-muted-foreground text-xs leading-5\">{summary}</p>\n        </div>\n        <CaretDownIcon className=\"size-4 shrink-0 text-muted-foreground\" />\n      </CollapsibleTrigger>\n      <CollapsibleContent className=\"space-y-3 border-foreground/10 border-t px-3 py-3 text-sm\">\n        {children}\n      </CollapsibleContent>\n    </Collapsible>\n  );\n}\n\nexport function OpenAiAgentsSdkDemoWorkspace({\n  aiSdkExtensionProfile,\n  chatModel,\n  contextProfile,\n  guardrailCatalog,\n  guideCoverage,\n  handoffCatalog,\n  isChatAvailable,\n  mcpCatalog,\n  mcpProfile,\n  modelProfile,\n  nodeVersion,\n  runProfile,\n  sandboxProfile,\n  sessionProfile,\n  setupMessage,\n  traceProfile,\n  toolCatalog,\n  voiceProfile,\n}: OpenAiAgentsSdkDemoWorkspaceProps) {\n  const {\n    addToolApprovalResponse,\n    clearError,\n    error,\n    hasMessages,\n    isBusy,\n    messages,\n    regenerate,\n    sendMessage,\n    setMessages,\n    status,\n    stop,\n  } = useDemoChat<OpenAiAgentsSdkDemoMessage>({\n    createChat: () =>\n      new Chat({\n        sendAutomaticallyWhen:\n          lastAssistantMessageIsCompleteWithApprovalResponses,\n        transport: new DefaultChatTransport({\n          api: \"/api/demos/openai-agents-sdk-demo\",\n        }),\n      }),\n  });\n  const [hasUsedVoiceGuide, setHasUsedVoiceGuide] = useState(false);\n  const [chatErrorMessage, setChatErrorMessage] =\n    useState<OpenAiAgentsSdkDemoChatError | null>(null);\n  const runtimeInspector = buildOpenAiAgentsSdkDemoRuntimeInspector({\n    aiSdkExtensionProfile,\n    guideCoverage,\n    hasUsedVoiceGuide,\n    messages,\n    traceProfile,\n  });\n  const {\n    aiSdkModelAdapterStatus,\n    aiSdkUiBridgeStatus,\n    guideCoverageWithCurrentRun,\n    hasPendingApproval,\n    lastApprovalSummary,\n    lastContextSummary,\n    lastHandoffSummary,\n    lastMcpSummary,\n    lastResponseId,\n    lastResultSummary,\n    lastSandboxSummary,\n    lastSessionSummary,\n    lastStreamSummary,\n    lastTraceSummary,\n    traceIncludesSensitiveData,\n    usedGuardrailNames,\n    usedToolNames,\n  } = runtimeInspector;\n\n  useEffect(() => {\n    if (!error) {\n      return;\n    }\n\n    setChatErrorMessage({\n      id: `openai-agents-sdk-demo-error-${Date.now()}`,\n      message: error.message,\n      retryText: getOpenAiAgentsSdkDemoFailedTurnRetryText(messages),\n    });\n    setMessages(getOpenAiAgentsSdkDemoRecoverableMessages(messages));\n    clearError();\n  }, [clearError, error, messages, setMessages]);\n\n  function handleSendMessage(text: string) {\n    const trimmedText = text.trim();\n\n    if (trimmedText.length === 0) {\n      return;\n    }\n\n    clearError();\n    setChatErrorMessage(null);\n    sendMessage({ text: trimmedText });\n  }\n\n  function handleRetryFailedTurn(text: string) {\n    const trimmedText = text.trim();\n\n    if (trimmedText.length === 0) {\n      return;\n    }\n\n    clearError();\n    setChatErrorMessage(null);\n    sendMessage({ text: trimmedText });\n  }\n\n  return (\n    <div\n      className={cn(\n        \"grid grid-rows-[minmax(0,1fr)_minmax(0,26rem)] gap-4 overflow-hidden md:grid-cols-[minmax(0,1fr)_20rem] md:grid-rows-1\",\n        openAiAgentsSdkDemoWorkspaceLayout.workspaceHeightClassName\n      )}\n    >\n      <section className=\"flex min-h-0 flex-col overflow-hidden border border-foreground/10 bg-background\">\n        {isChatAvailable ? null : (\n          <div className=\"border-foreground/10 border-b px-4 py-3 text-muted-foreground text-xs/relaxed\">\n            {setupMessage}\n          </div>\n        )}\n\n        <Conversation className=\"min-h-0\">\n          <ConversationContent className=\"mx-auto flex w-full max-w-3xl flex-1 gap-6 px-4 py-6\">\n            {hasMessages ? (\n              messages.map((message, index) => {\n                const text = getOpenAiAgentsSdkDemoMessageText(message).trim();\n                const nextMessage = messages[index + 1];\n                const reasoningText = shouldRenderOpenAiAgentsSdkDemoReasoning(\n                  message,\n                  nextMessage\n                )\n                  ? getOpenAiAgentsSdkDemoRenderableReasoningText(\n                      message\n                    ).trim()\n                  : \"\";\n                const sourceParts = getOpenAiAgentsSdkDemoSourceParts(message);\n                const fileParts = getOpenAiAgentsSdkDemoFileParts(message);\n                const toolParts = getOpenAiAgentsSdkDemoToolParts(message);\n                const isLastMessage = index === messages.length - 1;\n                const isMessageStreaming =\n                  message.role === \"assistant\" && isBusy && isLastMessage;\n                const lastPart = message.parts.at(-1);\n                const isReasoningStreaming =\n                  isMessageStreaming && lastPart?.type === \"reasoning\";\n                const hasVisibleContent =\n                  text.length > 0 ||\n                  reasoningText.length > 0 ||\n                  sourceParts.length > 0 ||\n                  fileParts.length > 0 ||\n                  toolParts.length > 0;\n                const showThinking =\n                  message.role === \"assistant\" &&\n                  isMessageStreaming &&\n                  !hasVisibleContent;\n\n                return (\n                  <Message from={message.role} key={message.id}>\n                    <MessageContent\n                      className={cn(\n                        \"space-y-4\",\n                        message.role === \"assistant\" ? \"max-w-3xl\" : \"max-w-2xl\"\n                      )}\n                    >\n                      {reasoningText ? (\n                        <Reasoning\n                          className=\"w-full\"\n                          isStreaming={isReasoningStreaming}\n                        >\n                          <ReasoningTrigger />\n                          <ReasoningContent>{reasoningText}</ReasoningContent>\n                        </Reasoning>\n                      ) : null}\n\n                      {toolParts.map((part) =>\n                        part.state === \"approval-requested\" ||\n                        part.state === \"approval-responded\" ? (\n                          <OpenAiAgentsSdkDemoToolApproval\n                            key={part.toolCallId}\n                            onApprovalResponse={addToolApprovalResponse}\n                            part={part}\n                          />\n                        ) : (\n                          <OpenAiAgentsSdkDemoToolTrace\n                            isMessageStreaming={isMessageStreaming}\n                            key={part.toolCallId}\n                            part={part}\n                          />\n                        )\n                      )}\n\n                      {text ? <MessageResponse>{text}</MessageResponse> : null}\n\n                      <OpenAiAgentsSdkDemoSources sources={sourceParts} />\n\n                      {fileParts.length > 0 ? (\n                        <Attachments variant=\"list\">\n                          {fileParts.map((part) => {\n                            const attachmentId = `${message.id}-${part.filename ?? \"attachment\"}-${part.url}`;\n\n                            return (\n                              <Attachment\n                                data={{\n                                  ...part,\n                                  id: attachmentId,\n                                }}\n                                key={attachmentId}\n                              >\n                                <AttachmentPreview />\n                                <AttachmentInfo showMediaType />\n                              </Attachment>\n                            );\n                          })}\n                        </Attachments>\n                      ) : null}\n\n                      {showThinking ? <ThinkingState /> : null}\n                      {hasVisibleContent ||\n                      (message.role === \"assistant\" &&\n                        isMessageStreaming) ? null : (\n                        <p className=\"text-muted-foreground text-sm/relaxed\">\n                          No visible assistant output was returned for this\n                          turn.\n                        </p>\n                      )}\n                    </MessageContent>\n                  </Message>\n                );\n              })\n            ) : (\n              <ConversationEmptyState\n                description=\"Send a prompt and exercise the official OpenAI Agents SDK to AI SDK UI bridge without leaving the current frontend stack.\"\n                icon={<RobotIcon className=\"size-5\" />}\n                title=\"OpenAI Agents workspace is ready\"\n              />\n            )}\n            {chatErrorMessage ? (\n              <OpenAiAgentsSdkDemoErrorMessage\n                error={chatErrorMessage}\n                onRetry={handleRetryFailedTurn}\n              />\n            ) : null}\n          </ConversationContent>\n          <ConversationScrollButton />\n        </Conversation>\n\n        <div className=\"border-foreground/10 border-t px-4 py-4\">\n          <div className=\"mx-auto w-full max-w-3xl\">\n            <PromptInput onSubmit={({ text }) => handleSendMessage(text)}>\n              <PromptInputBody>\n                <PromptInputTextarea\n                  disabled={!isChatAvailable || isBusy || hasPendingApproval}\n                  placeholder={\n                    hasPendingApproval\n                      ? \"Approve or reject the pending tool request to continue this run.\"\n                      : \"Ask for a plan, a short explanation, or a demo-specific agent answer.\"\n                  }\n                />\n              </PromptInputBody>\n              <PromptInputFooter className=\"flex items-center justify-between gap-3 border-foreground/10 border-t px-3 py-3\">\n                <div className=\"flex flex-wrap items-center gap-2\">\n                  <Badge variant=\"outline\">OpenAI Agents SDK</Badge>\n                  <Badge variant=\"outline\">AI SDK UI bridge</Badge>\n                  <Badge variant=\"outline\">{chatModel}</Badge>\n                </div>\n                <div className=\"flex items-center gap-2\">\n                  {isBusy ? (\n                    <Button\n                      onClick={stop}\n                      size=\"sm\"\n                      type=\"button\"\n                      variant=\"outline\"\n                    >\n                      <StopIcon className=\"size-3.5\" />\n                      Stop\n                    </Button>\n                  ) : null}\n                  {hasMessages ? (\n                    <Button\n                      onClick={() => regenerate()}\n                      size=\"sm\"\n                      type=\"button\"\n                      variant=\"outline\"\n                    >\n                      <ArrowClockwiseIcon className=\"size-3.5\" />\n                      Retry\n                    </Button>\n                  ) : null}\n                  <PromptInputSubmit\n                    disabled={!isChatAvailable || hasPendingApproval}\n                    status={status}\n                  />\n                </div>\n              </PromptInputFooter>\n            </PromptInput>\n\n            {hasMessages ? null : (\n              <div className=\"mt-3 flex flex-wrap gap-2\">\n                {openAiAgentsSdkDemoSamplePrompts.map((prompt) => (\n                  <Button\n                    disabled={!isChatAvailable || hasPendingApproval || isBusy}\n                    key={prompt}\n                    onClick={() => handleSendMessage(prompt)}\n                    size=\"sm\"\n                    type=\"button\"\n                    variant=\"outline\"\n                  >\n                    <RobotIcon className=\"size-3.5\" />\n                    {prompt}\n                  </Button>\n                ))}\n              </div>\n            )}\n          </div>\n        </div>\n      </section>\n\n      <aside className=\"grid min-h-0 min-w-0 grid-rows-[auto_minmax(0,1fr)] overflow-hidden border border-foreground/10 bg-background\">\n        <OpenAiAgentsSdkDemoVoicePanel\n          onUsageChange={setHasUsedVoiceGuide}\n          voiceProfile={voiceProfile}\n        />\n\n        <div className=\"min-w-0 space-y-5 overflow-y-auto overflow-x-hidden p-4\">\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Runtime\n            </p>\n            <p className=\"mt-1 font-medium text-sm\">{nodeVersion}</p>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Contract\n            </p>\n            <p className=\"mt-1 text-sm\">\n              This slot keeps the agent backend on the official OpenAI run path\n              and lets the existing AI SDK UI frontend consume the result\n              without a custom stream protocol.\n            </p>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Running\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Workflow</span>\n                <span>{runProfile.workflowName}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Continuation</span>\n                <span>{runProfile.continuationStrategy}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Max turns</span>\n                <span>{runProfile.maxTurns}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Abort</span>\n                <span>\n                  {runProfile.usesRequestSignal ? \"request.signal\" : \"none\"}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Last response</span>\n                <span className=\"max-w-[10rem] truncate text-right\">\n                  {lastResponseId ?? \"Not available yet\"}\n                </span>\n              </div>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Context\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Primitive</span>\n                <span>{contextProfile.localContextPrimitive}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Session binding</span>\n                <span>{contextProfile.sessionBinding}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Run mode</span>\n                <span>{lastContextSummary?.researchMode ?? \"No run yet\"}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Default target</span>\n                <span>{contextProfile.suggestedDefaultTarget}</span>\n              </div>\n              <div className=\"space-y-2 pt-1\">\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">Channels</p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {contextProfile.passesContextInto.map((value) => (\n                      <Badge key={value} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Latest user prompt\n                  </p>\n                  <p className=\"mt-1 text-xs/relaxed\">\n                    {lastContextSummary?.latestUserPromptPreview ||\n                      \"No context metadata yet\"}\n                  </p>\n                </div>\n              </div>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Sessions\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Primitive</span>\n                <span>{sessionProfile.sdkPrimitive}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Storage</span>\n                <span>{sessionProfile.historyStorage}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Transport</span>\n                <span className=\"max-w-[10rem] text-right\">\n                  {sessionProfile.sessionTransport}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Session id</span>\n                <span className=\"max-w-[10rem] truncate text-right\">\n                  {lastSessionSummary?.sessionId ?? \"Not created yet\"}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">History items</span>\n                <span>{lastSessionSummary?.historyItemCount ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">CRUD helpers</span>\n                <span>\n                  {sessionProfile.supportsCrudHelpers\n                    ? \"get/add/pop/clear\"\n                    : \"none\"}\n                </span>\n              </div>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Streaming\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <InspectorRow\n                label=\"Bridge\"\n                value=\"createAiSdkUiMessageStream()\"\n              />\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Raw model events</span>\n                <span>{lastStreamSummary?.rawModelEventCount ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Run item events</span>\n                <span>{lastStreamSummary?.runItemEventCount ?? 0}</span>\n              </div>\n              <div className=\"space-y-2 pt-1\">\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">Agents</p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastStreamSummary?.agentNames.length\n                      ? lastStreamSummary.agentNames\n                      : [\"No stream metadata yet\"]\n                    ).map((value) => (\n                      <Badge key={value} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Raw event types\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastStreamSummary?.rawModelEventTypes.length\n                      ? lastStreamSummary.rawModelEventTypes\n                      : [\"No stream metadata yet\"]\n                    ).map((value) => (\n                      <Badge key={value} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Run item names\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastStreamSummary?.runItemEventNames.length\n                      ? lastStreamSummary.runItemEventNames\n                      : [\"No stream metadata yet\"]\n                    ).map((value) => (\n                      <Badge key={value} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">Sources</p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastStreamSummary?.rawModelSources.length\n                      ? lastStreamSummary.rawModelSources\n                      : [\"No stream metadata yet\"]\n                    ).map((value) => (\n                      <Badge key={value} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              AI SDK Extension\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <InspectorRow\n                label=\"UI bridge\"\n                value={aiSdkExtensionProfile.uiBridge.sdkPrimitive}\n              />\n              <InspectorRow\n                label=\"Response helper\"\n                value={aiSdkExtensionProfile.uiBridge.responseHelper}\n              />\n              <InspectorRow\n                label=\"Bridge status\"\n                value={<InspectorBadge>{aiSdkUiBridgeStatus}</InspectorBadge>}\n              />\n              <InspectorRow\n                label=\"Model adapter\"\n                value={aiSdkExtensionProfile.modelAdapter.sdkPrimitive}\n              />\n              <InspectorRow\n                label=\"Adapter status\"\n                value={\n                  <InspectorBadge>{aiSdkModelAdapterStatus}</InspectorBadge>\n                }\n              />\n              <p className=\"pt-1 text-muted-foreground text-xs/relaxed\">\n                {aiSdkExtensionProfile.modelAdapter.notes}\n              </p>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Voice Agents\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <div className=\"flex flex-wrap gap-1.5\">\n                <InspectorBadge>{voiceProfile.agentPrimitive}</InspectorBadge>\n                <InspectorBadge>{voiceProfile.sessionPrimitive}</InspectorBadge>\n                <InspectorBadge>\n                  {voiceProfile.browserTransport.transport}\n                </InspectorBadge>\n                <InspectorBadge className=\"capitalize\">\n                  {voiceProfile.browserTransport.status}\n                </InspectorBadge>\n              </div>\n\n              <div className=\"space-y-2\">\n                <InspectorRow\n                  label=\"Browser model\"\n                  value={voiceProfile.browserTransport.sessionModel}\n                />\n                <InspectorRow\n                  label=\"Voice\"\n                  value={voiceProfile.browserTransport.sessionVoice}\n                />\n                <InspectorRow\n                  label=\"Tools\"\n                  value={`${voiceProfile.lane.toolNames.length} total / ${voiceProfile.lane.approvalToolNames.length} gated`}\n                />\n                <InspectorRow\n                  label=\"Handoffs\"\n                  value={String(voiceProfile.lane.handoffAgentNames.length)}\n                />\n                <InspectorRow\n                  label=\"Provider lanes\"\n                  value={String(voiceProfile.providerExtensions.length)}\n                />\n                <InspectorRow\n                  label=\"Workspace\"\n                  value={\n                    voiceProfile.supportedInsideCurrentWorkspace\n                      ? \"supported\"\n                      : \"blocked\"\n                  }\n                />\n              </div>\n\n              <InspectorCollapsible\n                defaultOpen={false}\n                summary=\"Route, key contract, events, and browser-only hooks.\"\n                title=\"Browser lane\"\n              >\n                <InspectorRow\n                  label=\"Route\"\n                  value={voiceProfile.browserTransport.routePath}\n                />\n                <InspectorRow\n                  label=\"Credential\"\n                  value={voiceProfile.browserTransport.credentialContract}\n                />\n                <InspectorRow\n                  label=\"Workspace support\"\n                  value={\n                    voiceProfile.supportedInsideCurrentWorkspace\n                      ? \"supported\"\n                      : \"blocked\"\n                  }\n                />\n                <InspectorRow\n                  label=\"Chat route\"\n                  value={\n                    voiceProfile.supportedInsideCurrentChatRoute\n                      ? \"supported\"\n                      : \"blocked\"\n                  }\n                />\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Session events\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {voiceProfile.lane.emittedSessionEvents.map((value) => (\n                      <InspectorBadge key={value}>{value}</InspectorBadge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Transport hook\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    <InspectorBadge>\n                      {voiceProfile.lane.transportEscapeHatch}\n                    </InspectorBadge>\n                  </div>\n                </div>\n              </InspectorCollapsible>\n\n              <InspectorCollapsible\n                defaultOpen={false}\n                summary=\"Server WebSocket, raw audio loop, and SIP runtime contracts.\"\n                title=\"Server lanes\"\n              >\n                <InspectorRow\n                  label=\"Server transport\"\n                  value={voiceProfile.serverTransport.transport}\n                />\n                <InspectorRow\n                  label=\"Server model\"\n                  value={voiceProfile.serverTransport.model}\n                />\n                <InspectorRow\n                  label=\"Server voice\"\n                  value={voiceProfile.serverTransport.sessionVoice}\n                />\n                <InspectorRow\n                  label=\"Server key\"\n                  value={voiceProfile.serverTransport.openAiApiKeyEnvVar}\n                />\n                <InspectorRow\n                  label=\"Server status\"\n                  value={\n                    <InspectorBadge>\n                      {voiceProfile.serverTransport.status}\n                    </InspectorBadge>\n                  }\n                />\n                <InspectorRow\n                  label=\"Audio input\"\n                  value={voiceProfile.serverAudioLane.inputPrimitive}\n                />\n                <InspectorRow\n                  label=\"Audio response\"\n                  value={voiceProfile.serverAudioLane.requestResponsePrimitive}\n                />\n                <InspectorRow\n                  label=\"Audio status\"\n                  value={\n                    <InspectorBadge>\n                      {voiceProfile.serverAudioLane.status}\n                    </InspectorBadge>\n                  }\n                />\n                <InspectorRow\n                  label=\"SIP transport\"\n                  value={voiceProfile.sipTransport.transport}\n                />\n                <InspectorRow\n                  label=\"SIP route\"\n                  value={voiceProfile.sipTransport.routePath}\n                />\n                <InspectorRow\n                  label=\"SIP key\"\n                  value={voiceProfile.sipTransport.openAiApiKeyEnvVar}\n                />\n                <InspectorRow\n                  label=\"SIP status\"\n                  value={\n                    <InspectorBadge>\n                      {voiceProfile.sipTransport.status}\n                    </InspectorBadge>\n                  }\n                />\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Server primitives\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {voiceProfile.serverTransport.sdkPrimitives.map((value) => (\n                      <InspectorBadge key={value}>{value}</InspectorBadge>\n                    ))}\n                    <InspectorBadge>\n                      {voiceProfile.serverAudioLane.interruptPrimitive}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.serverAudioLane.outputAudioEvent}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.serverAudioLane.outputTranscriptEvent}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.sipTransport.connectPrimitive}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.sipTransport.initialConfigPrimitive}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.sipTransport.callControlContract}\n                    </InspectorBadge>\n                  </div>\n                </div>\n              </InspectorCollapsible>\n\n              <InspectorCollapsible\n                defaultOpen={false}\n                summary=\"Twilio and Cloudflare wrappers, routes, and hosting contracts.\"\n                title=\"Provider bridges\"\n              >\n                <InspectorRow\n                  label=\"Twilio route\"\n                  value={voiceProfile.twilioCallControl.routePath}\n                />\n                <InspectorRow\n                  label=\"Twilio stream env\"\n                  value={voiceProfile.twilioCallControl.mediaStreamUrlEnvVar}\n                />\n                <InspectorRow\n                  label=\"Twilio status\"\n                  value={\n                    <InspectorBadge>\n                      {voiceProfile.twilioCallControl.status}\n                    </InspectorBadge>\n                  }\n                />\n                <InspectorRow\n                  label=\"Twilio bridge\"\n                  value={\n                    <InspectorBadge>\n                      {voiceProfile.twilioMediaStreamBridge.status}\n                    </InspectorBadge>\n                  }\n                />\n                <InspectorRow\n                  label=\"Twilio app\"\n                  value={\n                    <InspectorBadge>\n                      {voiceProfile.twilioMediaStreamServer.status}\n                    </InspectorBadge>\n                  }\n                />\n                <InspectorRow\n                  label=\"Cloudflare route\"\n                  value={voiceProfile.cloudflareWorkerApp.connectRoutePath}\n                />\n                <InspectorRow\n                  label=\"Cloudflare app\"\n                  value={\n                    <InspectorBadge>\n                      {voiceProfile.cloudflareWorkerApp.status}\n                    </InspectorBadge>\n                  }\n                />\n                <InspectorRow\n                  label=\"Cloudflare module\"\n                  value={\n                    <InspectorBadge>\n                      {voiceProfile.cloudflareWorkerModule.status}\n                    </InspectorBadge>\n                  }\n                />\n                <InspectorRow\n                  label=\"Cloudflare worker\"\n                  value={\n                    <InspectorBadge>\n                      {voiceProfile.cloudflareWorkerRuntime.status}\n                    </InspectorBadge>\n                  }\n                />\n                <div className=\"space-y-2\">\n                  {voiceProfile.providerExtensions.map((extension) => (\n                    <div\n                      className=\"space-y-2 border border-foreground/10 px-2 py-2\"\n                      key={extension.id}\n                    >\n                      <div className=\"flex flex-wrap items-center justify-between gap-2\">\n                        <span className=\"min-w-0 text-sm\">\n                          {extension.label}\n                        </span>\n                        <InspectorBadge>{extension.status}</InspectorBadge>\n                      </div>\n                      <div className=\"flex flex-wrap gap-1.5\">\n                        <InspectorBadge>\n                          {extension.sdkPrimitive}\n                        </InspectorBadge>\n                        <InspectorBadge>\n                          {extension.runtimeContract}\n                        </InspectorBadge>\n                        <InspectorBadge>\n                          {extension.workflowName}\n                        </InspectorBadge>\n                      </div>\n                    </div>\n                  ))}\n                </div>\n              </InspectorCollapsible>\n\n              <InspectorCollapsible\n                defaultOpen={false}\n                summary=\"Long-form transport notes and deployment contracts.\"\n                title=\"Contracts\"\n              >\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">Twilio</p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    <InspectorBadge>\n                      {voiceProfile.twilioCallControl.transportContract}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.twilioCallControl.sdkPrimitive}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {\n                        voiceProfile.twilioCallControl\n                          .requiredMediaStreamProtocol\n                      }\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.twilioMediaStreamBridge.sdkPrimitive}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.twilioMediaStreamBridge.hostingContract}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.twilioMediaStreamBridge.closeBehavior}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.twilioMediaStreamServer.serverPrimitive}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {\n                        voiceProfile.twilioMediaStreamServer\n                          .publicTransportContract\n                      }\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.twilioMediaStreamServer.websocketProtocol}\n                    </InspectorBadge>\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">Cloudflare</p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    <InspectorBadge>\n                      {voiceProfile.cloudflareWorkerModule.modulePrimitive}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.cloudflareWorkerModule.runtimeContract}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.cloudflareWorkerApp.serverPrimitive}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.cloudflareWorkerApp.publicTransportContract}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {\n                        voiceProfile.cloudflareWorkerApp\n                          .websocketUpgradePrimitive\n                      }\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.cloudflareWorkerRuntime.sdkPrimitive}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {\n                        voiceProfile.cloudflareWorkerRuntime\n                          .websocketUpgradePrimitive\n                      }\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.cloudflareWorkerRuntime.openEventBehavior}\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {\n                        voiceProfile.cloudflareWorkerRuntime\n                          .workerCompatibilityFlag\n                      }\n                    </InspectorBadge>\n                    <InspectorBadge>\n                      {voiceProfile.cloudflareWorkerRuntime.runtimeEntryPoint}\n                    </InspectorBadge>\n                  </div>\n                </div>\n                <p className=\"text-muted-foreground text-xs/relaxed\">\n                  {voiceProfile.notes}\n                </p>\n              </InspectorCollapsible>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Handoffs\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Configured</span>\n                <span>{handoffCatalog.length}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Active agent</span>\n                <span className=\"max-w-[10rem] text-right\">\n                  {lastHandoffSummary?.activeAgentName ?? \"No handoff yet\"}\n                </span>\n              </div>\n              <div className=\"space-y-2 pt-1\">\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Configured handoffs\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {handoffCatalog.map((item) => (\n                      <Badge key={item.name} variant=\"outline\">\n                        {item.name}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">Targets</p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastHandoffSummary?.handoffTargetNames.length\n                      ? lastHandoffSummary.handoffTargetNames\n                      : [\"No handoff metadata yet\"]\n                    ).map((value) => (\n                      <Badge key={value} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">Transitions</p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastHandoffSummary?.handoffTransitions.length\n                      ? lastHandoffSummary.handoffTransitions\n                      : [\"No handoff metadata yet\"]\n                    ).map((value) => (\n                      <Badge key={value} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              MCP\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Transport</span>\n                <span>{mcpProfile.transport}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Lifecycle</span>\n                <span>{mcpProfile.lifecycle}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Route</span>\n                <span className=\"max-w-[10rem] text-right\">\n                  {mcpProfile.routePath}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Strict schemas</span>\n                <span>\n                  {mcpProfile.convertSchemasToStrict ? \"enabled\" : \"disabled\"}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Active servers</span>\n                <span>{lastMcpSummary?.activeServerNames.length ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Failed servers</span>\n                <span>{lastMcpSummary?.failedServerNames.length ?? 0}</span>\n              </div>\n              <div className=\"space-y-2 pt-1\">\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    SDK primitives\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {mcpProfile.sdkPrimitives.map((value) => (\n                      <InspectorBadge key={value}>{value}</InspectorBadge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Configured tools\n                  </p>\n                  <div className=\"mt-1 grid gap-1.5\">\n                    {mcpCatalog\n                      .flatMap((server) => server.toolNames)\n                      .map((value) => (\n                        <InspectorBadge\n                          className=\"w-full justify-start\"\n                          key={value}\n                        >\n                          {value}\n                        </InspectorBadge>\n                      ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">Used this run</p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastMcpSummary?.usedToolNames.length\n                      ? lastMcpSummary.usedToolNames\n                      : [\"No MCP tool usage yet\"]\n                    ).map((value) => (\n                      <InspectorBadge key={value}>{value}</InspectorBadge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Failed server errors\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastMcpSummary?.failedServerErrors.length\n                      ? lastMcpSummary.failedServerErrors\n                      : [\"No MCP connection errors\"]\n                    ).map((value) => (\n                      <InspectorBadge key={value}>{value}</InspectorBadge>\n                    ))}\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Sandbox\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Client</span>\n                <span>{sandboxProfile.clientBackend}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Agent model</span>\n                <span>{sandboxProfile.agentModel}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Manifest root</span>\n                <span>\n                  {lastSandboxSummary?.manifestRoot ??\n                    sandboxProfile.manifestRoot}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Workspace</span>\n                <span>{sandboxProfile.workspaceSource}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Persistence</span>\n                <span className=\"max-w-[10rem] text-right\">\n                  {sandboxProfile.sessionPersistence}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Active agent</span>\n                <span className=\"max-w-[10rem] text-right\">\n                  {lastSandboxSummary?.currentAgentName ?? \"No sandbox run yet\"}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Workspace ready</span>\n                <span>\n                  {lastSandboxSummary\n                    ? lastSandboxSummary.workspaceReady\n                      ? \"yes\"\n                      : \"no\"\n                    : \"unknown\"}\n                </span>\n              </div>\n              <div className=\"space-y-2 pt-1\">\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">Mounted paths</p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastSandboxSummary?.mountedPaths.length\n                      ? lastSandboxSummary.mountedPaths\n                      : sandboxProfile.mountedPaths\n                    ).map((value) => (\n                      <Badge key={value} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Default capabilities\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {sandboxProfile.defaultCapabilities.map((value) => (\n                      <Badge key={value} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    SDK primitives\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {sandboxProfile.sdkPrimitives.map((value) => (\n                      <Badge key={value} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Persisted sessions\n                  </p>\n                  <p className=\"mt-1 text-xs/relaxed\">\n                    {lastSandboxSummary?.persistedSessionCount ?? 0}\n                  </p>\n                </div>\n              </div>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Tracing\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <InspectorRow\n                label=\"Runtime default\"\n                value={traceProfile.defaultServerRuntimeTracing}\n              />\n              <InspectorRow\n                label=\"Workflow source\"\n                value={traceProfile.workflowNameSource}\n              />\n              <InspectorRow\n                label=\"Group strategy\"\n                value={traceProfile.groupingStrategy}\n              />\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Tracing</span>\n                <span>\n                  {lastTraceSummary?.tracingDisabled ? \"disabled\" : \"enabled\"}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Sensitive data</span>\n                <span>\n                  {traceIncludesSensitiveData ? \"included\" : \"redacted\"}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Export key</span>\n                <span className=\"max-w-[10rem] text-right\">\n                  {lastTraceSummary?.exportApiKeySource ??\n                    traceProfile.exportApiKeySource}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Trace id</span>\n                <span className=\"max-w-[10rem] truncate text-right\">\n                  {lastTraceSummary?.traceId ?? \"No trace yet\"}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Group id</span>\n                <span className=\"max-w-[10rem] truncate text-right\">\n                  {lastTraceSummary?.groupId ?? \"No group yet\"}\n                </span>\n              </div>\n              <div className=\"space-y-2 pt-1\">\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    SDK primitives\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {traceProfile.sdkPrimitives.map((value) => (\n                      <InspectorBadge key={value}>{value}</InspectorBadge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Trace metadata keys\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastTraceSummary?.metadataKeys.length\n                      ? lastTraceSummary.metadataKeys\n                      : [\"No trace metadata yet\"]\n                    ).map((value) => (\n                      <InspectorBadge key={value}>{value}</InspectorBadge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Disable switch\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    <InspectorBadge>\n                      {traceProfile.disableEnvVar}\n                    </InspectorBadge>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Model\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Model</span>\n                <span className=\"text-right\">{modelProfile.model}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">API</span>\n                <span>{modelProfile.api}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Transport</span>\n                <span>{modelProfile.responsesTransport}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Reasoning</span>\n                <span>{modelProfile.reasoningEffort}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Verbosity</span>\n                <span>{modelProfile.textVerbosity}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Provider</span>\n                <span>{modelProfile.provider}</span>\n              </div>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Guardrails\n            </p>\n            <div className=\"mt-2 space-y-2\">\n              {guardrailCatalog.map((item) => (\n                <div\n                  className=\"space-y-2 border border-foreground/10 px-3 py-2\"\n                  key={item.name}\n                >\n                  <div className=\"flex items-start justify-between gap-2\">\n                    <div className=\"min-w-0\">\n                      <p className=\"break-all font-medium text-sm\">\n                        {item.name}\n                      </p>\n                      <p className=\"break-words text-muted-foreground text-xs\">\n                        {item.sdkPrimitive}\n                      </p>\n                    </div>\n                    <Badge\n                      className={cn(\n                        getToolAvailabilityClassName(item.availability)\n                      )}\n                      variant=\"outline\"\n                    >\n                      {item.availability}\n                    </Badge>\n                  </div>\n                  <p className=\"text-muted-foreground text-xs\">{item.notes}</p>\n                  {usedGuardrailNames.has(item.name) ? (\n                    <Badge\n                      className=\"border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300\"\n                      variant=\"outline\"\n                    >\n                      Evaluated this run\n                    </Badge>\n                  ) : null}\n                </div>\n              ))}\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Approvals\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Pending</span>\n                <span>{lastApprovalSummary?.pendingApprovals.length ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Decisions</span>\n                <span>{lastApprovalSummary?.decisions.length ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Paused state</span>\n                <span>\n                  {lastApprovalSummary?.serializedRunState\n                    ? \"serialized\"\n                    : \"none\"}\n                </span>\n              </div>\n              <div className=\"space-y-2 pt-1\">\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">\n                    Pending approvals\n                  </p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastApprovalSummary?.pendingApprovals.length\n                      ? lastApprovalSummary.pendingApprovals.map(\n                          (item) => item.toolName\n                        )\n                      : [\"No pending approval metadata yet\"]\n                    ).map((value) => (\n                      <Badge key={value} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">Decisions</p>\n                  <div className=\"mt-1 flex flex-wrap gap-1.5\">\n                    {(lastApprovalSummary?.decisions.length\n                      ? lastApprovalSummary.decisions.map((item) =>\n                          item.approved ? \"approved\" : \"rejected\"\n                        )\n                      : [\"No approval decisions yet\"]\n                    ).map((value, index) => (\n                      <Badge key={`${value}-${index}`} variant=\"outline\">\n                        {value}\n                      </Badge>\n                    ))}\n                  </div>\n                </div>\n              </div>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Results\n            </p>\n            <div className=\"mt-2 space-y-2 border border-foreground/10 px-3 py-3 text-sm\">\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Active agent</span>\n                <span className=\"max-w-[10rem] text-right\">\n                  {lastResultSummary?.activeAgentName ??\n                    \"No settled result yet\"}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Last agent</span>\n                <span className=\"max-w-[10rem] text-right\">\n                  {lastResultSummary?.lastAgentName ?? \"No settled result yet\"}\n                </span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">History</span>\n                <span>{lastResultSummary?.historyLength ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Output items</span>\n                <span>{lastResultSummary?.outputCount ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">New items</span>\n                <span>{lastResultSummary?.newItemsCount ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Raw responses</span>\n                <span>{lastResultSummary?.rawResponseCount ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Interruptions</span>\n                <span>{lastResultSummary?.interruptionCount ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Requests</span>\n                <span>{lastResultSummary?.requestCount ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Tokens</span>\n                <span>{lastResultSummary?.totalTokens ?? 0}</span>\n              </div>\n              <div className=\"flex items-start justify-between gap-3\">\n                <span className=\"text-muted-foreground\">Run state</span>\n                <span>\n                  {lastResultSummary?.hasResumableState\n                    ? \"available\"\n                    : \"not captured\"}\n                </span>\n              </div>\n              <div className=\"space-y-2 pt-1\">\n                <div>\n                  <p className=\"text-muted-foreground text-xs\">Final output</p>\n                  <p className=\"mt-1 text-xs/relaxed\">\n                    {lastResultSummary?.finalOutputPreview ??\n                      \"No settled result preview yet\"}\n                  </p>\n                </div>\n              </div>\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Tools\n            </p>\n            <div className=\"mt-2 space-y-2\">\n              {toolCatalog.map((item) => (\n                <div\n                  className=\"space-y-2 border border-foreground/10 px-3 py-2\"\n                  key={item.name}\n                >\n                  <div className=\"flex items-start justify-between gap-2\">\n                    <div className=\"min-w-0\">\n                      <p className=\"break-all font-medium text-sm\">\n                        {item.name}\n                      </p>\n                      <p className=\"break-words text-muted-foreground text-xs\">\n                        {item.sdkPrimitive}\n                      </p>\n                    </div>\n                    <Badge\n                      className={cn(\n                        getToolAvailabilityClassName(item.availability)\n                      )}\n                      variant=\"outline\"\n                    >\n                      {item.availability}\n                    </Badge>\n                  </div>\n                  <p className=\"text-muted-foreground text-xs\">{item.notes}</p>\n                  {usedToolNames.has(item.name) ? (\n                    <Badge\n                      className=\"border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300\"\n                      variant=\"outline\"\n                    >\n                      Used this run\n                    </Badge>\n                  ) : null}\n                </div>\n              ))}\n            </div>\n          </div>\n          <div>\n            <p className=\"text-[11px] text-muted-foreground uppercase tracking-[0.2em]\">\n              Guide Coverage\n            </p>\n            <div className=\"mt-2 space-y-2\">\n              {guideCoverageWithCurrentRun.map((item) => (\n                <div\n                  className=\"space-y-2 border border-foreground/10 px-3 py-2\"\n                  key={item.id}\n                >\n                  <div className=\"flex items-start justify-between gap-2\">\n                    <div className=\"min-w-0\">\n                      <p className=\"break-all font-medium text-sm\">\n                        {item.label}\n                      </p>\n                      <p className=\"break-words text-muted-foreground text-xs\">\n                        {item.sdkPrimitive}\n                      </p>\n                    </div>\n                    <Badge\n                      className={cn(\n                        \"shrink-0\",\n                        getStatusClassName(item.implementationStatus)\n                      )}\n                      variant=\"outline\"\n                    >\n                      {getImplementationLabel(item.implementationStatus)}\n                    </Badge>\n                  </div>\n                  <div className=\"space-y-1 text-muted-foreground text-xs\">\n                    <p>{item.observable}</p>\n                    <a\n                      className=\"block break-all text-foreground underline-offset-4 hover:underline\"\n                      href={item.sourceGuide}\n                      rel=\"noreferrer\"\n                      target=\"_blank\"\n                    >\n                      {item.sourceGuide}\n                    </a>\n                  </div>\n                  <div className=\"flex flex-wrap items-center gap-2\">\n                    <Badge\n                      className={cn(getStatusClassName(item.currentRunStatus))}\n                      variant=\"outline\"\n                    >\n                      Run: {getRunStatusLabel(item.currentRunStatus)}\n                    </Badge>\n                    <Badge variant=\"outline\">\n                      Provider: {item.providerCapabilityStatus}\n                    </Badge>\n                  </div>\n                </div>\n              ))}\n            </div>\n          </div>\n        </div>\n      </aside>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/openai-agents-sdk-demo/openai-agents-sdk-demo-workspace.tsx"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/ai-gateway/contract.ts",
      "content": "import { createGateway } from \"ai\";\n\nexport const DEFAULT_GATEWAY_BASE_URL = \"https://ai-gateway.vercel.sh/v3/ai\";\nexport const MINIMUM_NODE_VERSION = \"22.13.0\";\nconst nodeVersionPattern = /^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:[-+].*)?$/;\n\nexport type AiGatewayEnvRecord = Record<string, string | undefined>;\n\nexport interface ParsedNodeVersion {\n  major: number;\n  minor: number;\n  patch: number;\n}\n\nexport interface AiGatewayContractConfig {\n  apiKey: string;\n  baseURL: string;\n  chatModel: string;\n}\n\nexport interface AiGatewayResolvedEnv {\n  apiKey: string | undefined;\n  baseURL: string;\n  chatModel: string;\n}\n\nexport interface AiGatewaySetupConfig {\n  baseURL: string;\n  chatModel: string;\n}\n\nexport interface AiGatewayContractSetupState<\n  TConfig extends AiGatewaySetupConfig = AiGatewaySetupConfig,\n> {\n  config: TConfig;\n  isReady: boolean;\n  issues: string[];\n  nodeVersion: string;\n}\n\nexport interface AiGatewayContractOptions<\n  TConfig extends AiGatewaySetupConfig = AiGatewaySetupConfig,\n> {\n  buildConfig?: (\n    resolvedEnv: AiGatewayResolvedEnv,\n    env: AiGatewayEnvRecord\n  ) => TConfig;\n  defaultBaseURL?: string;\n  defaultChatModel: string;\n  getAdditionalIssues?: (\n    resolvedEnv: AiGatewayResolvedEnv,\n    env: AiGatewayEnvRecord\n  ) => string[];\n  missingApiKeyError: string;\n  missingApiKeyIssue?: string;\n}\n\nconst genericMissingApiKeyIssue =\n  \"AI_GATEWAY_API_KEY is missing. The demo can render, but chat requests will fail until it is configured.\";\n\nexport function parseNodeVersion(version: string): ParsedNodeVersion {\n  const match = nodeVersionPattern.exec(version);\n  const major = Number(match?.[1]);\n  const minor = Number(match?.[2]);\n  const patch = Number(match?.[3]);\n\n  if (![major, minor, patch].every(Number.isInteger)) {\n    throw new Error(`Unable to parse Node.js version: \"${version}\".`);\n  }\n\n  return { major, minor, patch };\n}\n\nexport function getNodeMajor(version: string): number {\n  return parseNodeVersion(version).major;\n}\n\nfunction compareNodeVersions(\n  left: ParsedNodeVersion,\n  right: ParsedNodeVersion\n): number {\n  if (left.major !== right.major) {\n    return left.major - right.major;\n  }\n\n  if (left.minor !== right.minor) {\n    return left.minor - right.minor;\n  }\n\n  return left.patch - right.patch;\n}\n\nexport function assertSupportedNodeRuntime(version = process.version): number {\n  const parsedVersion = parseNodeVersion(version);\n  const minimumVersion = parseNodeVersion(MINIMUM_NODE_VERSION);\n\n  if (compareNodeVersions(parsedVersion, minimumVersion) < 0) {\n    throw new Error(\n      `Node.js ${version} is unsupported. This demo workspace requires Node.js >=${MINIMUM_NODE_VERSION}.`\n    );\n  }\n\n  return parsedVersion.major;\n}\n\nexport function resolveAiGatewayContractEnv(\n  env: AiGatewayEnvRecord,\n  options: Pick<AiGatewayContractOptions, \"defaultBaseURL\" | \"defaultChatModel\">\n): AiGatewayResolvedEnv {\n  return {\n    apiKey: env.AI_GATEWAY_API_KEY,\n    baseURL:\n      env.AI_GATEWAY_BASE_URL ||\n      options.defaultBaseURL ||\n      DEFAULT_GATEWAY_BASE_URL,\n    chatModel: env.AI_GATEWAY_CHAT_MODEL || options.defaultChatModel,\n  };\n}\n\nexport function readAiGatewayContractConfig(\n  env: AiGatewayEnvRecord,\n  options: AiGatewayContractOptions\n): AiGatewayContractConfig {\n  assertSupportedNodeRuntime();\n  const resolvedEnv = resolveAiGatewayContractEnv(env, options);\n\n  if (!resolvedEnv.apiKey) {\n    throw new Error(options.missingApiKeyError);\n  }\n\n  return {\n    apiKey: resolvedEnv.apiKey,\n    baseURL: resolvedEnv.baseURL,\n    chatModel: resolvedEnv.chatModel,\n  };\n}\n\nexport function buildAiGatewayContractSetupState<\n  TConfig extends AiGatewaySetupConfig,\n>(\n  env: AiGatewayEnvRecord,\n  options: AiGatewayContractOptions<TConfig>\n): AiGatewayContractSetupState<TConfig> {\n  const issues: string[] = [];\n  const resolvedEnv = resolveAiGatewayContractEnv(env, options);\n\n  try {\n    assertSupportedNodeRuntime();\n  } catch (error) {\n    issues.push(\n      error instanceof Error ? error.message : \"Unsupported Node.js runtime.\"\n    );\n  }\n\n  if (!resolvedEnv.apiKey) {\n    issues.push(options.missingApiKeyIssue || genericMissingApiKeyIssue);\n  }\n\n  issues.push(...(options.getAdditionalIssues?.(resolvedEnv, env) ?? []));\n\n  return {\n    config:\n      options.buildConfig?.(resolvedEnv, env) ??\n      ({\n        baseURL: resolvedEnv.baseURL,\n        chatModel: resolvedEnv.chatModel,\n      } as TConfig),\n    isReady: issues.length === 0,\n    issues,\n    nodeVersion: process.version,\n  };\n}\n\nexport function createAiGatewayFromContract(\n  env: AiGatewayEnvRecord,\n  options: AiGatewayContractOptions\n): ReturnType<typeof createGateway> {\n  const { apiKey, baseURL } = readAiGatewayContractConfig(env, options);\n\n  return createGateway({\n    apiKey,\n    baseURL,\n  });\n}\n",
      "type": "registry:lib",
      "target": "@lib/ai-gateway/contract.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/message-metadata.ts",
      "content": "import type { UIMessage } from \"ai\";\nimport { z } from \"zod\";\n\nimport { openAiAgentsSdkDemoApprovalSummarySchema } from \"./server/approvals\";\nimport { openAiAgentsSdkDemoContextSummarySchema } from \"./server/context\";\nimport { openAiAgentsSdkDemoAiSdkExtensionSummarySchema } from \"./server/extensions\";\nimport { openAiAgentsSdkDemoMcpSummarySchema } from \"./server/mcp\";\nimport { openAiAgentsSdkDemoSandboxSummarySchema } from \"./server/sandbox\";\nimport { openAiAgentsSdkDemoSessionSummarySchema } from \"./server/sessions\";\nimport { openAiAgentsSdkDemoTraceSummarySchema } from \"./server/tracing\";\n\nexport const openAiAgentsSdkDemoStreamSummarySchema = z.object({\n  agentNames: z.array(z.string()),\n  rawModelEventCount: z.number().int().nonnegative(),\n  rawModelEventTypes: z.array(z.string()),\n  rawModelSources: z.array(z.string()),\n  runItemEventCount: z.number().int().nonnegative(),\n  runItemEventNames: z.array(z.string()),\n});\n\nexport const openAiAgentsSdkDemoHandoffSummarySchema = z.object({\n  activeAgentName: z.string().optional(),\n  handoffTargetNames: z.array(z.string()),\n  handoffTransitions: z.array(z.string()),\n});\n\nexport const openAiAgentsSdkDemoResultSummarySchema = z.object({\n  activeAgentName: z.string().optional(),\n  finalOutputPreview: z.string().optional(),\n  hasResumableState: z.boolean(),\n  historyLength: z.number().int().nonnegative(),\n  inputTokens: z.number().int().nonnegative(),\n  interruptionCount: z.number().int().nonnegative(),\n  lastAgentName: z.string().optional(),\n  newItemsCount: z.number().int().nonnegative(),\n  outputCount: z.number().int().nonnegative(),\n  outputTokens: z.number().int().nonnegative(),\n  rawResponseCount: z.number().int().nonnegative(),\n  requestCount: z.number().int().nonnegative(),\n  totalTokens: z.number().int().nonnegative(),\n});\n\nexport const openAiAgentsSdkDemoMessageMetadataSchema = z.object({\n  aiSdkExtensionSummary:\n    openAiAgentsSdkDemoAiSdkExtensionSummarySchema.optional(),\n  approvalSummary: openAiAgentsSdkDemoApprovalSummarySchema.optional(),\n  contextSummary: openAiAgentsSdkDemoContextSummarySchema.optional(),\n  handoffSummary: openAiAgentsSdkDemoHandoffSummarySchema.optional(),\n  lastResponseId: z.string().optional(),\n  mcpSummary: openAiAgentsSdkDemoMcpSummarySchema.optional(),\n  resultSummary: openAiAgentsSdkDemoResultSummarySchema.optional(),\n  sandboxSummary: openAiAgentsSdkDemoSandboxSummarySchema.optional(),\n  sessionSummary: openAiAgentsSdkDemoSessionSummarySchema.optional(),\n  streamSummary: openAiAgentsSdkDemoStreamSummarySchema.optional(),\n  traceSummary: openAiAgentsSdkDemoTraceSummarySchema.optional(),\n  usedGuardrailNames: z.array(z.string()).optional(),\n  usedGuideIds: z.array(z.string()).optional(),\n  usedToolNames: z.array(z.string()).optional(),\n});\n\nexport type OpenAiAgentsSdkDemoMessageMetadata = z.infer<\n  typeof openAiAgentsSdkDemoMessageMetadataSchema\n>;\n\nexport type OpenAiAgentsSdkDemoMessage =\n  UIMessage<OpenAiAgentsSdkDemoMessageMetadata>;\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/message-metadata.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/README.md",
      "content": "# OpenAI Agents SDK Demo\n\n`openai-agents-sdk-demo` keeps the backend on the official OpenAI Agents SDK path, swaps the default provider transport onto AI Gateway's OpenAI-compatible Responses endpoint, and keeps the frontend on the repo's existing AI SDK UI stack.\n\n## Business shape\n\nThis demo proves the narrowest official bridge:\n\n- `@openai/agents` defines and runs the agent\n- an `OpenAI` client pointed at AI Gateway is registered through `setDefaultOpenAIClient(...)`\n- `run(..., { stream: true })` produces the streamed run\n- `@openai/agents-extensions/ai-sdk-ui` converts that run into the AI SDK UI response the existing chat workspace can consume\n\n## Feature slice\n\n```text\napp/\n├── api/demos/openai-agents-sdk-demo/\n│   ├── route.ts\n│   ├── mcp/route.ts\n│   └── realtime/\n│       ├── client-secrets/route.ts\n│       ├── sip/route.ts\n│       └── twilio/incoming-call/route.ts\n└── demos/openai-agents-sdk-demo/page.tsx\ncomponents/openai-agents-sdk-demo/\n├── openai-agents-sdk-demo-runtime-inspector.ts\n├── openai-agents-sdk-demo-screen.tsx\n├── openai-agents-sdk-demo-session.ts\n├── openai-agents-sdk-demo-voice-panel.tsx\n├── openai-agents-sdk-demo-voice-utils.ts\n├── openai-agents-sdk-demo-workspace-layout.ts\n└── openai-agents-sdk-demo-workspace.tsx\nlib/openai-agents-sdk-demo/\n├── README.md\n├── message-metadata.ts\n├── voice-lane.ts\n├── server/\n│   ├── agents.ts\n│   ├── approvals.ts\n│   ├── chat.ts\n│   ├── context.ts\n│   ├── demo-mcp-server.ts\n│   ├── extensions.ts\n│   ├── handoff-extensions.ts\n│   ├── handoffs.ts\n│   ├── guardrails.ts\n│   ├── guide-coverage.ts\n│   ├── mcp.ts\n│   ├── models.ts\n│   ├── results.ts\n│   ├── running.ts\n│   ├── sandbox.ts\n│   ├── sessions.ts\n│   ├── stream-artifacts.ts\n│   ├── streaming.ts\n│   ├── tracing.ts\n│   ├── voice-extensions.ts\n│   ├── voice-cloudflare-app.ts\n│   ├── voice-cloudflare-worker-module.ts\n│   ├── voice-cloudflare-worker.ts\n│   ├── voice-realtime.ts\n│   ├── voice-server-audio.ts\n│   ├── voice-sip-route.ts\n│   ├── voice-sip.ts\n│   ├── voice-twilio-app.ts\n│   ├── voice-twilio-bridge.ts\n│   ├── voice-twilio-route.ts\n│   ├── voice-websocket.ts\n│   ├── voice.ts\n│   ├── runtime.ts\n│   └── tools.ts\n```\n\n## Contracts\n\n- Missing `AI_GATEWAY_API_KEY` blocks chat requests with an explicit setup error.\n- The backend source core stays on the official OpenAI Agents SDK run path.\n- The route returns the official AI SDK UI bridge response instead of a repo-local custom stream format.\n- The current Tools slice wires `tool()`, one approval-required `tool({ needsApproval: true })`, `webSearchTool()`, `fileSearchTool()` when vector stores are configured, `codeInterpreterTool()`, `imageGenerationTool()`, `toolSearchTool()` on supported GPT-5.4+/5.5 models, and `agent.asTool()` into the main agent.\n- Tool availability stays explicit. In the current AI Gateway Responses configuration, `imageGenerationTool()` is registered but provider-blocked for user-facing runs because streamed hosted image generation does not return a renderable artifact to the AI SDK UI bridge.\n- The current Guardrails slice wires one input guardrail and one output guardrail into the main agent, returns explicit tripwire errors, and surfaces per-run guardrail evaluation back to the UI through message metadata.\n- The current Running Agents slice uses the official `run()` helper with explicit `maxTurns`, request-scoped `AbortSignal`, and provider-aware continuation: true OpenAI Responses ids (`resp_*`) use `previousResponseId`, while AI Gateway ids (`gen_*`) continue through `MemorySession` history instead of being sent back as unusable response ids.\n- The current Streaming slice keeps the official `createAiSdkUiMessageStream()` bridge for assistant text while wrapping the underlying `RunStreamEvent` iterable with a thin observer that records `agent_updated_stream_event`, `raw_model_stream_event`, and `run_item_stream_event` metadata for the runtime inspector.\n- The workspace inspector now exposes the latest stream summary: agent names, raw model event count/types/sources, and run-item event count/names.\n- The current Agent Orchestration slice treats the already-configured `research_memo_agent` specialist as the first official `agent.asTool()` coverage path. When that tool runs, the workspace marks both `Tools` and `Agent Orchestration` from real run metadata.\n- The current Handoffs slice exposes one specialist agent directly in `handoffs: [agent]` and one explicit `handoff()` with `inputType`, `onHandoff`, and a tool-history filter. The workspace inspector now exposes configured handoffs plus per-run active-agent, target, and transition metadata from real handoff items.\n- The current Results slice waits for `await agentStream.completed`, then maps the settled `RunResult` surface into message metadata: `activeAgent`, `lastAgent`, `finalOutput`, `history`, `newItems`, `output`, `interruptions`, `state.usage`, and `rawResponses`.\n- The workspace inspector now exposes the latest settled result summary: active agent, final output preview, history/output/new-item counts, interruption count, usage totals, and whether resumable run state was captured.\n- The current Human-in-the-loop slice pauses on a real approval-required tool, surfaces AI SDK approval cards in the chat, serializes paused `RunState`, and resumes through `RunState.fromString(...)` plus `state.approve(...)` / `state.reject(...)`.\n- The workspace inspector now exposes the latest approval summary: pending approvals, reviewer decisions, and whether a paused run state is serialized and ready to resume.\n- The current Sessions slice passes an official `MemorySession` into every `run(...)`, carries the session id through assistant metadata, reuses the same session for AI Gateway follow-ups, true `previousResponseId` follow-ups, and approval resume, and exposes session profile plus latest session summary in the workspace inspector.\n- The current Context Management slice builds a typed local `RunContext<T>` object from the latest user turn plus `MemorySession`, passes it through `run(..., { context })`, uses dynamic instructions on the main agent, reads `runContext.context` in `tool()` and guardrail callbacks, and exposes context summary/profile in the runtime inspector.\n- The current MCP slice uses the official local `MCPServerStreamableHttp` path against a demo-local Streamable HTTP MCP route, connects servers through `connectMcpServers(...)`, passes `mcpServers.active` into `Agent.mcpServers`, enables `includeServerInToolNames`, and exposes connection state plus prefixed MCP tool usage in the runtime inspector.\n- The current Tracing slice keeps the official per-run tracing path on `run(...)`: explicit `workflowName`, `traceId`, `groupId`, `traceMetadata`, `tracingDisabled`, `traceIncludeSensitiveData`, and optional `tracing.apiKey` override. The workspace inspector exposes the latest trace summary plus the tracing setup contract.\n- The current Sandbox Agents slice keeps the official sandbox path on `SandboxAgent`, `Manifest`, `Capabilities.default()`, `UnixLocalSandboxClient`, and `run(..., { sandbox })`. The sandbox specialist mounts `docs/frontend` and the demo feature slice read-only, persists `sessionState` by demo session id, and exposes sandbox profile/summary metadata in the workspace inspector.\n- The current Extensions / AI SDK Integration slice keeps the official `@openai/agents-extensions/ai-sdk-ui` bridge on the route, exposes bridge usage through assistant metadata, and keeps the beta `aisdk(model)` adapter as an explicit `not-used` provider boundary while the run depends on deferred Responses tool loading.\n- The current Voice Agents slice exposes the official `RealtimeAgent` / `RealtimeSession` primitives, WebRTC browser transport, a real `/api/demos/openai-agents-sdk-demo/realtime/client-secrets` route backed by `client.realtime.clientSecrets.create()`, successful client-secret minting counted as `send_message` usage for `openai-agents-sdk-demo`, and a browser voice lane that calls `session.connect({ apiKey })`. The workspace now keeps the main surface to two columns by moving voice entry into a compact screen-rail strip plus a detail dialog, while the full realtime controls, transcripts, approvals, and event feed stay inside that dialog. That voice lane also carries official realtime function tools, an approval-required publish tool, one realtime handoff target, suggested smoke prompts, and visible session-event state.\n- The current server-side transport slice now includes a real `OpenAIRealtimeWebSocket` session factory in `server/voice-websocket.ts`. It proves the official server transport path, keeps raw event access on `session.transport.sendEvent()`, and still requires a custom audio pipeline before it becomes a full end-to-end product lane.\n- The current server-audio slice now includes a real `RealtimeSession.sendAudio()` loop in `server/voice-server-audio.ts`. It buffers `session.on(\"audio\")` output chunks, forwards raw `transport_event` items, exposes `session.transport.requestResponse()` and `RealtimeSession.interrupt()`, and keeps the application-owned capture/playback layer explicit.\n- The current SIP transport slice now includes a real `OpenAIRealtimeSIP` session factory in `server/voice-sip.ts` plus an `OpenAIRealtimeSIP.buildInitialConfig()` helper for call-accept payloads. It keeps the `callId` and provider call-control contract explicit instead of pretending the current browser page can start SIP calls directly.\n- The current SIP route slice now includes a real `/api/demos/openai-agents-sdk-demo/realtime/sip` endpoint in `server/voice-sip-route.ts`. It validates `callId`, gates on `OPENAI_API_KEY`, and returns the official `OpenAIRealtimeSIP.buildInitialConfig()` payload so an external SIP provider or a future OpenAI call-control lane can accept the call without rebuilding session config logic outside the SDK.\n- The current provider-extension slice now includes real `CloudflareRealtimeTransportLayer` and `TwilioRealtimeTransportLayer` factories in `server/voice-extensions.ts`. They preserve the official extension primitives, require a native `OPENAI_API_KEY`, and keep provider/runtime prerequisites explicit.\n- The current Cloudflare worker slice now includes a real runtime wrapper in `server/voice-cloudflare-worker.ts`. It consumes `CloudflareRealtimeTransportLayer`, connects through `RealtimeSession.connect(...)`, and keeps the workerd-specific `fetch() + Upgrade: websocket`, `skipOpenEventListeners: true`, and `nodejs_compat` contract explicit without pretending the Next app already runs inside Cloudflare Workers.\n- The current Cloudflare worker-app slice now includes a real fetch-wrapper factory in `server/voice-cloudflare-app.ts`. It follows the official worker entry shape with `export default { fetch(request, env, ctx) }`, exposes `/` health plus `/connect` session-bootstrap behavior, validates the `Upgrade: websocket` contract up front, and composes the existing Cloudflare transport runtime instead of inventing a repo-local transport.\n- The current Cloudflare worker-module slice now includes a deployable module wrapper in `server/voice-cloudflare-worker-module.ts`. It exports the actual `fetch(request, env, ctx)` entry contract on top of the existing worker app factory, so the remaining gap is deployment/runtime wiring and live socket verification, not missing SDK glue.\n- The current Twilio call-control slice now includes a real `/api/demos/openai-agents-sdk-demo/realtime/twilio/incoming-call` endpoint in `server/voice-twilio-route.ts`. It returns TwiML `<Connect><Stream />`, requires `OPENAI_AGENTS_TWILIO_MEDIA_STREAM_URL` to point at an external `wss://` media-stream server, and keeps the WebSocket hosting boundary explicit instead of pretending the current Next route already terminates Twilio media streams.\n- The current Twilio media-stream slice now includes a real bridge factory in `server/voice-twilio-bridge.ts`. It consumes `TwilioRealtimeTransportLayer`, connects the session with `RealtimeSession.connect(...)`, and closes the session when the upstream Twilio websocket closes. Hosting the websocket server is still an external deployment concern, and the demo keeps that contract explicit.\n- The current Twilio deployed-server slice now includes a real app factory in `server/voice-twilio-app.ts`. It follows the official Fastify/WebSocket example shape with `/`, `/incoming-call`, and `/media-stream` entry points, derives the public `wss://.../media-stream` URL from the incoming request host, and composes the existing TwiML builder plus realtime bridge so an external server can reuse demo-local voice primitives without rewriting them.\n- `server/handoff-extensions.ts` is a local copy of the official handoff prompt-prefix and `removeAllTools` semantics. It exists because the published `@openai/agents-core/extensions` entry is not resolvable from the current app package graph even though the upstream package ships those helpers.\n",
      "type": "registry:file",
      "target": "@lib/openai-agents-sdk-demo/README.md"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/agents.ts",
      "content": "import { Agent, type MCPServer } from \"@openai/agents\";\n\nimport {\n  getOpenAiAgentsSdkDemoContextInstructions,\n  type OpenAiAgentsSdkDemoContext,\n} from \"./context\";\nimport { getOpenAiAgentsSdkDemoGuardrails } from \"./guardrails\";\nimport { createOpenAiAgentsSdkDemoHandoffs } from \"./handoffs\";\nimport {\n  isOpenAiAgentsSdkDemoImageGenerationProviderBlocked,\n  type OpenAiAgentsSdkDemoModelProfile,\n} from \"./models\";\nimport { createOpenAiAgentsSdkDemoTools } from \"./tools\";\n\ninterface OpenAiAgentsSdkDemoAgentOptions {\n  env?: Record<string, string | undefined>;\n  mcpServers?: MCPServer[];\n  modelProfile: OpenAiAgentsSdkDemoModelProfile;\n}\n\nconst openAiAgentsSdkDemoInstructions = [\n  \"You are the OpenAI Agents SDK demo in an engineer-facing demo gallery.\",\n  \"Answer clearly and briefly.\",\n  \"Explain which part of the answer comes from the OpenAI Agents SDK runtime path only when the user asks for implementation detail.\",\n  \"Do not mention internal helper or tool names such as web_search, web.run, code_interpreter, or agent.asTool unless the user explicitly asks how the demo is implemented.\",\n  \"For time-sensitive public-company questions, use web_search before citing facts.\",\n  \"For a brief web-search summary, use the hosted web_search tool directly and answer from the searched evidence.\",\n  \"Use code_interpreter when calculations, tables, or comparisons would improve the answer.\",\n  \"Use build_research_brief only at the start of an investment research or public-company company-analysis request.\",\n  \"If the user asks to send or publish a finished research conclusion, use publish_research_summary and wait for approval before claiming it was shared.\",\n  \"Use image_generation for image requests when the user asks for a generated image.\",\n  \"Use MCP tools when the user asks about this demo's README, durable frontend doc, runtime contract, or MCP setup.\",\n  \"If an MCP server is unavailable, explain that state plainly and continue without pretending the tools ran.\",\n  \"Use research_memo_agent when you have enough evidence to synthesize a memo.\",\n  \"Use sandbox_workspace_agent when the user needs repo-grounded inspection of the mounted demo docs or feature slice through a sandbox workspace.\",\n  \"Use a handoff when one specialist should take over the conversation directly.\",\n  \"Do not claim to have generated an image, file, or download unless the corresponding tool actually ran and the current chat surface can render that artifact.\",\n].join(\" \");\n\nexport function createOpenAiAgentsSdkDemoAgent({\n  env,\n  mcpServers = [],\n  modelProfile,\n}: OpenAiAgentsSdkDemoAgentOptions) {\n  const { inputGuardrails, outputGuardrails } =\n    getOpenAiAgentsSdkDemoGuardrails();\n  const isImageGenerationProviderBlocked =\n    isOpenAiAgentsSdkDemoImageGenerationProviderBlocked(modelProfile);\n\n  return new Agent<OpenAiAgentsSdkDemoContext>({\n    inputGuardrails,\n    instructions: (runContext) =>\n      [\n        openAiAgentsSdkDemoInstructions,\n        getOpenAiAgentsSdkDemoContextInstructions(runContext, {\n          isImageGenerationProviderBlocked,\n        }),\n      ].join(\" \"),\n    model: modelProfile.model,\n    modelSettings: {\n      reasoning: {\n        effort: modelProfile.reasoningEffort,\n      },\n      text: {\n        verbosity: modelProfile.textVerbosity,\n      },\n    },\n    mcpConfig: {\n      convertSchemasToStrict: true,\n      includeServerInToolNames: true,\n    },\n    mcpServers,\n    name: \"OpenAI Agents SDK Demo\",\n    outputGuardrails,\n    handoffs: createOpenAiAgentsSdkDemoHandoffs({\n      modelProfile,\n    }),\n    toolUseBehavior: \"run_llm_again\",\n    tools: createOpenAiAgentsSdkDemoTools({\n      env,\n      modelProfile,\n    }),\n  });\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/agents.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/approvals.ts",
      "content": "import { RunState, type RunToolApprovalItem } from \"@openai/agents\";\nimport type { UIMessage } from \"ai\";\nimport { z } from \"zod\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\n\nexport const openAiAgentsSdkDemoApprovalDecisionSchema = z.object({\n  approvalId: z.string(),\n  approved: z.boolean(),\n  reason: z.string().optional(),\n});\n\nexport const openAiAgentsSdkDemoPendingApprovalSchema = z.object({\n  agentName: z.string().optional(),\n  approvalId: z.string(),\n  argumentsPreview: z.string().optional(),\n  toolCallId: z.string(),\n  toolName: z.string(),\n});\n\nexport const openAiAgentsSdkDemoApprovalSummarySchema = z.object({\n  decisions: z.array(openAiAgentsSdkDemoApprovalDecisionSchema),\n  hasPendingApprovals: z.boolean(),\n  pendingApprovals: z.array(openAiAgentsSdkDemoPendingApprovalSchema),\n  serializedRunState: z.string().optional(),\n});\n\nexport class OpenAiAgentsSdkDemoApprovalError extends Error {}\n\nconst pendingApprovalMessage =\n  \"A tool approval is pending. Approve or reject it before continuing.\";\n\nfunction getLatestAssistantMessage(messages: UIMessage[]) {\n  return [...messages]\n    .reverse()\n    .find((message) => message.role === \"assistant\");\n}\n\nfunction getLatestAssistantMetadata(messages: UIMessage[]) {\n  return getLatestAssistantMessage(messages)?.metadata as\n    | OpenAiAgentsSdkDemoMessageMetadata\n    | undefined;\n}\n\nfunction stringifyPreview(value: unknown) {\n  if (typeof value === \"undefined\") {\n    return;\n  }\n\n  const text =\n    typeof value === \"string\" ? value : JSON.stringify(value, null, 0);\n  const normalized = text.trim();\n\n  if (!normalized) {\n    return;\n  }\n\n  if (normalized.length <= 180) {\n    return normalized;\n  }\n\n  return `${normalized.slice(0, 177)}...`;\n}\n\nfunction getApprovalId(interruption: RunToolApprovalItem) {\n  const rawItem = interruption.rawItem as {\n    callId?: string;\n    id?: string;\n  };\n\n  return (\n    rawItem.id ?? rawItem.callId ?? interruption.toolName ?? \"tool-approval\"\n  );\n}\n\nfunction getToolCallId(interruption: RunToolApprovalItem) {\n  const rawItem = interruption.rawItem as {\n    callId?: string;\n    id?: string;\n  };\n\n  return rawItem.callId ?? rawItem.id ?? interruption.toolName ?? \"tool-call\";\n}\n\nfunction getOpenAiAgentsSdkDemoApprovalResponses(messages: UIMessage[]) {\n  const latestAssistantMessage = getLatestAssistantMessage(messages);\n\n  if (!latestAssistantMessage) {\n    return [];\n  }\n\n  return latestAssistantMessage.parts.flatMap((part) => {\n    const toolPart = part as {\n      approval?: {\n        approved?: boolean;\n        id?: string;\n        reason?: string;\n      };\n      state?: string;\n      type?: string;\n    };\n\n    if (\n      (toolPart.type !== \"dynamic-tool\" &&\n        !String(toolPart.type).startsWith(\"tool-\")) ||\n      toolPart.state !== \"approval-responded\" ||\n      !toolPart.approval ||\n      typeof toolPart.approval.id !== \"string\" ||\n      typeof toolPart.approval.approved !== \"boolean\"\n    ) {\n      return [];\n    }\n\n    return [\n      {\n        approvalId: toolPart.approval.id,\n        approved: toolPart.approval.approved,\n        reason:\n          typeof toolPart.approval.reason === \"string\"\n            ? toolPart.approval.reason\n            : undefined,\n      },\n    ];\n  });\n}\n\nexport function getOpenAiAgentsSdkDemoPendingApprovalMessage() {\n  return pendingApprovalMessage;\n}\n\nexport function getOpenAiAgentsSdkDemoApprovalErrorMessage(error: unknown) {\n  if (error instanceof OpenAiAgentsSdkDemoApprovalError) {\n    return error.message;\n  }\n\n  return null;\n}\n\nexport function hasOpenAiAgentsSdkDemoPendingApproval(messages: UIMessage[]) {\n  const approvalSummary = getLatestAssistantMetadata(messages)?.approvalSummary;\n\n  return Boolean(approvalSummary?.hasPendingApprovals);\n}\n\nexport function hasOpenAiAgentsSdkDemoApprovalResponses(messages: UIMessage[]) {\n  return getOpenAiAgentsSdkDemoApprovalResponses(messages).length > 0;\n}\n\nexport async function getOpenAiAgentsSdkDemoApprovalResumeState(\n  messages: UIMessage[],\n  agent: Parameters<typeof RunState.fromString>[0]\n) {\n  const approvalSummary = getLatestAssistantMetadata(messages)?.approvalSummary;\n  const responses = getOpenAiAgentsSdkDemoApprovalResponses(messages);\n\n  if (responses.length === 0) {\n    return null;\n  }\n\n  if (!approvalSummary?.serializedRunState) {\n    throw new OpenAiAgentsSdkDemoApprovalError(\n      \"The pending approval state is missing serialized run data. Start a new turn and reproduce the approval request.\"\n    );\n  }\n\n  const state = await RunState.fromString(\n    agent,\n    approvalSummary.serializedRunState\n  );\n  const interruptions = state.getInterruptions();\n\n  for (const response of responses) {\n    const interruption = interruptions.find(\n      (item) => getApprovalId(item) === response.approvalId\n    );\n\n    if (!interruption) {\n      throw new OpenAiAgentsSdkDemoApprovalError(\n        `Approval response ${response.approvalId} does not match any pending interruption.`\n      );\n    }\n\n    if (response.approved) {\n      state.approve(interruption);\n      continue;\n    }\n\n    state.reject(interruption, {\n      ...(response.reason ? { message: response.reason } : {}),\n    });\n  }\n\n  return {\n    responses,\n    state,\n  };\n}\n\nexport function getOpenAiAgentsSdkDemoApprovalUsageMetadata({\n  interruptions,\n  responses = [],\n  state,\n}: {\n  interruptions: RunToolApprovalItem[];\n  responses?: Array<{\n    approvalId: string;\n    approved: boolean;\n    reason?: string;\n  }>;\n  state?: {\n    toString(): string;\n  };\n}) {\n  if (interruptions.length === 0 && responses.length === 0) {\n    return;\n  }\n\n  const pendingApprovals = interruptions.map((interruption) => ({\n    ...(interruption.agent?.name ? { agentName: interruption.agent.name } : {}),\n    approvalId: getApprovalId(interruption),\n    ...(stringifyPreview(interruption.arguments)\n      ? { argumentsPreview: stringifyPreview(interruption.arguments) }\n      : {}),\n    toolCallId: getToolCallId(interruption),\n    toolName: interruption.toolName ?? \"tool\",\n  }));\n  const usedToolNames = Array.from(\n    new Set(pendingApprovals.map((item) => item.toolName).filter(Boolean))\n  );\n\n  return {\n    approvalSummary: {\n      decisions: responses,\n      hasPendingApprovals: pendingApprovals.length > 0,\n      pendingApprovals,\n      ...(pendingApprovals.length > 0 && state\n        ? {\n            serializedRunState: state.toString(),\n          }\n        : {}),\n    },\n    usedGuideIds: [\"human-in-the-loop\"],\n    ...(usedToolNames.length > 0 ? { usedToolNames } : {}),\n  };\n}\n\nexport function assertOpenAiAgentsSdkDemoNoPendingApproval(\n  messages: UIMessage[]\n) {\n  if (!hasOpenAiAgentsSdkDemoPendingApproval(messages)) {\n    return;\n  }\n\n  if (hasOpenAiAgentsSdkDemoApprovalResponses(messages)) {\n    return;\n  }\n\n  const latestMessage = messages.at(-1);\n\n  if (latestMessage?.role === \"user\") {\n    throw new OpenAiAgentsSdkDemoApprovalError(pendingApprovalMessage);\n  }\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/approvals.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/chat.ts",
      "content": "import type { RunStreamEvent, RunToolApprovalItem } from \"@openai/agents\";\nimport {\n  run,\n  setDefaultOpenAIClient,\n  setOpenAIAPI,\n  setOpenAIResponsesTransport,\n} from \"@openai/agents\";\nimport { createAiSdkUiMessageStream } from \"@openai/agents-extensions/ai-sdk-ui\";\nimport {\n  createUIMessageStream,\n  createUIMessageStreamResponse,\n  type UIMessage,\n  type UIMessageChunk,\n} from \"ai\";\nimport OpenAI from \"openai\";\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\nimport { createOpenAiAgentsSdkDemoAgent } from \"./agents\";\nimport {\n  assertOpenAiAgentsSdkDemoNoPendingApproval,\n  getOpenAiAgentsSdkDemoApprovalErrorMessage,\n  getOpenAiAgentsSdkDemoApprovalResumeState,\n  getOpenAiAgentsSdkDemoApprovalUsageMetadata,\n} from \"./approvals\";\nimport {\n  createOpenAiAgentsSdkDemoContext,\n  getOpenAiAgentsSdkDemoContextUsageMetadata,\n} from \"./context\";\nimport { getOpenAiAgentsSdkDemoAiSdkExtensionUsageMetadata } from \"./extensions\";\nimport {\n  getOpenAiAgentsSdkDemoGuardrailErrorMessage,\n  getOpenAiAgentsSdkDemoGuardrailUsageMetadata,\n} from \"./guardrails\";\nimport { getOpenAiAgentsSdkDemoHandoffUsageMetadata } from \"./handoffs\";\nimport {\n  connectOpenAiAgentsSdkDemoMcpServers,\n  getOpenAiAgentsSdkDemoMcpUsageMetadata,\n} from \"./mcp\";\nimport {\n  getOpenAiAgentsSdkDemoModelProfile,\n  getOpenAiAgentsSdkDemoProviderErrorMessage,\n  isOpenAiAgentsSdkDemoImageGenerationProviderBlocked,\n} from \"./models\";\nimport { getOpenAiAgentsSdkDemoResultUsageMetadata } from \"./results\";\nimport {\n  getOpenAiAgentsSdkDemoRunningUsageMetadata,\n  getOpenAiAgentsSdkDemoRunProfile,\n  getOpenAiAgentsSdkDemoRunRequest,\n} from \"./running\";\nimport {\n  getOpenAiAgentsSdkDemoSandboxRunConfig,\n  getOpenAiAgentsSdkDemoSandboxUsageMetadata,\n  recordOpenAiAgentsSdkDemoSandboxSessionState,\n} from \"./sandbox\";\nimport {\n  getOpenAiAgentsSdkDemoSession,\n  getOpenAiAgentsSdkDemoSessionUsageMetadata,\n} from \"./sessions\";\nimport { getOpenAiAgentsSdkDemoArtifactChunks } from \"./stream-artifacts\";\nimport {\n  createOpenAiAgentsSdkDemoStreamSummaryCollector,\n  observeOpenAiAgentsSdkDemoStreamEvents,\n} from \"./streaming\";\nimport { getOpenAiAgentsSdkDemoRunUsageMetadata } from \"./tools\";\nimport {\n  createOpenAiAgentsSdkDemoTraceRunConfig,\n  getOpenAiAgentsSdkDemoLatestTraceUsageMetadata,\n  getOpenAiAgentsSdkDemoTraceUsageMetadata,\n} from \"./tracing\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nfunction isLikelyImageGenerationPrompt(messages: UIMessage[]) {\n  const latestUserText = [...messages]\n    .reverse()\n    .find((message) => message.role === \"user\");\n\n  if (!latestUserText) {\n    return false;\n  }\n\n  const content = latestUserText.parts\n    .filter((part) => part.type === \"text\")\n    .map((part) => part.text)\n    .join(\"\\n\")\n    .trim()\n    .toLowerCase();\n\n  if (!content) {\n    return false;\n  }\n\n  return /生成.*图|图片|image|draw|illustration|artwork|poster/.test(content);\n}\n\nfunction configureOpenAiAgentsSdkDemoGatewayClient(env: DemoEnv) {\n  const modelProfile = getOpenAiAgentsSdkDemoModelProfile(env);\n\n  setOpenAIAPI(modelProfile.api);\n  setOpenAIResponsesTransport(modelProfile.responsesTransport);\n  setDefaultOpenAIClient(\n    new OpenAI({\n      apiKey: env.AI_GATEWAY_API_KEY,\n      baseURL: modelProfile.baseUrl,\n    })\n  );\n}\n\nasync function createDemoAgent(env: DemoEnv, origin: string) {\n  const modelProfile = getOpenAiAgentsSdkDemoModelProfile(env);\n\n  const mcpServers = await connectOpenAiAgentsSdkDemoMcpServers({\n    origin,\n  });\n\n  return {\n    agent: createOpenAiAgentsSdkDemoAgent({\n      env,\n      mcpServers: mcpServers.active,\n      modelProfile,\n    }),\n    mcpServers,\n  };\n}\n\ninterface DemoAgentStream extends AsyncIterable<RunStreamEvent> {\n  activeAgent?: {\n    name?: string;\n  };\n  completed: Promise<unknown>;\n  finalOutput?: unknown;\n  history?: unknown[];\n  inputGuardrailResults?: unknown[];\n  interruptions?: RunToolApprovalItem[];\n  lastAgent?: {\n    name?: string;\n  };\n  lastResponseId?: string;\n  newItems?: Parameters<\n    typeof getOpenAiAgentsSdkDemoArtifactChunks\n  >[0][\"newItems\"];\n  output?: unknown[];\n  outputGuardrailResults?: unknown[];\n  rawResponses?: Parameters<\n    typeof getOpenAiAgentsSdkDemoArtifactChunks\n  >[0][\"rawResponses\"];\n  state?: {\n    toJSON?: () => unknown;\n    usage?: {\n      inputTokens?: number;\n      outputTokens?: number;\n      requests?: number;\n      totalTokens?: number;\n    };\n  };\n}\n\nfunction createOpenAiAgentsSdkDemoUiMessageStream(\n  eventSource: Parameters<typeof createAiSdkUiMessageStream>[0],\n  agentStream: DemoAgentStream\n) {\n  const baseStream = createAiSdkUiMessageStream(eventSource);\n\n  return new ReadableStream<UIMessageChunk>({\n    async start(controller) {\n      const reader = baseStream.getReader();\n      let finishChunk: UIMessageChunk | null = null;\n\n      try {\n        while (true) {\n          const { done, value } = await reader.read();\n\n          if (done) {\n            break;\n          }\n\n          if (value.type === \"finish\") {\n            finishChunk = value;\n            continue;\n          }\n\n          controller.enqueue(value);\n        }\n\n        await agentStream.completed;\n\n        for (const chunk of getOpenAiAgentsSdkDemoArtifactChunks({\n          newItems: agentStream.newItems,\n          rawResponses: agentStream.rawResponses,\n        })) {\n          controller.enqueue(chunk);\n        }\n\n        controller.enqueue(\n          finishChunk ?? {\n            finishReason: \"stop\",\n            type: \"finish\",\n          }\n        );\n        controller.close();\n      } catch (error) {\n        controller.error(error);\n      } finally {\n        reader.releaseLock();\n      }\n    },\n    async cancel(reason) {\n      await baseStream.cancel(reason);\n    },\n  });\n}\n\nfunction mergeOpenAiAgentsSdkDemoMessageMetadata(\n  ...metadataEntries: Array<OpenAiAgentsSdkDemoMessageMetadata | undefined>\n): OpenAiAgentsSdkDemoMessageMetadata | undefined {\n  const aiSdkExtensionSummary = metadataEntries\n    .map((item) => item?.aiSdkExtensionSummary)\n    .find((value) => value);\n  const approvalSummary = metadataEntries\n    .map((item) => item?.approvalSummary)\n    .find((value) => value);\n  const contextSummary = metadataEntries\n    .map((item) => item?.contextSummary)\n    .find((value) => value);\n  const handoffSummary = metadataEntries\n    .map((item) => item?.handoffSummary)\n    .find((value) => value);\n  const lastResponseId = metadataEntries\n    .map((item) => item?.lastResponseId)\n    .filter((value): value is string => Boolean(value))\n    .at(-1);\n  const mcpSummary = metadataEntries\n    .map((item) => item?.mcpSummary)\n    .find((value) => value);\n  const resultSummary = metadataEntries\n    .map((item) => item?.resultSummary)\n    .find((value) => value);\n  const sandboxSummary = metadataEntries\n    .map((item) => item?.sandboxSummary)\n    .find((value) => value);\n  const sessionSummary = metadataEntries\n    .map((item) => item?.sessionSummary)\n    .find((value) => value);\n  const streamSummary = metadataEntries\n    .map((item) => item?.streamSummary)\n    .find((value) => value);\n  const traceSummary = metadataEntries\n    .map((item) => item?.traceSummary)\n    .find((value) => value);\n  const usedGuideIds = Array.from(\n    new Set(metadataEntries.flatMap((item) => item?.usedGuideIds ?? []))\n  );\n  const usedGuardrailNames = Array.from(\n    new Set(metadataEntries.flatMap((item) => item?.usedGuardrailNames ?? []))\n  );\n  const usedToolNames = Array.from(\n    new Set(metadataEntries.flatMap((item) => item?.usedToolNames ?? []))\n  );\n\n  if (\n    !(\n      aiSdkExtensionSummary ||\n      approvalSummary ||\n      contextSummary ||\n      handoffSummary ||\n      lastResponseId ||\n      mcpSummary ||\n      resultSummary ||\n      sandboxSummary ||\n      sessionSummary ||\n      streamSummary ||\n      traceSummary\n    ) &&\n    usedGuideIds.length === 0 &&\n    usedGuardrailNames.length === 0 &&\n    usedToolNames.length === 0\n  ) {\n    return;\n  }\n\n  return {\n    ...(aiSdkExtensionSummary ? { aiSdkExtensionSummary } : {}),\n    ...(approvalSummary ? { approvalSummary } : {}),\n    ...(contextSummary ? { contextSummary } : {}),\n    ...(handoffSummary ? { handoffSummary } : {}),\n    ...(lastResponseId ? { lastResponseId } : {}),\n    ...(mcpSummary ? { mcpSummary } : {}),\n    ...(resultSummary ? { resultSummary } : {}),\n    ...(sandboxSummary ? { sandboxSummary } : {}),\n    ...(sessionSummary ? { sessionSummary } : {}),\n    ...(streamSummary ? { streamSummary } : {}),\n    ...(traceSummary ? { traceSummary } : {}),\n    ...(usedGuideIds.length > 0 ? { usedGuideIds } : {}),\n    ...(usedGuardrailNames.length > 0 ? { usedGuardrailNames } : {}),\n    ...(usedToolNames.length > 0 ? { usedToolNames } : {}),\n  };\n}\n\nexport async function streamOpenAiAgentsSdkDemo(\n  messages: UIMessage[],\n  env: DemoEnv = process.env,\n  options?: {\n    origin?: string;\n    signal?: AbortSignal;\n  }\n) {\n  configureOpenAiAgentsSdkDemoGatewayClient(env);\n  const modelProfile = getOpenAiAgentsSdkDemoModelProfile(env);\n  const { agent, mcpServers } = await createDemoAgent(\n    env,\n    options?.origin ?? \"http://localhost:3000\"\n  );\n  let shouldCloseMcpServersImmediately = true;\n  const runProfile = getOpenAiAgentsSdkDemoRunProfile(env);\n  const session = await getOpenAiAgentsSdkDemoSession(messages);\n  const demoContext = await createOpenAiAgentsSdkDemoContext({\n    messages,\n    session,\n  });\n  const traceRunConfig = createOpenAiAgentsSdkDemoTraceRunConfig({\n    env,\n    sessionId: demoContext.sessionId,\n    workflowName: runProfile.workflowName,\n  });\n  const sandboxRunConfig = getOpenAiAgentsSdkDemoSandboxRunConfig({\n    sessionId: demoContext.sessionId,\n  });\n  const approvalResume = await getOpenAiAgentsSdkDemoApprovalResumeState(\n    messages,\n    agent\n  );\n\n  assertOpenAiAgentsSdkDemoNoPendingApproval(messages);\n\n  let agentStream: DemoAgentStream;\n\n  if (approvalResume) {\n    agentStream = (await run(agent, approvalResume.state, {\n      ...(options?.signal ? { signal: options.signal } : {}),\n      maxTurns: runProfile.maxTurns,\n      ...sandboxRunConfig,\n      session,\n      stream: true as const,\n    })) as DemoAgentStream;\n  } else {\n    const runRequest = getOpenAiAgentsSdkDemoRunRequest(\n      {\n        messages,\n        signal: options?.signal,\n      },\n      env\n    );\n\n    agentStream = (await run(agent, runRequest.input, {\n      context: demoContext,\n      ...runRequest.options,\n      ...sandboxRunConfig,\n      session,\n      ...traceRunConfig.options,\n    })) as DemoAgentStream;\n  }\n  const streamSummaryCollector =\n    createOpenAiAgentsSdkDemoStreamSummaryCollector();\n  const observedEventStream = observeOpenAiAgentsSdkDemoStreamEvents(\n    agentStream,\n    (event) => {\n      streamSummaryCollector.observe(event);\n    }\n  );\n  const uiMessageStream = createOpenAiAgentsSdkDemoUiMessageStream(\n    observedEventStream,\n    agentStream\n  );\n\n  try {\n    const response = createUIMessageStreamResponse({\n      stream: createUIMessageStream({\n        execute({ writer }) {\n          writer.merge(uiMessageStream);\n\n          return agentStream.completed\n            .then(() => {\n              const sessionMetadataPromise =\n                getOpenAiAgentsSdkDemoSessionUsageMetadata(session);\n\n              return sessionMetadataPromise.then((sessionMetadata) => {\n                recordOpenAiAgentsSdkDemoSandboxSessionState({\n                  sessionId: demoContext.sessionId,\n                  state: agentStream.state,\n                });\n\n                const metadata = mergeOpenAiAgentsSdkDemoMessageMetadata(\n                  getOpenAiAgentsSdkDemoRunningUsageMetadata(\n                    agentStream.lastResponseId\n                  ),\n                  getOpenAiAgentsSdkDemoApprovalUsageMetadata({\n                    interruptions: agentStream.interruptions ?? [],\n                    responses: approvalResume?.responses,\n                    state: agentStream.state,\n                  }),\n                  streamSummaryCollector.toMetadata(),\n                  getOpenAiAgentsSdkDemoHandoffUsageMetadata({\n                    activeAgentName: agentStream.lastAgent?.name,\n                    newItems: agentStream.newItems ?? [],\n                  }),\n                  getOpenAiAgentsSdkDemoResultUsageMetadata({\n                    activeAgentName: agentStream.activeAgent?.name,\n                    finalOutput: agentStream.finalOutput,\n                    hasResumableState: Boolean(agentStream.state),\n                    historyLength: agentStream.history?.length ?? 0,\n                    interruptionCount: agentStream.interruptions?.length ?? 0,\n                    lastAgentName: agentStream.lastAgent?.name,\n                    newItemsCount: agentStream.newItems?.length ?? 0,\n                    outputCount: agentStream.output?.length ?? 0,\n                    rawResponseCount: agentStream.rawResponses?.length ?? 0,\n                    usage: agentStream.state?.usage,\n                  }),\n                  getOpenAiAgentsSdkDemoContextUsageMetadata(demoContext),\n                  getOpenAiAgentsSdkDemoMcpUsageMetadata({\n                    mcpServers,\n                    newItems: agentStream.newItems ?? [],\n                  }),\n                  getOpenAiAgentsSdkDemoSandboxUsageMetadata({\n                    newItems: agentStream.newItems ?? [],\n                    sessionId: demoContext.sessionId,\n                    state: agentStream.state,\n                  }),\n                  getOpenAiAgentsSdkDemoAiSdkExtensionUsageMetadata(),\n                  sessionMetadata,\n                  approvalResume\n                    ? getOpenAiAgentsSdkDemoLatestTraceUsageMetadata(messages)\n                    : getOpenAiAgentsSdkDemoTraceUsageMetadata(\n                        traceRunConfig.summary\n                      ),\n                  getOpenAiAgentsSdkDemoRunUsageMetadata(\n                    agentStream.newItems ?? []\n                  ),\n                  getOpenAiAgentsSdkDemoGuardrailUsageMetadata({\n                    inputGuardrailResults: (agentStream.inputGuardrailResults ??\n                      []) as Parameters<\n                      typeof getOpenAiAgentsSdkDemoGuardrailUsageMetadata\n                    >[0][\"inputGuardrailResults\"],\n                    outputGuardrailResults:\n                      (agentStream.outputGuardrailResults ?? []) as Parameters<\n                        typeof getOpenAiAgentsSdkDemoGuardrailUsageMetadata\n                      >[0][\"outputGuardrailResults\"],\n                  })\n                );\n\n                if (!metadata) {\n                  return;\n                }\n\n                writer.write({\n                  messageMetadata: metadata,\n                  type: \"message-metadata\",\n                });\n              });\n            })\n            .finally(() => mcpServers.close());\n        },\n        onError(error) {\n          const approvalMessage =\n            getOpenAiAgentsSdkDemoApprovalErrorMessage(error);\n\n          if (approvalMessage) {\n            return approvalMessage;\n          }\n\n          const guardrailMessage =\n            getOpenAiAgentsSdkDemoGuardrailErrorMessage(error);\n\n          if (guardrailMessage) {\n            return guardrailMessage;\n          }\n\n          if (\n            error instanceof Error &&\n            error.message === \"terminated\" &&\n            isOpenAiAgentsSdkDemoImageGenerationProviderBlocked(modelProfile) &&\n            isLikelyImageGenerationPrompt(messages)\n          ) {\n            return \"The current AI Gateway Responses path registered imageGenerationTool(), but the upstream stream terminated before this demo received a renderable image artifact. In this provider configuration, image generation is blocked.\";\n          }\n\n          const providerErrorMessage =\n            getOpenAiAgentsSdkDemoProviderErrorMessage(error);\n\n          if (providerErrorMessage) {\n            return providerErrorMessage;\n          }\n\n          return error instanceof Error\n            ? error.message\n            : \"The agent stream failed.\";\n        },\n      }),\n    });\n\n    shouldCloseMcpServersImmediately = false;\n    return response;\n  } finally {\n    if (shouldCloseMcpServersImmediately) {\n      await mcpServers.close();\n    }\n  }\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/chat.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/context.ts",
      "content": "import type { MemorySession, RunContext } from \"@openai/agents\";\nimport type { UIMessage } from \"ai\";\nimport { z } from \"zod\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\n\nconst defaultResearchTarget = \"Tesla\";\nconst maxPromptPreviewLength = 120;\n\nexport const openAiAgentsSdkDemoContextSummarySchema = z.object({\n  defaultResearchTarget: z.literal(defaultResearchTarget),\n  latestUserPromptPreview: z.string(),\n  localContextKind: z.literal(\"RunContext\"),\n  researchMode: z.enum([\"company-analysis\", \"general\"]),\n  sessionId: z.string(),\n  sessionKind: z.literal(\"MemorySession\"),\n});\n\nexport interface OpenAiAgentsSdkDemoContextProfile {\n  localContextPrimitive: \"RunContext\";\n  passesContextInto: Array<\n    \"agent.asTool toolInput\" | \"dynamic instructions\" | \"guardrails\" | \"tool()\"\n  >;\n  sessionBinding: \"session-id\";\n  suggestedDefaultTarget: \"Tesla\";\n}\n\nexport interface OpenAiAgentsSdkDemoContext {\n  defaultResearchTarget: \"Tesla\";\n  latestUserPrompt: string;\n  latestUserPromptPreview: string;\n  researchMode: \"company-analysis\" | \"general\";\n  sessionId: string;\n  sessionKind: \"MemorySession\";\n}\n\nfunction getLatestUserPrompt(messages: UIMessage[]) {\n  return (\n    [...messages]\n      .reverse()\n      .find((message) => message.role === \"user\")\n      ?.parts.filter((part) => part.type === \"text\")\n      .map((part) => part.text)\n      .join(\"\\n\")\n      .trim() ?? \"\"\n  );\n}\n\nfunction getPromptPreview(text: string) {\n  if (text.length <= maxPromptPreviewLength) {\n    return text;\n  }\n\n  return `${text.slice(0, maxPromptPreviewLength - 1)}...`;\n}\n\nfunction inferResearchMode(\n  latestUserPrompt: string\n): OpenAiAgentsSdkDemoContext[\"researchMode\"] {\n  if (\n    /(company|stock|equity|earnings|valuation|financial|10-k|10q|tesla|分析|财务|投研|公司|研究|估值)/i.test(\n      latestUserPrompt\n    )\n  ) {\n    return \"company-analysis\";\n  }\n\n  return \"general\";\n}\n\nexport async function createOpenAiAgentsSdkDemoContext({\n  messages,\n  session,\n}: {\n  messages: UIMessage[];\n  session: Pick<MemorySession, \"getSessionId\">;\n}): Promise<OpenAiAgentsSdkDemoContext> {\n  const latestUserPrompt = getLatestUserPrompt(messages);\n\n  return {\n    defaultResearchTarget,\n    latestUserPrompt,\n    latestUserPromptPreview: getPromptPreview(latestUserPrompt),\n    researchMode: inferResearchMode(latestUserPrompt),\n    sessionId: await session.getSessionId(),\n    sessionKind: \"MemorySession\",\n  };\n}\n\nexport function getOpenAiAgentsSdkDemoContextProfile(): OpenAiAgentsSdkDemoContextProfile {\n  return {\n    localContextPrimitive: \"RunContext\",\n    passesContextInto: [\n      \"dynamic instructions\",\n      \"tool()\",\n      \"guardrails\",\n      \"agent.asTool toolInput\",\n    ],\n    sessionBinding: \"session-id\",\n    suggestedDefaultTarget: \"Tesla\",\n  };\n}\n\nexport function getOpenAiAgentsSdkDemoContextInstructions(\n  runContext: RunContext<OpenAiAgentsSdkDemoContext>,\n  options: {\n    isImageGenerationProviderBlocked: boolean;\n  }\n) {\n  const context = runContext.context;\n  const instructionLines = [\n    `Current demo run mode: ${context.researchMode}.`,\n    context.researchMode === \"company-analysis\"\n      ? `If the user requests company research without naming a company, default to ${context.defaultResearchTarget}.`\n      : \"Keep the answer general unless the user explicitly asks for company research.\",\n  ];\n\n  if (options.isImageGenerationProviderBlocked) {\n    instructionLines.push(\n      \"The current AI Gateway Responses path cannot return a renderable image_generation artifact to this chat surface. For image-generation requests, explain that the official hosted tool is currently provider-blocked in this demo instead of claiming success.\"\n    );\n  }\n\n  return instructionLines.join(\" \");\n}\n\nexport function getOpenAiAgentsSdkDemoToolContextNote(\n  runContext?: RunContext<OpenAiAgentsSdkDemoContext>\n) {\n  const context = runContext?.context;\n\n  if (!context) {\n    return null;\n  }\n\n  return `Run context: ${context.researchMode}; default research target when unspecified: ${context.defaultResearchTarget}.`;\n}\n\nexport function getOpenAiAgentsSdkDemoContextUsageMetadata(\n  context: OpenAiAgentsSdkDemoContext\n) {\n  return {\n    contextSummary: {\n      defaultResearchTarget: context.defaultResearchTarget,\n      latestUserPromptPreview: context.latestUserPromptPreview,\n      localContextKind: \"RunContext\",\n      researchMode: context.researchMode,\n      sessionId: context.sessionId,\n      sessionKind: context.sessionKind,\n    },\n    usedGuideIds: [\"context\"],\n  } satisfies OpenAiAgentsSdkDemoMessageMetadata;\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/context.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/demo-mcp-server.ts",
      "content": "import { existsSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n// biome-ignore lint/correctness/noUnresolvedImports: the MCP SDK wildcard export requires the .js subpath at runtime.\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n// biome-ignore lint/correctness/noUnresolvedImports: the MCP SDK wildcard export requires the .js subpath at runtime.\nimport { WebStandardStreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport { z } from \"zod\";\n\nimport {\n  OPENAI_AGENTS_SDK_DEMO_MCP_SERVER_NAME,\n  openAiAgentsSdkDemoMcpToolNames,\n} from \"./mcp\";\n\nconst demoDocIds = [\"feature-readme\", \"frontend-doc\"] as const;\n\nfunction firstExistingPath(paths: readonly string[], label: string) {\n  const resolvedPath = paths.find((candidate) => existsSync(candidate));\n\n  if (!resolvedPath) {\n    throw new Error(\n      `Could not resolve the OpenAI Agents SDK demo ${label} document from cwd ${process.cwd()}.`\n    );\n  }\n\n  return resolvedPath;\n}\n\nfunction getDemoDocumentPaths() {\n  return {\n    featureReadme: firstExistingPath(\n      [\n        path.join(process.cwd(), \"lib/openai-agents-sdk-demo/README.md\"),\n        path.join(process.cwd(), \"src/lib/openai-agents-sdk-demo/README.md\"),\n      ],\n      \"README\"\n    ),\n    frontendDoc: firstExistingPath(\n      [\n        path.join(process.cwd(), \"docs/frontend/openai-agents-sdk-demo.md\"),\n        path.join(process.cwd(), \"src/docs/frontend/openai-agents-sdk-demo.md\"),\n      ],\n      \"frontend docs\"\n    ),\n  };\n}\n\nfunction jsonText(value: unknown) {\n  return {\n    content: [\n      {\n        text: JSON.stringify(value, null, 2),\n        type: \"text\" as const,\n      },\n    ],\n  };\n}\n\nasync function getDemoDocSources() {\n  const paths = getDemoDocumentPaths();\n\n  return [\n    {\n      id: \"feature-readme\" as const,\n      label: \"lib/openai-agents-sdk-demo/README.md\",\n      text: await readFile(paths.featureReadme, \"utf8\"),\n    },\n    {\n      id: \"frontend-doc\" as const,\n      label: \"docs/frontend/openai-agents-sdk-demo.md\",\n      text: await readFile(paths.frontendDoc, \"utf8\"),\n    },\n  ];\n}\n\nasync function readDemoDoc(document: (typeof demoDocIds)[number]) {\n  const sources = await getDemoDocSources();\n  const source = sources.find((item) => item.id === document);\n\n  if (!source) {\n    throw new Error(`Unknown demo document: ${document}`);\n  }\n\n  return {\n    document: source.id,\n    label: source.label,\n    text: source.text,\n  };\n}\n\nasync function searchDemoDocs({\n  limit = 6,\n  query,\n}: {\n  limit?: number;\n  query: string;\n}) {\n  const normalizedQuery = query.trim().toLowerCase();\n\n  if (!normalizedQuery) {\n    return [];\n  }\n\n  const sources = await getDemoDocSources();\n  const matches: Array<{\n    document: string;\n    line: number;\n    text: string;\n  }> = [];\n\n  for (const source of sources) {\n    const lines = source.text.split(\"\\n\");\n\n    for (let index = 0; index < lines.length; index += 1) {\n      const lineText = lines[index]?.trim() ?? \"\";\n\n      if (!lineText.toLowerCase().includes(normalizedQuery)) {\n        continue;\n      }\n\n      matches.push({\n        document: source.label,\n        line: index + 1,\n        text: lineText,\n      });\n\n      if (matches.length >= limit) {\n        return matches;\n      }\n    }\n  }\n\n  return matches;\n}\n\nexport function createOpenAiAgentsSdkDemoMcpServer() {\n  const server = new McpServer(\n    {\n      name: OPENAI_AGENTS_SDK_DEMO_MCP_SERVER_NAME,\n      title: \"OpenAI Agents SDK Demo Docs\",\n      version: \"0.1.0\",\n    },\n    {\n      instructions:\n        \"Use these tools when the user asks about this demo's README, durable frontend doc, setup contract, or official guide coverage notes.\",\n    }\n  );\n\n  server.registerTool(\n    openAiAgentsSdkDemoMcpToolNames[0],\n    {\n      description:\n        \"Read one durable document for the OpenAI Agents SDK demo feature slice.\",\n      inputSchema: {\n        document: z.enum(demoDocIds),\n      },\n    },\n    async ({ document }) => jsonText(await readDemoDoc(document))\n  );\n\n  server.registerTool(\n    openAiAgentsSdkDemoMcpToolNames[1],\n    {\n      description:\n        \"Search the OpenAI Agents SDK demo README and durable frontend doc for line-level matches.\",\n      inputSchema: {\n        limit: z.number().int().min(1).max(20).optional(),\n        query: z.string().min(1),\n      },\n    },\n    async ({ limit, query }) => jsonText(await searchDemoDocs({ limit, query }))\n  );\n\n  return server;\n}\n\nexport async function handleOpenAiAgentsSdkDemoMcpRequest(request: Request) {\n  const server = createOpenAiAgentsSdkDemoMcpServer();\n  const transport = new WebStandardStreamableHTTPServerTransport({\n    enableJsonResponse: true,\n    sessionIdGenerator: undefined,\n  });\n\n  await server.connect(transport);\n\n  try {\n    return await transport.handleRequest(request);\n  } finally {\n    await server.close();\n  }\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/demo-mcp-server.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/extensions.ts",
      "content": "import { z } from \"zod\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\n\nexport const openAiAgentsSdkDemoAiSdkExtensionSummarySchema = z.object({\n  modelAdapterStatus: z.literal(\"not-used\"),\n  uiBridgeStatus: z.literal(\"configured\"),\n  usedBridgePrimitive: z.literal(\"createAiSdkUiMessageStream()\"),\n});\n\nexport interface OpenAiAgentsSdkDemoAiSdkExtensionProfile {\n  modelAdapter: {\n    notes: string;\n    sdkPrimitive: \"aisdk(model)\";\n    status: \"not-used\";\n  };\n  uiBridge: {\n    responseHelper: \"createAiSdkUiMessageStreamResponse()\";\n    sdkPrimitive: \"createAiSdkUiMessageStream()\";\n    status: \"configured\";\n  };\n}\n\nexport function getOpenAiAgentsSdkDemoAiSdkExtensionProfile(): OpenAiAgentsSdkDemoAiSdkExtensionProfile {\n  return {\n    modelAdapter: {\n      notes:\n        \"Official docs recommend the default OpenAI provider for OpenAI models, and the beta aisdk() model adapter does not support deferred Responses tool loading.\",\n      sdkPrimitive: \"aisdk(model)\",\n      status: \"not-used\",\n    },\n    uiBridge: {\n      responseHelper: \"createAiSdkUiMessageStreamResponse()\",\n      sdkPrimitive: \"createAiSdkUiMessageStream()\",\n      status: \"configured\",\n    },\n  };\n}\n\nexport function getOpenAiAgentsSdkDemoAiSdkExtensionUsageMetadata() {\n  return {\n    aiSdkExtensionSummary: {\n      modelAdapterStatus: \"not-used\",\n      uiBridgeStatus: \"configured\",\n      usedBridgePrimitive: \"createAiSdkUiMessageStream()\",\n    },\n    usedGuideIds: [\"extensions-ai-sdk\"],\n  } satisfies OpenAiAgentsSdkDemoMessageMetadata;\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/extensions.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/guardrails.ts",
      "content": "import {\n  type InputGuardrail,\n  type InputGuardrailResult,\n  InputGuardrailTripwireTriggered,\n  type OutputGuardrail,\n  type OutputGuardrailResult,\n  OutputGuardrailTripwireTriggered,\n  type RunContext,\n} from \"@openai/agents\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\nimport type { OpenAiAgentsSdkDemoContext } from \"./context\";\n\nexport type OpenAiAgentsSdkDemoGuardrailAvailability =\n  | \"configured\"\n  | \"setup-required\";\n\nexport type OpenAiAgentsSdkDemoGuardrailKind = \"input\" | \"output\";\n\nexport interface OpenAiAgentsSdkDemoGuardrailCatalogEntry {\n  availability: OpenAiAgentsSdkDemoGuardrailAvailability;\n  kind: OpenAiAgentsSdkDemoGuardrailKind;\n  name: string;\n  notes: string;\n  sdkPrimitive: string;\n}\n\ninterface OpenAiAgentsSdkDemoGuardrailCatalogOptions {\n  isChatAvailable: boolean;\n}\n\nconst promptScopeGuardrail: InputGuardrail = {\n  execute: async ({ input, context }) => {\n    const normalizedInput =\n      typeof input === \"string\" ? input : JSON.stringify(input);\n    const shouldBlock =\n      /(ignore\\s+previous\\s+instructions|reveal\\s+.*system\\s+prompt|bypass\\s+guardrail)/i.test(\n        normalizedInput\n      );\n    const demoContext = (context as RunContext<OpenAiAgentsSdkDemoContext>)\n      .context;\n\n    return {\n      outputInfo: {\n        matchedPolicy: shouldBlock ? \"prompt-injection-or-system-prompt\" : null,\n        researchMode: demoContext?.researchMode,\n        sessionId: demoContext?.sessionId,\n      },\n      tripwireTriggered: shouldBlock,\n    };\n  },\n  name: \"prompt_scope_guardrail\",\n  runInParallel: false,\n};\n\nconst investmentAdviceGuardrail: OutputGuardrail = {\n  execute: async ({ agentOutput, context }) => {\n    const normalizedOutput =\n      typeof agentOutput === \"string\"\n        ? agentOutput\n        : JSON.stringify(agentOutput);\n    const shouldBlock =\n      /(buy recommendation|sell recommendation|strong buy|strong sell|强烈买入|强烈卖出|买入建议|卖出建议|推荐买入|推荐卖出)/i.test(\n        normalizedOutput\n      );\n    const demoContext = (context as RunContext<OpenAiAgentsSdkDemoContext>)\n      .context;\n\n    return {\n      outputInfo: {\n        matchedPolicy: shouldBlock ? \"direct-investment-recommendation\" : null,\n        researchMode: demoContext?.researchMode,\n        sessionId: demoContext?.sessionId,\n      },\n      tripwireTriggered: shouldBlock,\n    };\n  },\n  name: \"investment_advice_guardrail\",\n};\n\nfunction isInputGuardrailTripwire(\n  error: unknown\n): error is InputGuardrailTripwireTriggered {\n  return (\n    error instanceof InputGuardrailTripwireTriggered ||\n    (typeof error === \"object\" &&\n      error !== null &&\n      \"result\" in error &&\n      \"constructor\" in error &&\n      (error as { constructor?: { name?: string } }).constructor?.name ===\n        \"InputGuardrailTripwireTriggered\")\n  );\n}\n\nfunction isOutputGuardrailTripwire(\n  error: unknown\n): error is OutputGuardrailTripwireTriggered<any, any> {\n  return (\n    error instanceof OutputGuardrailTripwireTriggered ||\n    (typeof error === \"object\" &&\n      error !== null &&\n      \"result\" in error &&\n      \"constructor\" in error &&\n      (error as { constructor?: { name?: string } }).constructor?.name ===\n        \"OutputGuardrailTripwireTriggered\")\n  );\n}\n\nexport function getOpenAiAgentsSdkDemoGuardrails() {\n  return {\n    inputGuardrails: [promptScopeGuardrail],\n    outputGuardrails: [investmentAdviceGuardrail],\n  };\n}\n\nexport function getOpenAiAgentsSdkDemoGuardrailCatalog({\n  isChatAvailable,\n}: OpenAiAgentsSdkDemoGuardrailCatalogOptions): OpenAiAgentsSdkDemoGuardrailCatalogEntry[] {\n  const availability: OpenAiAgentsSdkDemoGuardrailAvailability = isChatAvailable\n    ? \"configured\"\n    : \"setup-required\";\n\n  return [\n    {\n      availability,\n      kind: \"input\",\n      name: \"prompt_scope_guardrail\",\n      notes:\n        \"Blocks direct prompt-injection or system-prompt extraction requests before the run proceeds.\",\n      sdkPrimitive: \"defineInputGuardrail()\",\n    },\n    {\n      availability,\n      kind: \"output\",\n      name: \"investment_advice_guardrail\",\n      notes:\n        \"Blocks direct buy or sell recommendation phrasing in the final agent output.\",\n      sdkPrimitive: \"defineOutputGuardrail()\",\n    },\n  ];\n}\n\nexport function getOpenAiAgentsSdkDemoGuardrailUsageMetadata({\n  inputGuardrailResults,\n  outputGuardrailResults,\n}: {\n  inputGuardrailResults: InputGuardrailResult[];\n  outputGuardrailResults: OutputGuardrailResult[];\n}): OpenAiAgentsSdkDemoMessageMetadata | undefined {\n  const usedGuardrailNames = Array.from(\n    new Set([\n      ...inputGuardrailResults.map((result) => result.guardrail.name),\n      ...outputGuardrailResults.map((result) => result.guardrail.name),\n    ])\n  );\n\n  if (usedGuardrailNames.length === 0) {\n    return;\n  }\n\n  return {\n    usedGuardrailNames,\n    usedGuideIds: [\"guardrails\"],\n  };\n}\n\nexport function getOpenAiAgentsSdkDemoGuardrailErrorMessage(error: unknown) {\n  if (isInputGuardrailTripwire(error)) {\n    return `Input guardrail \"${error.result.guardrail.name}\" blocked the request. This demo rejects prompt-injection and system-prompt extraction attempts.`;\n  }\n\n  if (isOutputGuardrailTripwire(error)) {\n    return `Output guardrail \"${error.result.guardrail.name}\" blocked the response. This demo can analyze evidence but will not emit direct buy or sell recommendations.`;\n  }\n\n  return null;\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/guardrails.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/guide-coverage.ts",
      "content": "export type OpenAiAgentsSdkGuideImplementationStatus =\n  | \"blocked\"\n  | \"implemented\"\n  | \"not-started\";\n\nexport type OpenAiAgentsSdkGuideRunStatus =\n  | \"blocked\"\n  | \"not-started\"\n  | \"ready\"\n  | \"used-this-run\";\n\nexport type OpenAiAgentsSdkProviderCapabilityStatus =\n  | \"available\"\n  | \"not-evaluated\"\n  | \"setup-required\";\n\nexport interface OpenAiAgentsSdkDemoGuideCoverage {\n  currentRunStatus: OpenAiAgentsSdkGuideRunStatus;\n  id: string;\n  implementationStatus: OpenAiAgentsSdkGuideImplementationStatus;\n  label: string;\n  observable: string;\n  providerCapabilityStatus: OpenAiAgentsSdkProviderCapabilityStatus;\n  sdkPrimitive: string;\n  sourceGuide: string;\n}\n\ninterface OpenAiAgentsSdkDemoGuideCoverageOptions {\n  isChatAvailable: boolean;\n  isVoiceProviderAvailable: boolean;\n}\n\nconst openAiAgentsSdkGuideCoverageRegistry: OpenAiAgentsSdkDemoGuideCoverage[] =\n  [\n    {\n      currentRunStatus: \"ready\",\n      id: \"agents\",\n      implementationStatus: \"implemented\",\n      label: \"Agents\",\n      observable: \"Agent instance passed to run()\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"Agent\",\n      sourceGuide: \"https://openai.github.io/openai-agents-js/guides/agents/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"models\",\n      implementationStatus: \"implemented\",\n      label: \"Models\",\n      observable:\n        \"Agent modelSettings.reasoning + text.verbosity on the responses API path\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"model, modelSettings\",\n      sourceGuide: \"https://openai.github.io/openai-agents-js/guides/models/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"tools\",\n      implementationStatus: \"implemented\",\n      label: \"Tools\",\n      observable: \"RunToolCallItem / RunToolCallOutputItem\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"tool()\",\n      sourceGuide: \"https://openai.github.io/openai-agents-js/guides/tools/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"guardrails\",\n      implementationStatus: \"implemented\",\n      label: \"Guardrails\",\n      observable: \"Guardrail tripwire result\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"inputGuardrails / outputGuardrails\",\n      sourceGuide:\n        \"https://openai.github.io/openai-agents-js/guides/guardrails/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"running-agents\",\n      implementationStatus: \"implemented\",\n      label: \"Running Agents\",\n      observable:\n        \"run() + previousResponseId / MemorySession continuation + maxTurns + AbortSignal\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"run()\",\n      sourceGuide:\n        \"https://openai.github.io/openai-agents-js/guides/running-agents/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"streaming\",\n      implementationStatus: \"implemented\",\n      label: \"Streaming\",\n      observable:\n        \"RunStreamEvent metadata from raw_model_stream_event, run_item_stream_event, and agent_updated_stream_event\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"RunStreamEvent\",\n      sourceGuide:\n        \"https://openai.github.io/openai-agents-js/guides/streaming/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"agent-orchestration\",\n      implementationStatus: \"implemented\",\n      label: \"Agent Orchestration\",\n      observable: \"agent.asTool() invocation through a specialist sub-agent\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"agent.asTool()\",\n      sourceGuide:\n        \"https://openai.github.io/openai-agents-js/guides/multi-agent/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"handoffs\",\n      implementationStatus: \"implemented\",\n      label: \"Handoffs\",\n      observable: \"RunHandoffCallItem / RunHandoffOutputItem\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"handoff()\",\n      sourceGuide: \"https://openai.github.io/openai-agents-js/guides/handoffs/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"results\",\n      implementationStatus: \"implemented\",\n      label: \"Results\",\n      observable: \"finalOutput, history, newItems, state\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"RunResult\",\n      sourceGuide: \"https://openai.github.io/openai-agents-js/guides/results/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"human-in-the-loop\",\n      implementationStatus: \"implemented\",\n      label: \"Human-in-the-loop\",\n      observable: \"RunToolApprovalItem interruption\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"interruptions / approval\",\n      sourceGuide:\n        \"https://openai.github.io/openai-agents-js/guides/human-in-the-loop/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"sessions\",\n      implementationStatus: \"implemented\",\n      label: \"Sessions\",\n      observable: \"MemorySession history + assistant metadata session id\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"MemorySession\",\n      sourceGuide: \"https://openai.github.io/openai-agents-js/guides/sessions/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"context\",\n      implementationStatus: \"implemented\",\n      label: \"Context Management\",\n      observable:\n        \"RunContext through run(), dynamic instructions, tools, and guardrails\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"RunContext<T>\",\n      sourceGuide: \"https://openai.github.io/openai-agents-js/guides/context/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"mcp\",\n      implementationStatus: \"implemented\",\n      label: \"MCP\",\n      observable:\n        \"MCP server connection state + server-prefixed MCP tool call items\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"MCPServerStreamableHttp / connectMcpServers\",\n      sourceGuide: \"https://openai.github.io/openai-agents-js/guides/mcp/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"tracing\",\n      implementationStatus: \"implemented\",\n      label: \"Tracing\",\n      observable:\n        \"workflowName + traceId + groupId + traceMetadata + tracingDisabled\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"traceId / groupId / RunConfig.tracing\",\n      sourceGuide: \"https://openai.github.io/openai-agents-js/guides/tracing/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"sandbox-agents\",\n      implementationStatus: \"implemented\",\n      label: \"Sandbox Agents\",\n      observable:\n        \"SandboxAgent lifecycle + RunConfig.sandbox + persisted sandbox session state\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"SandboxAgent / RunConfig.sandbox\",\n      sourceGuide:\n        \"https://openai.github.io/openai-agents-js/guides/sandbox-agents/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"voice-agents\",\n      implementationStatus: \"implemented\",\n      label: \"Voice Agents\",\n      observable:\n        \"RealtimeSession.connect({ apiKey }) over WebRTC plus visible browser microphone controls\",\n      providerCapabilityStatus: \"setup-required\",\n      sdkPrimitive: \"RealtimeAgent / RealtimeSession\",\n      sourceGuide:\n        \"https://openai.github.io/openai-agents-js/guides/voice-agents/\",\n    },\n    {\n      currentRunStatus: \"ready\",\n      id: \"extensions-ai-sdk\",\n      implementationStatus: \"implemented\",\n      label: \"AI SDK Extension\",\n      observable:\n        \"AI SDK UI stream from createAiSdkUiMessageStream(); aisdk(model) adapter boundary is explicit\",\n      providerCapabilityStatus: \"available\",\n      sdkPrimitive: \"createAiSdkUiMessageStream() / aisdk(model)\",\n      sourceGuide:\n        \"https://openai.github.io/openai-agents-js/extensions/ai-sdk/\",\n    },\n  ];\n\nconst openAiAgentsSdkChatRunGatedGuideIds = new Set([\n  \"agents\",\n  \"models\",\n  \"tools\",\n  \"guardrails\",\n  \"running-agents\",\n  \"streaming\",\n  \"agent-orchestration\",\n  \"handoffs\",\n  \"results\",\n  \"human-in-the-loop\",\n  \"sessions\",\n  \"context\",\n  \"mcp\",\n  \"tracing\",\n  \"sandbox-agents\",\n  \"extensions-ai-sdk\",\n]);\n\nexport function getOpenAiAgentsSdkDemoGuideCoverage({\n  isChatAvailable,\n  isVoiceProviderAvailable,\n}: OpenAiAgentsSdkDemoGuideCoverageOptions): OpenAiAgentsSdkDemoGuideCoverage[] {\n  return openAiAgentsSdkGuideCoverageRegistry.map((item) => {\n    if (item.id === \"voice-agents\") {\n      return {\n        ...item,\n        currentRunStatus: isVoiceProviderAvailable ? \"ready\" : \"blocked\",\n        providerCapabilityStatus: isVoiceProviderAvailable\n          ? \"available\"\n          : \"setup-required\",\n      };\n    }\n\n    if (openAiAgentsSdkChatRunGatedGuideIds.has(item.id)) {\n      return {\n        ...item,\n        currentRunStatus: isChatAvailable ? \"ready\" : \"blocked\",\n        providerCapabilityStatus: isChatAvailable\n          ? \"available\"\n          : \"setup-required\",\n      };\n    }\n\n    return item;\n  });\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/guide-coverage.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/handoff-extensions.ts",
      "content": "import type { RunContext, RunItem } from \"@openai/agents\";\n\nconst TOOL_ITEM_TYPES = new Set([\n  \"apply_patch_call\",\n  \"apply_patch_call_output\",\n  \"computer_call\",\n  \"computer_call_result\",\n  \"function_call\",\n  \"function_call_result\",\n  \"handoff_call_item\",\n  \"handoff_output_item\",\n  \"hosted_tool_call\",\n  \"reasoning\",\n  \"shell_call\",\n  \"shell_call_output\",\n  \"tool_search_call\",\n  \"tool_search_output\",\n]);\n\nexport const RECOMMENDED_PROMPT_PREFIX = `# System context\nYou are part of a multi-agent system called the Agents SDK, designed to make agent coordination and execution easy. Agents uses two primary abstractions: **Agents** and **Handoffs**. An agent encompasses instructions and tools and can hand off a conversation to another agent when appropriate. Handoffs are achieved by calling a handoff function, generally named \\`transfer_to_<agent_name>\\`. Transfers between agents are handled seamlessly in the background; do not mention or draw attention to these transfers in your conversation with the user.`;\n\nexport function promptWithHandoffInstructions(prompt: string) {\n  return `${RECOMMENDED_PROMPT_PREFIX}\\n\\n${prompt}`;\n}\n\ninterface OpenAiAgentsSdkDemoHandoffInputData {\n  inputHistory: string | Array<{ type?: string }>;\n  newItems: RunItem[];\n  preHandoffItems: RunItem[];\n  runContext?: RunContext<any>;\n}\n\nfunction removeToolTypesFromInput<TInputItem extends { type?: string }>(\n  items: TInputItem[]\n) {\n  return items.filter((item) => !TOOL_ITEM_TYPES.has(item.type ?? \"\"));\n}\n\nfunction removeToolsFromItems(items: RunItem[]) {\n  return items.filter(\n    (item) =>\n      !TOOL_ITEM_TYPES.has(\n        ((item.rawItem ?? {}) as { type?: string }).type ?? \"\"\n      )\n  );\n}\n\nexport function removeAllTools<TInputItem extends { type?: string }>(\n  handoffInputData: Omit<\n    OpenAiAgentsSdkDemoHandoffInputData,\n    \"inputHistory\"\n  > & {\n    inputHistory: string | TInputItem[];\n  }\n) {\n  const { inputHistory, preHandoffItems, newItems, runContext } =\n    handoffInputData;\n\n  return {\n    inputHistory: Array.isArray(inputHistory)\n      ? removeToolTypesFromInput(inputHistory)\n      : inputHistory,\n    newItems: removeToolsFromItems(newItems),\n    preHandoffItems: removeToolsFromItems(preHandoffItems),\n    runContext,\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/handoff-extensions.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/handoffs.ts",
      "content": "import { Agent, handoff, type RunItem } from \"@openai/agents\";\nimport { z } from \"zod\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\nimport {\n  promptWithHandoffInstructions,\n  removeAllTools,\n} from \"./handoff-extensions\";\nimport type { OpenAiAgentsSdkDemoModelProfile } from \"./models\";\n\nexport type OpenAiAgentsSdkDemoHandoffAvailability =\n  | \"configured\"\n  | \"setup-required\";\n\nexport type OpenAiAgentsSdkDemoHandoffKind = \"agent\" | \"handoff\";\n\nexport interface OpenAiAgentsSdkDemoHandoffCatalogEntry {\n  availability: OpenAiAgentsSdkDemoHandoffAvailability;\n  kind: OpenAiAgentsSdkDemoHandoffKind;\n  name: string;\n  notes: string;\n  sdkPrimitive: string;\n}\n\ninterface OpenAiAgentsSdkDemoHandoffCatalogOptions {\n  isChatAvailable: boolean;\n}\n\ninterface OpenAiAgentsSdkDemoHandoffOptions {\n  modelProfile: OpenAiAgentsSdkDemoModelProfile;\n}\n\nconst researchLeadHandoffInput = z.object({\n  reason: z.string().min(1),\n});\n\nfunction createMarketContextAgent({\n  modelProfile,\n}: OpenAiAgentsSdkDemoHandoffOptions) {\n  return new Agent({\n    handoffDescription:\n      \"Use this specialist when the user needs direct market context, company history, or competitive framing.\",\n    instructions: promptWithHandoffInstructions(\n      \"You take over the conversation when the user needs market context, business history, or competitor framing for a public-company research task. Keep the answer concise, evidence-first, and scoped to the live request.\"\n    ),\n    model: modelProfile.model,\n    modelSettings: {\n      reasoning: {\n        effort: modelProfile.reasoningEffort,\n      },\n      text: {\n        verbosity: modelProfile.textVerbosity,\n      },\n    },\n    name: \"Market Context Agent\",\n  });\n}\n\nfunction createResearchLeadAgent({\n  modelProfile,\n}: OpenAiAgentsSdkDemoHandoffOptions) {\n  return new Agent({\n    instructions: promptWithHandoffInstructions(\n      \"You take over the conversation when the user wants a direct long-form research synthesis or a next-step research plan. Keep the answer structured, cite live evidence where needed, and close with the most decision-relevant open questions.\"\n    ),\n    model: modelProfile.model,\n    modelSettings: {\n      reasoning: {\n        effort: modelProfile.reasoningEffort,\n      },\n      text: {\n        verbosity: modelProfile.textVerbosity,\n      },\n    },\n    name: \"Research Lead Agent\",\n  });\n}\n\nexport function createOpenAiAgentsSdkDemoHandoffs({\n  modelProfile,\n}: OpenAiAgentsSdkDemoHandoffOptions) {\n  const marketContextAgent = createMarketContextAgent({\n    modelProfile,\n  });\n  const researchLeadAgent = createResearchLeadAgent({\n    modelProfile,\n  });\n\n  return [\n    marketContextAgent,\n    handoff(researchLeadAgent, {\n      inputFilter: removeAllTools,\n      inputType: researchLeadHandoffInput,\n      onHandoff(_context, input) {\n        void input;\n      },\n      toolDescriptionOverride:\n        \"Transfer to the research lead when the specialist should answer directly with a research synthesis or next-step plan.\",\n      toolNameOverride: \"transfer_to_research_lead\",\n    }),\n  ];\n}\n\nexport function getOpenAiAgentsSdkDemoHandoffCatalog({\n  isChatAvailable,\n}: OpenAiAgentsSdkDemoHandoffCatalogOptions): OpenAiAgentsSdkDemoHandoffCatalogEntry[] {\n  const availability: OpenAiAgentsSdkDemoHandoffAvailability = isChatAvailable\n    ? \"configured\"\n    : \"setup-required\";\n\n  return [\n    {\n      availability,\n      kind: \"agent\",\n      name: \"Market Context Agent\",\n      notes:\n        \"Direct specialist agent passed in the handoffs array, using the agent.handoffDescription path from the official guide.\",\n      sdkPrimitive: \"handoffs: [agent]\",\n    },\n    {\n      availability,\n      kind: \"handoff\",\n      name: \"Research Lead handoff\",\n      notes:\n        \"Explicit handoff() object with inputType, onHandoff, and removeAllTools inputFilter, following the official guide examples.\",\n      sdkPrimitive: \"handoff()\",\n    },\n  ];\n}\n\nfunction getHandoffTargetName(rawItem: {\n  agent?: { name?: string };\n  type?: string;\n}) {\n  if (rawItem.type !== \"handoff_call_item\") {\n    return null;\n  }\n\n  return rawItem.agent?.name ?? null;\n}\n\nfunction getHandoffTransition(rawItem: {\n  sourceAgent?: { name?: string };\n  targetAgent?: { name?: string };\n  type?: string;\n}) {\n  if (rawItem.type !== \"handoff_output_item\") {\n    return null;\n  }\n\n  if (!(rawItem.sourceAgent?.name && rawItem.targetAgent?.name)) {\n    return null;\n  }\n\n  return `${rawItem.sourceAgent.name} -> ${rawItem.targetAgent.name}`;\n}\n\nexport function getOpenAiAgentsSdkDemoHandoffUsageMetadata({\n  activeAgentName,\n  newItems,\n}: {\n  activeAgentName?: string;\n  newItems: RunItem[];\n}): OpenAiAgentsSdkDemoMessageMetadata | undefined {\n  const handoffTargetNames = Array.from(\n    new Set(\n      newItems\n        .map((item) =>\n          getHandoffTargetName(\n            (item.rawItem ?? {}) as { agent?: { name?: string }; type?: string }\n          )\n        )\n        .filter((value): value is string => Boolean(value))\n    )\n  );\n  const handoffTransitions = Array.from(\n    new Set(\n      newItems\n        .map((item) =>\n          getHandoffTransition(\n            (item.rawItem ?? {}) as {\n              sourceAgent?: { name?: string };\n              targetAgent?: { name?: string };\n              type?: string;\n            }\n          )\n        )\n        .filter((value): value is string => Boolean(value))\n    )\n  );\n\n  if (handoffTargetNames.length === 0 && handoffTransitions.length === 0) {\n    return;\n  }\n\n  return {\n    handoffSummary: {\n      ...(activeAgentName ? { activeAgentName } : {}),\n      handoffTargetNames,\n      handoffTransitions,\n    },\n    usedGuideIds: [\"handoffs\"],\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/handoffs.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/mcp.ts",
      "content": "import {\n  connectMcpServers,\n  createMCPToolStaticFilter,\n  MCPServerStreamableHttp,\n  type MCPServers,\n  type RunItem,\n} from \"@openai/agents\";\nimport { z } from \"zod\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\n\nexport const OPENAI_AGENTS_SDK_DEMO_MCP_ROUTE_PATH =\n  \"/api/demos/openai-agents-sdk-demo/mcp\";\nexport const OPENAI_AGENTS_SDK_DEMO_MCP_SERVER_NAME = \"openai_agents_demo_docs\";\nexport const openAiAgentsSdkDemoMcpToolNames = [\n  \"read_demo_doc\",\n  \"search_demo_docs\",\n] as const;\n\nexport const openAiAgentsSdkDemoMcpSummarySchema = z.object({\n  activeServerNames: z.array(z.string()),\n  failedServerErrors: z.array(z.string()),\n  failedServerNames: z.array(z.string()),\n  usedToolNames: z.array(z.string()),\n});\n\nexport interface OpenAiAgentsSdkDemoMcpProfile {\n  convertSchemasToStrict: true;\n  lifecycle: \"connectMcpServers\";\n  routePath: typeof OPENAI_AGENTS_SDK_DEMO_MCP_ROUTE_PATH;\n  sdkPrimitives: [\n    \"MCPServerStreamableHttp\",\n    \"connectMcpServers\",\n    \"Agent.mcpServers\",\n    \"Agent.mcpConfig\",\n  ];\n  serverName: typeof OPENAI_AGENTS_SDK_DEMO_MCP_SERVER_NAME;\n  toolNamePrefixing: true;\n  transport: \"streamable-http\";\n}\n\nexport interface OpenAiAgentsSdkDemoMcpCatalogEntry {\n  name: string;\n  notes: string;\n  toolNames: string[];\n  transport: \"streamable-http\";\n  urlPath: string;\n}\n\nfunction getMcpToolName(rawItem: { name?: string; type?: string }) {\n  if (\n    rawItem.type === \"function_call\" ||\n    rawItem.type === \"function_call_result\"\n  ) {\n    return rawItem.name ?? null;\n  }\n\n  return null;\n}\n\nfunction getFailedServerErrorMessages(mcpServers: MCPServers) {\n  return mcpServers.failed.map((server) => {\n    const error = mcpServers.errors.get(server);\n\n    return error\n      ? `${server.name}: ${error.message}`\n      : `${server.name}: failed`;\n  });\n}\n\nexport function getOpenAiAgentsSdkDemoMcpExposedToolNames() {\n  return openAiAgentsSdkDemoMcpToolNames.map(\n    (toolName) => `${OPENAI_AGENTS_SDK_DEMO_MCP_SERVER_NAME}__${toolName}`\n  );\n}\n\nexport function isOpenAiAgentsSdkDemoMcpToolName(name: string) {\n  return getOpenAiAgentsSdkDemoMcpExposedToolNames().includes(name);\n}\n\nexport function getOpenAiAgentsSdkDemoMcpProfile(): OpenAiAgentsSdkDemoMcpProfile {\n  return {\n    convertSchemasToStrict: true,\n    lifecycle: \"connectMcpServers\",\n    routePath: OPENAI_AGENTS_SDK_DEMO_MCP_ROUTE_PATH,\n    sdkPrimitives: [\n      \"MCPServerStreamableHttp\",\n      \"connectMcpServers\",\n      \"Agent.mcpServers\",\n      \"Agent.mcpConfig\",\n    ],\n    serverName: OPENAI_AGENTS_SDK_DEMO_MCP_SERVER_NAME,\n    toolNamePrefixing: true,\n    transport: \"streamable-http\",\n  };\n}\n\nexport function getOpenAiAgentsSdkDemoMcpCatalog(): OpenAiAgentsSdkDemoMcpCatalogEntry[] {\n  return [\n    {\n      name: OPENAI_AGENTS_SDK_DEMO_MCP_SERVER_NAME,\n      notes:\n        \"Demo-local Streamable HTTP MCP server that exposes this feature slice's README and durable frontend doc through official MCP tools.\",\n      toolNames: getOpenAiAgentsSdkDemoMcpExposedToolNames(),\n      transport: \"streamable-http\",\n      urlPath: OPENAI_AGENTS_SDK_DEMO_MCP_ROUTE_PATH,\n    },\n  ];\n}\n\nexport async function connectOpenAiAgentsSdkDemoMcpServers({\n  origin,\n}: {\n  origin: string;\n}) {\n  const server = new MCPServerStreamableHttp({\n    cacheToolsList: true,\n    errorFunction: ({ error }) =>\n      error instanceof Error\n        ? `MCP tool call failed: ${error.message}`\n        : \"MCP tool call failed.\",\n    name: OPENAI_AGENTS_SDK_DEMO_MCP_SERVER_NAME,\n    toolFilter: createMCPToolStaticFilter({\n      allowed: [...openAiAgentsSdkDemoMcpToolNames],\n    }),\n    url: new URL(OPENAI_AGENTS_SDK_DEMO_MCP_ROUTE_PATH, origin).toString(),\n  });\n\n  return await connectMcpServers([server], {\n    connectInParallel: true,\n  });\n}\n\nexport function getOpenAiAgentsSdkDemoMcpUsageMetadata({\n  mcpServers,\n  newItems,\n}: {\n  mcpServers: MCPServers;\n  newItems: RunItem[];\n}): OpenAiAgentsSdkDemoMessageMetadata | undefined {\n  const usedToolNames = Array.from(\n    new Set(\n      newItems\n        .map((item) =>\n          getMcpToolName(\n            (item.rawItem ?? {}) as { name?: string; type?: string }\n          )\n        )\n        .filter(\n          (value): value is string =>\n            value !== null && isOpenAiAgentsSdkDemoMcpToolName(value)\n        )\n    )\n  );\n  const hasConnectionMetadata =\n    mcpServers.active.length > 0 || mcpServers.failed.length > 0;\n\n  if (!hasConnectionMetadata && usedToolNames.length === 0) {\n    return;\n  }\n\n  return {\n    mcpSummary: {\n      activeServerNames: mcpServers.active.map((server) => server.name),\n      failedServerErrors: getFailedServerErrorMessages(mcpServers),\n      failedServerNames: mcpServers.failed.map((server) => server.name),\n      usedToolNames,\n    },\n    ...(usedToolNames.length > 0 ? { usedGuideIds: [\"mcp\"] } : {}),\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/mcp.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/models.ts",
      "content": "type DemoEnv = Record<string, string | undefined>;\n\nexport type DemoReasoningEffort =\n  | \"high\"\n  | \"low\"\n  | \"medium\"\n  | \"minimal\"\n  | \"none\"\n  | \"xhigh\";\n\nexport type DemoTextVerbosity = \"high\" | \"low\" | \"medium\";\n\nexport interface OpenAiAgentsSdkDemoModelProfile {\n  api: \"responses\";\n  baseUrl: string;\n  model: string;\n  provider: \"gateway-openai-client\";\n  reasoningEffort: DemoReasoningEffort;\n  responsesTransport: \"http\";\n  textVerbosity: DemoTextVerbosity;\n}\n\nconst defaultGatewayBaseUrl = \"https://ai-gateway.vercel.sh/v1\";\nconst defaultGatewayChatModel = \"openai/gpt-5-mini\";\nconst defaultReasoningEffort: DemoReasoningEffort = \"medium\";\nconst defaultTextVerbosity: DemoTextVerbosity = \"low\";\nconst reasoningEfforts = new Set<DemoReasoningEffort>([\n  \"none\",\n  \"minimal\",\n  \"low\",\n  \"medium\",\n  \"high\",\n  \"xhigh\",\n]);\nconst textVerbosityLevels = new Set<DemoTextVerbosity>([\n  \"low\",\n  \"medium\",\n  \"high\",\n]);\n\nexport function getOpenAiAgentsSdkDemoChatModel(env: DemoEnv) {\n  return (\n    env.OPENAI_AGENTS_MODEL ||\n    env.AI_GATEWAY_CHAT_MODEL ||\n    defaultGatewayChatModel\n  );\n}\n\nexport function getOpenAiAgentsSdkDemoGatewayBaseUrl(env: DemoEnv) {\n  return env.OPENAI_AGENTS_GATEWAY_BASE_URL || defaultGatewayBaseUrl;\n}\n\nexport function getOpenAiAgentsSdkDemoReasoningEffort(\n  env: DemoEnv\n): DemoReasoningEffort {\n  const configuredEffort = env.OPENAI_AGENTS_REASONING_EFFORT;\n\n  if (\n    configuredEffort &&\n    reasoningEfforts.has(configuredEffort as DemoReasoningEffort)\n  ) {\n    return configuredEffort as DemoReasoningEffort;\n  }\n\n  return defaultReasoningEffort;\n}\n\nexport function getOpenAiAgentsSdkDemoTextVerbosity(\n  env: DemoEnv\n): DemoTextVerbosity {\n  const configuredVerbosity = env.OPENAI_AGENTS_TEXT_VERBOSITY;\n\n  if (\n    configuredVerbosity &&\n    textVerbosityLevels.has(configuredVerbosity as DemoTextVerbosity)\n  ) {\n    return configuredVerbosity as DemoTextVerbosity;\n  }\n\n  return defaultTextVerbosity;\n}\n\nexport function getOpenAiAgentsSdkDemoModelProfile(\n  env: DemoEnv\n): OpenAiAgentsSdkDemoModelProfile {\n  return {\n    api: \"responses\",\n    baseUrl: getOpenAiAgentsSdkDemoGatewayBaseUrl(env),\n    model: getOpenAiAgentsSdkDemoChatModel(env),\n    provider: \"gateway-openai-client\",\n    reasoningEffort: getOpenAiAgentsSdkDemoReasoningEffort(env),\n    responsesTransport: \"http\",\n    textVerbosity: getOpenAiAgentsSdkDemoTextVerbosity(env),\n  };\n}\n\nexport function isOpenAiAgentsSdkDemoImageGenerationProviderBlocked(\n  modelProfile: Pick<\n    OpenAiAgentsSdkDemoModelProfile,\n    \"api\" | \"baseUrl\" | \"provider\"\n  >\n) {\n  return (\n    modelProfile.api === \"responses\" &&\n    modelProfile.provider === \"gateway-openai-client\" &&\n    modelProfile.baseUrl.includes(\"ai-gateway.vercel.sh\")\n  );\n}\n\nfunction getErrorMessage(error: unknown) {\n  if (error instanceof Error) {\n    return error.message;\n  }\n\n  if (typeof error === \"string\") {\n    return error;\n  }\n\n  try {\n    return JSON.stringify(error);\n  } catch {\n    return String(error);\n  }\n}\n\nexport function getOpenAiAgentsSdkDemoProviderErrorMessage(error: unknown) {\n  const message = getErrorMessage(error);\n\n  if (/At least one user message is required in the input/i.test(message)) {\n    return [\n      \"AI Gateway rejected the OpenAI Agents SDK function-tool continuation request.\",\n      \"The SDK default toolUseBehavior is run_llm_again, so after a local tool() call succeeds, the run sends that tool output back to the model for the final answer.\",\n      `Provider response: ${message}`,\n      \"No fake final answer was injected for this demo.\",\n    ].join(\" \");\n  }\n\n  if (/No tool output found for function call/i.test(message)) {\n    return [\n      \"AI Gateway rejected the OpenAI Agents SDK tool-output continuation request.\",\n      \"The provider reported a missing function-call output while the SDK was continuing the official run loop.\",\n      `Provider response: ${message}`,\n      \"No fake tool output was injected for this demo.\",\n    ].join(\" \");\n  }\n\n  return null;\n}\n\nexport function supportsOpenAiAgentsSdkToolSearch(model: string) {\n  const normalizedModel = model.toLowerCase();\n\n  if (!normalizedModel.startsWith(\"openai/gpt-5\")) {\n    return false;\n  }\n\n  if (\n    normalizedModel.startsWith(\"openai/gpt-5.4\") ||\n    normalizedModel.startsWith(\"openai/gpt-5.5\")\n  ) {\n    return true;\n  }\n\n  return false;\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/models.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/results.ts",
      "content": "import type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\n\nfunction getFinalOutputPreview(finalOutput: unknown) {\n  if (typeof finalOutput === \"undefined\") {\n    return;\n  }\n\n  const text =\n    typeof finalOutput === \"string\"\n      ? finalOutput\n      : JSON.stringify(finalOutput, null, 0);\n\n  const normalizedText = text.trim();\n\n  if (normalizedText.length <= 240) {\n    return normalizedText;\n  }\n\n  return `${normalizedText.slice(0, 237)}...`;\n}\n\nexport function getOpenAiAgentsSdkDemoResultUsageMetadata({\n  activeAgentName,\n  finalOutput,\n  hasResumableState,\n  historyLength,\n  interruptionCount,\n  lastAgentName,\n  newItemsCount,\n  outputCount,\n  rawResponseCount,\n  usage,\n}: {\n  activeAgentName?: string;\n  finalOutput?: unknown;\n  hasResumableState: boolean;\n  historyLength: number;\n  interruptionCount: number;\n  lastAgentName?: string;\n  newItemsCount: number;\n  outputCount: number;\n  rawResponseCount: number;\n  usage?: {\n    inputTokens?: number;\n    outputTokens?: number;\n    requests?: number;\n    totalTokens?: number;\n  };\n}): OpenAiAgentsSdkDemoMessageMetadata {\n  return {\n    resultSummary: {\n      ...(activeAgentName ? { activeAgentName } : {}),\n      ...(typeof finalOutput === \"undefined\"\n        ? {}\n        : {\n            finalOutputPreview: getFinalOutputPreview(finalOutput),\n          }),\n      hasResumableState,\n      historyLength,\n      inputTokens: usage?.inputTokens ?? 0,\n      interruptionCount,\n      ...(lastAgentName ? { lastAgentName } : {}),\n      newItemsCount,\n      outputCount,\n      outputTokens: usage?.outputTokens ?? 0,\n      rawResponseCount,\n      requestCount: usage?.requests ?? 0,\n      totalTokens: usage?.totalTokens ?? 0,\n    },\n    usedGuideIds: [\"results\"],\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/results.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/running.ts",
      "content": "import type { AgentInputItem } from \"@openai/agents\";\nimport type { UIMessage } from \"ai\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst defaultMaxTurns = 8;\nconst defaultWorkflowName = \"openai-agents-sdk-demo\";\nconst noUserInputErrorMessage =\n  \"At least one user message is required before starting an agent run.\";\n\nexport class OpenAiAgentsSdkDemoRunInputError extends Error {}\n\nexport interface OpenAiAgentsSdkDemoRunProfile {\n  continuationStrategy: \"previous-response-id-or-memory-session\";\n  maxTurns: number;\n  usesRequestSignal: true;\n  workflowName: string;\n}\n\nexport interface OpenAiAgentsSdkDemoRunRequestOptions {\n  messages: UIMessage[];\n  signal?: AbortSignal;\n}\n\nexport function getOpenAiAgentsSdkDemoRunProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoRunProfile {\n  const configuredMaxTurns = Number.parseInt(\n    env.OPENAI_AGENTS_MAX_TURNS ?? \"\",\n    10\n  );\n\n  return {\n    continuationStrategy: \"previous-response-id-or-memory-session\",\n    maxTurns:\n      Number.isFinite(configuredMaxTurns) && configuredMaxTurns > 0\n        ? configuredMaxTurns\n        : defaultMaxTurns,\n    usesRequestSignal: true,\n    workflowName: defaultWorkflowName,\n  };\n}\n\nfunction getMessageText(message: UIMessage) {\n  return message.parts\n    .filter((part) => part.type === \"text\")\n    .map((part) => part.text)\n    .join(\"\\n\")\n    .trim();\n}\n\nfunction createUserInputItem(content: string): AgentInputItem {\n  return {\n    content,\n    role: \"user\",\n  };\n}\n\nfunction createAssistantInputItem(content: string): AgentInputItem {\n  return {\n    content: [{ text: content, type: \"output_text\" }],\n    role: \"assistant\",\n    status: \"completed\",\n  };\n}\n\nfunction convertToAgentInput(messages: UIMessage[]): AgentInputItem[] {\n  const items: AgentInputItem[] = [];\n\n  for (const message of messages) {\n    if (message.role !== \"assistant\" && message.role !== \"user\") {\n      continue;\n    }\n\n    const content = getMessageText(message);\n\n    if (!content) {\n      continue;\n    }\n\n    items.push(\n      message.role === \"assistant\"\n        ? createAssistantInputItem(content)\n        : createUserInputItem(content)\n    );\n  }\n\n  return items;\n}\n\nfunction getLatestAssistantResponseId(messages: UIMessage[]) {\n  const latestAssistantMessage = [...messages]\n    .reverse()\n    .find((message) => message.role === \"assistant\");\n\n  if (!latestAssistantMessage?.metadata) {\n    return;\n  }\n\n  return (latestAssistantMessage.metadata as OpenAiAgentsSdkDemoMessageMetadata)\n    .lastResponseId;\n}\n\nfunction hasLatestAssistantSessionId(messages: UIMessage[]) {\n  const latestAssistantMessage = [...messages]\n    .reverse()\n    .find((message) => message.role === \"assistant\");\n\n  const metadata = latestAssistantMessage?.metadata as\n    | OpenAiAgentsSdkDemoMessageMetadata\n    | undefined;\n\n  return Boolean(metadata?.sessionSummary?.sessionId);\n}\n\nfunction isOpenAiResponsesResponseId(responseId: string | undefined) {\n  return responseId?.startsWith(\"resp_\") ?? false;\n}\n\nfunction getLatestUserInput(messages: UIMessage[]) {\n  const latestUserMessage = [...messages]\n    .reverse()\n    .find((message) => message.role === \"user\");\n\n  if (!latestUserMessage) {\n    return null;\n  }\n\n  const content = getMessageText(latestUserMessage);\n\n  if (!content) {\n    return null;\n  }\n\n  return [createUserInputItem(content)];\n}\n\nfunction isUserAgentInputItem(\n  item: AgentInputItem\n): item is AgentInputItem & { role: \"user\" } {\n  return \"role\" in item && item.role === \"user\";\n}\n\nfunction assertHasUserInput(input: AgentInputItem[]) {\n  if (input.some(isUserAgentInputItem)) {\n    return;\n  }\n\n  throw new OpenAiAgentsSdkDemoRunInputError(noUserInputErrorMessage);\n}\n\nexport function getOpenAiAgentsSdkDemoRunInputErrorMessage(error: unknown) {\n  if (error instanceof OpenAiAgentsSdkDemoRunInputError) {\n    return error.message;\n  }\n\n  return null;\n}\n\nexport function getOpenAiAgentsSdkDemoRunRequest(\n  { messages, signal }: OpenAiAgentsSdkDemoRunRequestOptions,\n  env: DemoEnv = process.env\n): {\n  input: AgentInputItem[];\n  options: {\n    maxTurns: number;\n    previousResponseId?: string;\n    signal?: AbortSignal;\n    stream: true;\n  };\n} {\n  const profile = getOpenAiAgentsSdkDemoRunProfile(env);\n  const latestAssistantResponseId = getLatestAssistantResponseId(messages);\n  const previousResponseId = isOpenAiResponsesResponseId(\n    latestAssistantResponseId\n  )\n    ? latestAssistantResponseId\n    : undefined;\n  const latestUserInput =\n    previousResponseId || hasLatestAssistantSessionId(messages)\n      ? getLatestUserInput(messages)\n      : null;\n  const input = latestUserInput ?? convertToAgentInput(messages);\n\n  assertHasUserInput(input);\n\n  return {\n    input,\n    options: {\n      ...(previousResponseId ? { previousResponseId } : {}),\n      ...(signal ? { signal } : {}),\n      maxTurns: profile.maxTurns,\n      stream: true,\n    },\n  };\n}\n\nexport function getOpenAiAgentsSdkDemoRunningUsageMetadata(\n  lastResponseId?: string\n) {\n  if (!lastResponseId) {\n    return;\n  }\n\n  return {\n    lastResponseId,\n    usedGuideIds: [\"running-agents\"],\n  } satisfies OpenAiAgentsSdkDemoMessageMetadata;\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/running.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/runtime.ts",
      "content": "import { type UIMessage, validateUIMessages } from \"ai\";\n\nimport { getOpenAiAgentsSdkDemoApprovalErrorMessage } from \"./approvals\";\nimport { streamOpenAiAgentsSdkDemo } from \"./chat\";\nimport {\n  getOpenAiAgentsSdkDemoContextProfile,\n  type OpenAiAgentsSdkDemoContextProfile,\n} from \"./context\";\nimport {\n  getOpenAiAgentsSdkDemoAiSdkExtensionProfile,\n  type OpenAiAgentsSdkDemoAiSdkExtensionProfile,\n} from \"./extensions\";\nimport {\n  getOpenAiAgentsSdkDemoGuardrailCatalog,\n  getOpenAiAgentsSdkDemoGuardrailErrorMessage,\n  type OpenAiAgentsSdkDemoGuardrailCatalogEntry,\n} from \"./guardrails\";\nimport {\n  getOpenAiAgentsSdkDemoGuideCoverage,\n  type OpenAiAgentsSdkDemoGuideCoverage,\n} from \"./guide-coverage\";\nimport {\n  getOpenAiAgentsSdkDemoHandoffCatalog,\n  type OpenAiAgentsSdkDemoHandoffCatalogEntry,\n} from \"./handoffs\";\nimport {\n  getOpenAiAgentsSdkDemoMcpCatalog,\n  getOpenAiAgentsSdkDemoMcpProfile,\n  type OpenAiAgentsSdkDemoMcpCatalogEntry,\n  type OpenAiAgentsSdkDemoMcpProfile,\n} from \"./mcp\";\nimport {\n  getOpenAiAgentsSdkDemoChatModel,\n  getOpenAiAgentsSdkDemoModelProfile,\n  getOpenAiAgentsSdkDemoProviderErrorMessage,\n  type OpenAiAgentsSdkDemoModelProfile,\n} from \"./models\";\nimport {\n  getOpenAiAgentsSdkDemoRunInputErrorMessage,\n  getOpenAiAgentsSdkDemoRunProfile,\n  type OpenAiAgentsSdkDemoRunProfile,\n} from \"./running\";\nimport {\n  getOpenAiAgentsSdkDemoSandboxProfile,\n  type OpenAiAgentsSdkDemoSandboxProfile,\n} from \"./sandbox\";\nimport {\n  getOpenAiAgentsSdkDemoSessionProfile,\n  type OpenAiAgentsSdkDemoSessionProfile,\n} from \"./sessions\";\nimport {\n  getOpenAiAgentsSdkDemoToolCatalog,\n  type OpenAiAgentsSdkDemoToolCatalogEntry,\n} from \"./tools\";\nimport {\n  getOpenAiAgentsSdkDemoTraceProfile,\n  type OpenAiAgentsSdkDemoTraceProfile,\n} from \"./tracing\";\nimport {\n  getOpenAiAgentsSdkDemoVoiceProfile,\n  type OpenAiAgentsSdkDemoVoiceProfile,\n} from \"./voice\";\nimport { getOpenAiAgentsSdkDemoVoiceClientSecretRouteState } from \"./voice-realtime\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\ninterface OpenAiAgentsSdkDemoRequestBody {\n  messages?: UIMessage[];\n}\n\ninterface OpenAiAgentsSdkDemoRequestDependencies {\n  streamOpenAiAgentsSdkDemo: (\n    messages: UIMessage[],\n    env: DemoEnv,\n    options?: {\n      origin?: string;\n      signal?: AbortSignal;\n    }\n  ) => Promise<Response> | Response;\n}\n\nexport interface OpenAiAgentsSdkDemoRuntimeState {\n  aiSdkExtensionProfile: OpenAiAgentsSdkDemoAiSdkExtensionProfile;\n  chatModel: string;\n  contextProfile: OpenAiAgentsSdkDemoContextProfile;\n  guardrailCatalog: OpenAiAgentsSdkDemoGuardrailCatalogEntry[];\n  guideCoverage: OpenAiAgentsSdkDemoGuideCoverage[];\n  handoffCatalog: OpenAiAgentsSdkDemoHandoffCatalogEntry[];\n  isChatAvailable: boolean;\n  mcpCatalog: OpenAiAgentsSdkDemoMcpCatalogEntry[];\n  mcpProfile: OpenAiAgentsSdkDemoMcpProfile;\n  modelProfile: OpenAiAgentsSdkDemoModelProfile;\n  nodeVersion: string;\n  runProfile: OpenAiAgentsSdkDemoRunProfile;\n  sandboxProfile: OpenAiAgentsSdkDemoSandboxProfile;\n  sessionProfile: OpenAiAgentsSdkDemoSessionProfile;\n  setupMessage: string | null;\n  statusLabel: \"Ready\" | \"Setup required\";\n  toolCatalog: OpenAiAgentsSdkDemoToolCatalogEntry[];\n  traceProfile: OpenAiAgentsSdkDemoTraceProfile;\n  voiceProfile: OpenAiAgentsSdkDemoVoiceProfile;\n}\n\nconst invalidMessagesError = 'Expected a JSON body with a \"messages\" array.';\nconst invalidUiMessagesError =\n  'Expected each \"messages\" entry to match the UIMessage format.';\n\nasync function readOpenAiAgentsSdkDemoMessages(body: unknown) {\n  const { messages } = (body ?? {}) as OpenAiAgentsSdkDemoRequestBody;\n\n  if (!Array.isArray(messages)) {\n    throw new Error(invalidMessagesError);\n  }\n\n  try {\n    return await validateUIMessages({ messages });\n  } catch {\n    throw new Error(invalidUiMessagesError);\n  }\n}\n\nexport function getOpenAiAgentsSdkDemoRuntimeState(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoRuntimeState {\n  const chatModel = getOpenAiAgentsSdkDemoChatModel(env);\n  const voiceRouteState =\n    getOpenAiAgentsSdkDemoVoiceClientSecretRouteState(env);\n  const issues = env.AI_GATEWAY_API_KEY\n    ? []\n    : [\n        \"AI_GATEWAY_API_KEY is missing. Add it to your installed Next.js app's .env.local before running this registry demo.\",\n      ];\n  const isChatAvailable = issues.length === 0;\n\n  return {\n    aiSdkExtensionProfile: getOpenAiAgentsSdkDemoAiSdkExtensionProfile(),\n    chatModel,\n    contextProfile: getOpenAiAgentsSdkDemoContextProfile(),\n    guardrailCatalog: getOpenAiAgentsSdkDemoGuardrailCatalog({\n      isChatAvailable,\n    }),\n    guideCoverage: getOpenAiAgentsSdkDemoGuideCoverage({\n      isChatAvailable,\n      isVoiceProviderAvailable: voiceRouteState.status === \"configured\",\n    }),\n    handoffCatalog: getOpenAiAgentsSdkDemoHandoffCatalog({\n      isChatAvailable,\n    }),\n    isChatAvailable,\n    mcpCatalog: getOpenAiAgentsSdkDemoMcpCatalog(),\n    mcpProfile: getOpenAiAgentsSdkDemoMcpProfile(),\n    modelProfile: getOpenAiAgentsSdkDemoModelProfile(env),\n    nodeVersion: process.version,\n    runProfile: getOpenAiAgentsSdkDemoRunProfile(env),\n    sandboxProfile: getOpenAiAgentsSdkDemoSandboxProfile(),\n    sessionProfile: getOpenAiAgentsSdkDemoSessionProfile(),\n    setupMessage: issues.length > 0 ? issues.join(\" \") : null,\n    traceProfile: getOpenAiAgentsSdkDemoTraceProfile(env),\n    statusLabel: isChatAvailable ? \"Ready\" : \"Setup required\",\n    toolCatalog: getOpenAiAgentsSdkDemoToolCatalog({\n      env,\n      isChatAvailable,\n    }),\n    voiceProfile: getOpenAiAgentsSdkDemoVoiceProfile(env),\n  };\n}\n\nexport async function handleOpenAiAgentsSdkDemoRequest(\n  request: Request,\n  env: DemoEnv = process.env,\n  dependencies: OpenAiAgentsSdkDemoRequestDependencies = {\n    streamOpenAiAgentsSdkDemo,\n  }\n) {\n  const runtimeState = getOpenAiAgentsSdkDemoRuntimeState(env);\n\n  if (!runtimeState.isChatAvailable) {\n    return Response.json(\n      {\n        error: runtimeState.setupMessage,\n      },\n      { status: 500 }\n    );\n  }\n\n  try {\n    const messages = await readOpenAiAgentsSdkDemoMessages(\n      await request.json()\n    );\n\n    return await dependencies.streamOpenAiAgentsSdkDemo(messages, env, {\n      origin: new URL(request.url).origin,\n      signal: request.signal,\n    });\n  } catch (error) {\n    if (\n      error instanceof Error &&\n      [invalidMessagesError, invalidUiMessagesError].includes(error.message)\n    ) {\n      return Response.json(\n        {\n          error: error.message,\n        },\n        { status: 400 }\n      );\n    }\n\n    const guardrailErrorMessage =\n      getOpenAiAgentsSdkDemoGuardrailErrorMessage(error);\n\n    if (guardrailErrorMessage) {\n      return Response.json(\n        {\n          error: guardrailErrorMessage,\n        },\n        { status: 400 }\n      );\n    }\n\n    const approvalErrorMessage =\n      getOpenAiAgentsSdkDemoApprovalErrorMessage(error);\n\n    if (approvalErrorMessage) {\n      return Response.json(\n        {\n          error: approvalErrorMessage,\n        },\n        { status: 400 }\n      );\n    }\n\n    const runInputErrorMessage =\n      getOpenAiAgentsSdkDemoRunInputErrorMessage(error);\n\n    if (runInputErrorMessage) {\n      return Response.json(\n        {\n          error: runInputErrorMessage,\n        },\n        { status: 400 }\n      );\n    }\n\n    const providerErrorMessage =\n      getOpenAiAgentsSdkDemoProviderErrorMessage(error);\n\n    if (providerErrorMessage) {\n      return Response.json(\n        {\n          error: providerErrorMessage,\n        },\n        { status: 400 }\n      );\n    }\n\n    throw error;\n  }\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/runtime.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/sandbox.ts",
      "content": "import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { RunContext, RunItem } from \"@openai/agents\";\nimport {\n  Capabilities,\n  localDir,\n  Manifest,\n  SandboxAgent,\n} from \"@openai/agents/sandbox\";\nimport {\n  UnixLocalSandboxClient,\n  type UnixLocalSandboxSessionState,\n} from \"@openai/agents/sandbox/local\";\nimport { z } from \"zod\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\nimport type { OpenAiAgentsSdkDemoContext } from \"./context\";\nimport type { OpenAiAgentsSdkDemoModelProfile } from \"./models\";\n\nconst sandboxManifestRoot = \"/workspace\";\nconst sandboxAgentModel = \"openai/gpt-5.4-mini\";\n\nfunction firstExistingPath(paths: readonly string[], label: string) {\n  const resolvedPath = paths.find((path) => existsSync(path));\n\n  if (!resolvedPath) {\n    throw new Error(\n      `Could not resolve the OpenAI Agents SDK demo sandbox ${label} path from cwd ${process.cwd()}.`\n    );\n  }\n\n  return resolvedPath;\n}\n\nconst sandboxMountedEntries = {\n  docs: firstExistingPath(\n    [\n      resolve(process.cwd(), \"docs/frontend\"),\n      resolve(process.cwd(), \"src/docs/frontend\"),\n      resolve(process.cwd(), \"lib/openai-agents-sdk-demo\"),\n      resolve(process.cwd(), \"src/lib/openai-agents-sdk-demo\"),\n    ],\n    \"docs\"\n  ),\n  feature: firstExistingPath(\n    [\n      resolve(process.cwd(), \"lib/openai-agents-sdk-demo\"),\n      resolve(process.cwd(), \"src/lib/openai-agents-sdk-demo\"),\n    ],\n    \"feature\"\n  ),\n} as const;\nconst sandboxClient = new UnixLocalSandboxClient();\nconst sandboxSessionStateStore = new Map<\n  string,\n  UnixLocalSandboxSessionState\n>();\n\nexport const openAiAgentsSdkDemoSandboxSummarySchema = z.object({\n  backendId: z.string(),\n  currentAgentName: z.string(),\n  manifestRoot: z.string(),\n  mountedPaths: z.array(z.string()),\n  persistedSessionCount: z.number().int().nonnegative(),\n  sessionId: z.string(),\n  workspaceReady: z.boolean(),\n});\n\nexport interface OpenAiAgentsSdkDemoSandboxProfile {\n  agentModel: \"openai/gpt-5.4-mini\";\n  clientBackend: \"unix_local\";\n  defaultCapabilities: [\"filesystem()\", \"shell()\", \"compaction()\"];\n  manifestRoot: \"/workspace\";\n  mountedPaths: [\"/workspace/docs\", \"/workspace/feature\"];\n  sdkPrimitives: [\n    \"SandboxAgent\",\n    \"Manifest\",\n    \"Capabilities.default()\",\n    \"UnixLocalSandboxClient\",\n    \"RunConfig.sandbox\",\n  ];\n  sessionPersistence: \"session-id -> process-local sessionState\";\n  workspaceSource: \"localDir() -> temp workspace\";\n}\n\nfunction getSandboxMountedPaths() {\n  return [\"/workspace/docs\", \"/workspace/feature\"] as const;\n}\n\nfunction didUseOpenAiAgentsSdkDemoSandboxTool(newItems: RunItem[] = []) {\n  return newItems.some(\n    (item) =>\n      (item.rawItem as { name?: string } | undefined)?.name ===\n      \"sandbox_workspace_agent\"\n  );\n}\n\nfunction createOpenAiAgentsSdkDemoSandboxManifest() {\n  return new Manifest({\n    extraPathGrants: Object.values(sandboxMountedEntries).map((path) => ({\n      description: \"OpenAI Agents SDK demo sandbox source path\",\n      path,\n      readOnly: true,\n    })),\n    entries: {\n      docs: localDir({\n        src: sandboxMountedEntries.docs,\n      }),\n      feature: localDir({\n        src: sandboxMountedEntries.feature,\n      }),\n    },\n    root: sandboxManifestRoot,\n  });\n}\n\nexport function getOpenAiAgentsSdkDemoSandboxProfile(): OpenAiAgentsSdkDemoSandboxProfile {\n  return {\n    agentModel: \"openai/gpt-5.4-mini\",\n    clientBackend: \"unix_local\",\n    defaultCapabilities: [\"filesystem()\", \"shell()\", \"compaction()\"],\n    manifestRoot: \"/workspace\",\n    mountedPaths: [\"/workspace/docs\", \"/workspace/feature\"],\n    sdkPrimitives: [\n      \"SandboxAgent\",\n      \"Manifest\",\n      \"Capabilities.default()\",\n      \"UnixLocalSandboxClient\",\n      \"RunConfig.sandbox\",\n    ],\n    sessionPersistence: \"session-id -> process-local sessionState\",\n    workspaceSource: \"localDir() -> temp workspace\",\n  };\n}\n\nfunction getSerializedSandboxState(state?: { toJSON?: () => unknown }) {\n  const serializedState = state?.toJSON?.() as\n    | {\n        sandbox?: {\n          backendId: string;\n          currentAgentName: string;\n          sessionState: {\n            manifest?: {\n              entries?: Record<string, unknown>;\n              root?: string;\n            };\n            workspaceReady: boolean;\n          };\n          sessionsByAgent: Record<string, unknown>;\n        };\n      }\n    | undefined;\n\n  return serializedState?.sandbox;\n}\n\nexport function createOpenAiAgentsSdkDemoSandboxWorkspaceAgent({\n  modelProfile,\n}: {\n  modelProfile: OpenAiAgentsSdkDemoModelProfile;\n}) {\n  return new SandboxAgent<OpenAiAgentsSdkDemoContext>({\n    capabilities: [...Capabilities.default()],\n    defaultManifest: createOpenAiAgentsSdkDemoSandboxManifest(),\n    instructions: (runContext: RunContext<OpenAiAgentsSdkDemoContext>) =>\n      [\n        \"Use the sandbox workspace to inspect the mounted demo docs and feature slice when the user needs repo-grounded answers.\",\n        `Current demo run mode: ${runContext.context.researchMode}.`,\n        \"Mounted workspace paths: /workspace/docs and /workspace/feature.\",\n        \"Prefer rg and sed for inspection, and cite the exact sandbox paths you read.\",\n        \"Do not claim changes to the host repository. Any edits stay inside the sandbox workspace unless the user explicitly asks for a patch plan.\",\n      ].join(\" \"),\n    model: sandboxAgentModel,\n    modelSettings: {\n      reasoning: {\n        effort: modelProfile.reasoningEffort,\n      },\n      text: {\n        verbosity: modelProfile.textVerbosity,\n      },\n    },\n    name: \"Sandbox Workspace Agent\",\n  });\n}\n\nexport function getOpenAiAgentsSdkDemoSandboxRunConfig({\n  sessionId,\n}: {\n  sessionId: string;\n}) {\n  const sessionState = sandboxSessionStateStore.get(sessionId);\n\n  return {\n    sandbox: {\n      client: sandboxClient,\n      ...(sessionState ? { sessionState } : {}),\n    },\n  };\n}\n\nexport function recordOpenAiAgentsSdkDemoSandboxSessionState({\n  sessionId,\n  state,\n}: {\n  sessionId: string;\n  state?: {\n    toJSON?: () => unknown;\n  };\n}) {\n  const serializedState = state?.toJSON?.() as\n    | {\n        sandbox?: {\n          sessionState?: UnixLocalSandboxSessionState;\n        };\n      }\n    | undefined;\n  const sessionState = serializedState?.sandbox?.sessionState;\n\n  if (!sessionState) {\n    return;\n  }\n\n  sandboxSessionStateStore.set(sessionId, sessionState);\n}\n\nexport function getOpenAiAgentsSdkDemoSandboxUsageMetadata({\n  newItems = [],\n  sessionId,\n  state,\n}: {\n  newItems?: RunItem[];\n  sessionId: string;\n  state?: {\n    toJSON?: () => unknown;\n  };\n}) {\n  const sandboxState = getSerializedSandboxState(state);\n  const usedSandboxTool = didUseOpenAiAgentsSdkDemoSandboxTool(newItems);\n\n  if (!(sandboxState || usedSandboxTool)) {\n    return;\n  }\n\n  const mountedPaths = Object.keys(\n    sandboxState?.sessionState.manifest?.entries ?? {}\n  ).map((entryName) => `${sandboxManifestRoot}/${entryName}`);\n\n  return {\n    sandboxSummary: {\n      backendId: sandboxState?.backendId ?? \"unix_local\",\n      currentAgentName:\n        sandboxState?.currentAgentName ?? \"Sandbox Workspace Agent\",\n      manifestRoot:\n        sandboxState?.sessionState.manifest?.root ?? sandboxManifestRoot,\n      mountedPaths:\n        mountedPaths.length > 0 ? mountedPaths : [...getSandboxMountedPaths()],\n      persistedSessionCount: sandboxState\n        ? Object.keys(sandboxState.sessionsByAgent).length\n        : sandboxSessionStateStore.has(sessionId)\n          ? 1\n          : 0,\n      sessionId,\n      workspaceReady:\n        sandboxState?.sessionState.workspaceReady ?? usedSandboxTool,\n    },\n    usedGuideIds: [\"sandbox-agents\"],\n  } satisfies OpenAiAgentsSdkDemoMessageMetadata;\n}\n\nexport function clearOpenAiAgentsSdkDemoSandboxSessionStateStore() {\n  sandboxSessionStateStore.clear();\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/sandbox.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/sessions.ts",
      "content": "import { type AgentInputItem, MemorySession } from \"@openai/agents\";\nimport type { UIMessage } from \"ai\";\nimport { z } from \"zod\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\n\nconst sessionStore = new Map<string, MemorySession>();\n\nexport const openAiAgentsSdkDemoSessionSummarySchema = z.object({\n  historyItemCount: z.number().int().nonnegative(),\n  sessionId: z.string(),\n  sessionKind: z.literal(\"MemorySession\"),\n  storageScope: z.literal(\"process-local\"),\n});\n\nexport interface OpenAiAgentsSdkDemoSessionProfile {\n  historyStorage: \"process-local\";\n  sdkPrimitive: \"MemorySession\";\n  sessionTransport: \"assistant-message metadata\";\n  supportsCrudHelpers: true;\n}\n\nexport function getOpenAiAgentsSdkDemoSessionProfile(): OpenAiAgentsSdkDemoSessionProfile {\n  return {\n    historyStorage: \"process-local\",\n    sdkPrimitive: \"MemorySession\",\n    sessionTransport: \"assistant-message metadata\",\n    supportsCrudHelpers: true,\n  };\n}\n\nfunction getMessageText(message: UIMessage) {\n  return message.parts\n    .filter((part) => part.type === \"text\")\n    .map((part) => part.text)\n    .join(\"\\n\")\n    .trim();\n}\n\nfunction createUserInputItem(content: string): AgentInputItem {\n  return {\n    content,\n    role: \"user\",\n  };\n}\n\nfunction createAssistantInputItem(content: string): AgentInputItem {\n  return {\n    content: [{ text: content, type: \"output_text\" }],\n    role: \"assistant\",\n    status: \"completed\",\n  };\n}\n\nfunction convertToAgentInput(messages: UIMessage[]): AgentInputItem[] {\n  const items: AgentInputItem[] = [];\n\n  for (const message of messages) {\n    if (message.role !== \"assistant\" && message.role !== \"user\") {\n      continue;\n    }\n\n    const content = getMessageText(message);\n\n    if (!content) {\n      continue;\n    }\n\n    items.push(\n      message.role === \"assistant\"\n        ? createAssistantInputItem(content)\n        : createUserInputItem(content)\n    );\n  }\n\n  return items;\n}\n\nfunction getLatestAssistantSessionId(messages: UIMessage[]) {\n  const latestAssistantMessage = [...messages]\n    .reverse()\n    .find((message) => message.role === \"assistant\");\n\n  const metadata = latestAssistantMessage?.metadata as\n    | OpenAiAgentsSdkDemoMessageMetadata\n    | undefined;\n\n  return metadata?.sessionSummary?.sessionId;\n}\n\nfunction getRehydrationSeedItems(messages: UIMessage[]) {\n  const sessionItems = convertToAgentInput(messages);\n  const latestMessage = messages.at(-1);\n\n  if (latestMessage?.role !== \"user\") {\n    return sessionItems;\n  }\n\n  return sessionItems.slice(0, -1);\n}\n\nasync function registerSession(session: MemorySession) {\n  const sessionId = await session.getSessionId();\n\n  sessionStore.set(sessionId, session);\n\n  return session;\n}\n\nexport async function getOpenAiAgentsSdkDemoSession(messages: UIMessage[]) {\n  const sessionId = getLatestAssistantSessionId(messages);\n\n  if (!sessionId) {\n    return registerSession(new MemorySession());\n  }\n\n  const existingSession = sessionStore.get(sessionId);\n\n  if (existingSession) {\n    return existingSession;\n  }\n\n  return registerSession(\n    new MemorySession({\n      initialItems: getRehydrationSeedItems(messages),\n      sessionId,\n    })\n  );\n}\n\nexport async function getOpenAiAgentsSdkDemoSessionUsageMetadata(\n  session: MemorySession\n) {\n  const [sessionId, historyItems] = await Promise.all([\n    session.getSessionId(),\n    session.getItems(),\n  ]);\n\n  return {\n    sessionSummary: {\n      historyItemCount: historyItems.length,\n      sessionId,\n      sessionKind: \"MemorySession\",\n      storageScope: \"process-local\",\n    },\n    usedGuideIds: [\"sessions\"],\n  } satisfies OpenAiAgentsSdkDemoMessageMetadata;\n}\n\nexport function clearOpenAiAgentsSdkDemoSessionStore() {\n  sessionStore.clear();\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/sessions.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/stream-artifacts.ts",
      "content": "import type { ModelResponse, RunItem } from \"@openai/agents\";\nimport type { UIMessageChunk } from \"ai\";\n\nconst defaultGeneratedImageMediaType = \"image/png\";\n\ninterface ImageGenerationArtifactSource {\n  id?: string;\n  name?: string;\n  output?: string;\n  result?: string | null;\n  status?: string;\n  type?: string;\n}\n\ninterface ImageGenerationRunArtifact {\n  id: string;\n  result: string;\n}\n\nfunction getImageGenerationArtifact({\n  output,\n  rawItem,\n}: {\n  output?: string;\n  rawItem: ImageGenerationArtifactSource | undefined;\n}): ImageGenerationRunArtifact | null {\n  if (!rawItem) {\n    return null;\n  }\n\n  if (\n    rawItem.type === \"image_generation_call\" &&\n    rawItem.status === \"completed\" &&\n    typeof rawItem.result === \"string\" &&\n    rawItem.result.length > 0\n  ) {\n    return {\n      id: rawItem.id ?? rawItem.result,\n      result: rawItem.result,\n    };\n  }\n\n  const hostedOutput =\n    typeof output === \"string\"\n      ? output\n      : typeof rawItem.output === \"string\"\n        ? rawItem.output\n        : null;\n\n  if (\n    rawItem.type === \"hosted_tool_call\" &&\n    rawItem.name === \"image_generation_call\" &&\n    rawItem.status === \"completed\" &&\n    hostedOutput &&\n    hostedOutput.length > 0\n  ) {\n    return {\n      id: rawItem.id ?? hostedOutput,\n      result: hostedOutput,\n    };\n  }\n\n  return null;\n}\n\nfunction appendArtifactChunk(\n  chunks: UIMessageChunk[],\n  emittedImageIds: Set<string>,\n  artifact: ImageGenerationRunArtifact | null\n) {\n  if (!artifact || emittedImageIds.has(artifact.id)) {\n    return;\n  }\n\n  emittedImageIds.add(artifact.id);\n  chunks.push({\n    mediaType: defaultGeneratedImageMediaType,\n    type: \"file\",\n    url: `data:${defaultGeneratedImageMediaType};base64,${artifact.result}`,\n  });\n}\n\nexport function getOpenAiAgentsSdkDemoArtifactChunks({\n  newItems = [],\n  rawResponses = [],\n}: {\n  newItems?: RunItem[];\n  rawResponses?: ModelResponse[];\n}): UIMessageChunk[] {\n  const emittedImageIds = new Set<string>();\n  const chunks: UIMessageChunk[] = [];\n\n  for (const item of newItems) {\n    appendArtifactChunk(\n      chunks,\n      emittedImageIds,\n      getImageGenerationArtifact({\n        output:\n          \"output\" in item && typeof item.output === \"string\"\n            ? item.output\n            : undefined,\n        rawItem: item.rawItem as ImageGenerationArtifactSource | undefined,\n      })\n    );\n  }\n\n  for (const response of rawResponses) {\n    for (const item of response.output) {\n      appendArtifactChunk(\n        chunks,\n        emittedImageIds,\n        getImageGenerationArtifact({\n          rawItem: item as ImageGenerationArtifactSource,\n        })\n      );\n    }\n  }\n\n  return chunks;\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/stream-artifacts.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/streaming.ts",
      "content": "import type { RunStreamEvent } from \"@openai/agents\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\n\nfunction pushUnique(target: string[], value: string | undefined, limit = 8) {\n  if (!value || target.includes(value)) {\n    return;\n  }\n\n  if (target.length >= limit) {\n    return;\n  }\n\n  target.push(value);\n}\n\nasync function* iterateReadableStream<T>(stream: ReadableStream<T>) {\n  const reader = stream.getReader();\n\n  try {\n    while (true) {\n      const { done, value } = await reader.read();\n\n      if (done) {\n        return;\n      }\n\n      yield value;\n    }\n  } finally {\n    reader.releaseLock();\n  }\n}\n\nfunction getAsyncIterable(\n  source: AsyncIterable<RunStreamEvent> | ReadableStream<RunStreamEvent>\n) {\n  if (Symbol.asyncIterator in source) {\n    return source as AsyncIterable<RunStreamEvent>;\n  }\n\n  return iterateReadableStream(source as ReadableStream<RunStreamEvent>);\n}\n\nexport function createOpenAiAgentsSdkDemoStreamSummaryCollector() {\n  const summary = {\n    agentNames: [] as string[],\n    rawModelEventCount: 0,\n    rawModelEventTypes: [] as string[],\n    rawModelSources: [] as string[],\n    runItemEventCount: 0,\n    runItemEventNames: [] as string[],\n  };\n\n  return {\n    observe(event: RunStreamEvent) {\n      if (event.type === \"agent_updated_stream_event\") {\n        pushUnique(summary.agentNames, event.agent.name);\n        return;\n      }\n\n      if (event.type === \"raw_model_stream_event\") {\n        summary.rawModelEventCount += 1;\n        pushUnique(summary.rawModelSources, event.source);\n        pushUnique(summary.rawModelEventTypes, event.data.type);\n        return;\n      }\n\n      if (event.type === \"run_item_stream_event\") {\n        summary.runItemEventCount += 1;\n        pushUnique(summary.runItemEventNames, event.name);\n      }\n    },\n    toMetadata(): OpenAiAgentsSdkDemoMessageMetadata | undefined {\n      const hasEvents =\n        summary.rawModelEventCount > 0 ||\n        summary.runItemEventCount > 0 ||\n        summary.agentNames.length > 0;\n\n      if (!hasEvents) {\n        return;\n      }\n\n      return {\n        streamSummary: summary,\n        usedGuideIds: [\"streaming\"],\n      };\n    },\n  };\n}\n\nexport function observeOpenAiAgentsSdkDemoStreamEvents(\n  source: AsyncIterable<RunStreamEvent> | ReadableStream<RunStreamEvent>,\n  onEvent: (event: RunStreamEvent) => void\n): AsyncIterable<RunStreamEvent> {\n  const events = getAsyncIterable(source);\n\n  return {\n    async *[Symbol.asyncIterator]() {\n      for await (const event of events) {\n        onEvent(event);\n        yield event;\n      }\n    },\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/streaming.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/tools.ts",
      "content": "import {\n  Agent,\n  codeInterpreterTool,\n  fileSearchTool,\n  imageGenerationTool,\n  type RunContext,\n  type RunItem,\n  tool,\n  toolSearchTool,\n  webSearchTool,\n} from \"@openai/agents\";\nimport { z } from \"zod\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\nimport {\n  getOpenAiAgentsSdkDemoToolContextNote,\n  type OpenAiAgentsSdkDemoContext,\n} from \"./context\";\nimport { isOpenAiAgentsSdkDemoMcpToolName } from \"./mcp\";\nimport {\n  getOpenAiAgentsSdkDemoChatModel,\n  getOpenAiAgentsSdkDemoModelProfile,\n  isOpenAiAgentsSdkDemoImageGenerationProviderBlocked,\n  type OpenAiAgentsSdkDemoModelProfile,\n  supportsOpenAiAgentsSdkToolSearch,\n} from \"./models\";\nimport { createOpenAiAgentsSdkDemoSandboxWorkspaceAgent } from \"./sandbox\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nexport type OpenAiAgentsSdkDemoToolAvailability =\n  | \"configured\"\n  | \"provider-blocked\"\n  | \"setup-required\";\n\nexport type OpenAiAgentsSdkDemoToolKind =\n  | \"agent-as-tool\"\n  | \"function\"\n  | \"hosted\";\n\nexport interface OpenAiAgentsSdkDemoToolCatalogEntry {\n  availability: OpenAiAgentsSdkDemoToolAvailability;\n  kind: OpenAiAgentsSdkDemoToolKind;\n  name: string;\n  notes: string;\n  sdkPrimitive: string;\n}\n\ninterface OpenAiAgentsSdkDemoToolCatalogOptions {\n  env?: DemoEnv;\n  isChatAvailable: boolean;\n}\n\ninterface OpenAiAgentsSdkDemoToolOptions {\n  env?: DemoEnv;\n  modelProfile: OpenAiAgentsSdkDemoModelProfile;\n}\n\nconst buildResearchBriefInput = z.object({\n  company: z.string().min(1),\n  focus: z.string().min(1).optional(),\n});\nconst researchFollowUpInput = z.object({\n  company: z.string().min(1),\n  focus: z.string().min(1).optional(),\n});\nconst riskWatchlistInput = z.object({\n  company: z.string().min(1),\n  thesis: z.string().min(1).optional(),\n});\nconst publishResearchSummaryInput = z.object({\n  audience: z.string().min(1),\n  company: z.string().min(1),\n  summary: z.string().min(1),\n});\n\nfunction getOpenAiAgentsSdkDemoVectorStoreIds(env: DemoEnv = process.env) {\n  const configuredIds = env.OPENAI_AGENTS_VECTOR_STORE_IDS;\n\n  if (!configuredIds) {\n    return [];\n  }\n\n  return Array.from(\n    new Set(\n      configuredIds\n        .split(\",\")\n        .map((id) => id.trim())\n        .filter(Boolean)\n    )\n  );\n}\n\nfunction createBuildResearchBriefTool() {\n  return tool({\n    description:\n      \"Create a compact public-company investment research brief before deeper financial analysis.\",\n    execute: (\n      { company, focus },\n      runContext?: RunContext<OpenAiAgentsSdkDemoContext>\n    ) => {\n      const researchFocus = focus?.trim() || \"overall business quality\";\n      const contextNote = getOpenAiAgentsSdkDemoToolContextNote(runContext);\n\n      return [\n        `Company: ${company}`,\n        `Focus: ${researchFocus}`,\n        \"Research brief:\",\n        \"- Start with the latest filings, earnings material, and management commentary.\",\n        \"- Confirm current facts with web search before citing them.\",\n        \"- Use code interpreter for tables, growth rates, scenario math, or valuation support.\",\n        \"- Separate business quality, financial performance, capital allocation, and key risks.\",\n        ...(contextNote ? [contextNote] : []),\n      ].join(\"\\n\");\n    },\n    name: \"build_research_brief\",\n    parameters: buildResearchBriefInput,\n  });\n}\n\nfunction createDraftFinancialFollowUpTool() {\n  return tool({\n    deferLoading: true,\n    description:\n      \"Draft follow-up financial questions for a public-company research task.\",\n    execute: ({ company, focus }) => {\n      const researchFocus = focus?.trim() || \"growth quality\";\n\n      return [\n        `Company: ${company}`,\n        `Focus: ${researchFocus}`,\n        \"Follow-up questions:\",\n        \"- Which revenue or margin drivers matter most in the next 2 to 4 quarters?\",\n        \"- Which line items need model-backed validation before citing a conclusion?\",\n        \"- Which management claims need fresh evidence from filings, earnings calls, or product updates?\",\n      ].join(\"\\n\");\n    },\n    name: \"draft_financial_follow_up\",\n    parameters: researchFollowUpInput,\n  });\n}\n\nfunction createBuildRiskWatchlistTool() {\n  return tool({\n    deferLoading: true,\n    description:\n      \"Build a compact risk watchlist for a public-company research thesis.\",\n    execute: ({ company, thesis }) => {\n      const thesisSummary = thesis?.trim() || \"the current research thesis\";\n\n      return [\n        `Company: ${company}`,\n        `Thesis: ${thesisSummary}`,\n        \"Risk watchlist:\",\n        \"- Demand and pricing pressure\",\n        \"- Execution risk across new products, production, or go-to-market changes\",\n        \"- Regulatory, legal, or capital-allocation surprises\",\n        \"- Facts that still need direct source confirmation before memo sign-off\",\n      ].join(\"\\n\");\n    },\n    name: \"build_risk_watchlist\",\n    parameters: riskWatchlistInput,\n  });\n}\n\nfunction createPublishResearchSummaryTool() {\n  return tool({\n    description:\n      \"Publish a finished research takeaway to an external audience after a human approval checkpoint.\",\n    execute: ({ audience, company, summary }) =>\n      [\n        `Audience: ${audience}`,\n        `Company: ${company}`,\n        \"Status: approved and ready to share.\",\n        `Summary: ${summary}`,\n      ].join(\"\\n\"),\n    name: \"publish_research_summary\",\n    needsApproval: true,\n    parameters: publishResearchSummaryInput,\n  });\n}\n\nfunction createResearchMemoAgentTool({\n  modelProfile,\n}: OpenAiAgentsSdkDemoToolOptions) {\n  const memoAgent = new Agent<OpenAiAgentsSdkDemoContext>({\n    instructions: (runContext) => {\n      const toolInputPreview =\n        runContext.toolInput && typeof runContext.toolInput === \"object\"\n          ? JSON.stringify(runContext.toolInput)\n          : null;\n\n      return [\n        \"You turn collected research notes into a concise investment-research memo with claims, evidence, risks, and open questions.\",\n        `Current demo run mode: ${runContext.context.researchMode}.`,\n        `Default research target when unspecified: ${runContext.context.defaultResearchTarget}.`,\n        ...(toolInputPreview\n          ? [`Current agent.asTool input: ${toolInputPreview}.`]\n          : []),\n      ].join(\" \");\n    },\n    model: modelProfile.model,\n    modelSettings: {\n      reasoning: {\n        effort: modelProfile.reasoningEffort,\n      },\n      text: {\n        verbosity: modelProfile.textVerbosity,\n      },\n    },\n    name: \"Research Memo Agent\",\n  });\n\n  return memoAgent.asTool({\n    toolDescription:\n      \"Synthesize gathered research into a compact memo with evidence, risks, and follow-up questions.\",\n    toolName: \"research_memo_agent\",\n  });\n}\n\nfunction createSandboxWorkspaceAgentTool({\n  modelProfile,\n}: OpenAiAgentsSdkDemoToolOptions) {\n  const sandboxAgent = createOpenAiAgentsSdkDemoSandboxWorkspaceAgent({\n    modelProfile,\n  });\n\n  return sandboxAgent.asTool({\n    toolDescription:\n      \"Inspect the mounted demo docs and feature slice inside a sandboxed workspace with filesystem and shell tools.\",\n    toolName: \"sandbox_workspace_agent\",\n  });\n}\n\nexport function createOpenAiAgentsSdkDemoTools({\n  env = process.env,\n  modelProfile,\n}: OpenAiAgentsSdkDemoToolOptions) {\n  const vectorStoreIds = getOpenAiAgentsSdkDemoVectorStoreIds(env);\n  const supportsToolSearch = supportsOpenAiAgentsSdkToolSearch(\n    modelProfile.model\n  );\n\n  return [\n    createBuildResearchBriefTool(),\n    createPublishResearchSummaryTool(),\n    ...(supportsToolSearch\n      ? [createDraftFinancialFollowUpTool(), createBuildRiskWatchlistTool()]\n      : []),\n    webSearchTool({\n      searchContextSize: \"medium\",\n    }),\n    ...(vectorStoreIds.length > 0\n      ? [\n          fileSearchTool(vectorStoreIds, {\n            includeSearchResults: true,\n            maxNumResults: 3,\n          }),\n        ]\n      : []),\n    codeInterpreterTool({\n      includeOutputs: true,\n    }),\n    imageGenerationTool({\n      size: \"1024x1024\",\n    }),\n    ...(supportsToolSearch ? [toolSearchTool()] : []),\n    createResearchMemoAgentTool({\n      modelProfile,\n    }),\n    createSandboxWorkspaceAgentTool({\n      modelProfile,\n    }),\n  ];\n}\n\nexport function getOpenAiAgentsSdkDemoToolCatalog({\n  env = process.env,\n  isChatAvailable,\n}: OpenAiAgentsSdkDemoToolCatalogOptions): OpenAiAgentsSdkDemoToolCatalogEntry[] {\n  const modelProfile = getOpenAiAgentsSdkDemoModelProfile(env);\n  const supportsToolSearch = supportsOpenAiAgentsSdkToolSearch(\n    getOpenAiAgentsSdkDemoChatModel(env)\n  );\n  const hasFileSearchSetup =\n    getOpenAiAgentsSdkDemoVectorStoreIds(env).length > 0 && isChatAvailable;\n  const isImageGenerationProviderBlocked =\n    isOpenAiAgentsSdkDemoImageGenerationProviderBlocked(modelProfile) &&\n    isChatAvailable;\n  const functionToolContinuationNote =\n    modelProfile.baseUrl.includes(\"ai-gateway.vercel.sh\") && isChatAvailable\n      ? \" The current AI Gateway path can reject the SDK's default run_llm_again continuation after local function tools; the demo surfaces that provider error instead of injecting a fake final answer.\"\n      : \"\";\n  const availability: OpenAiAgentsSdkDemoToolAvailability = isChatAvailable\n    ? \"configured\"\n    : \"setup-required\";\n  const deferredToolAvailability: OpenAiAgentsSdkDemoToolAvailability =\n    supportsToolSearch && isChatAvailable ? \"configured\" : \"setup-required\";\n\n  return [\n    {\n      availability,\n      kind: \"function\",\n      name: \"build_research_brief\",\n      notes: `Thin official tool() example for the Tesla-style research flow.${functionToolContinuationNote}`,\n      sdkPrimitive: \"tool()\",\n    },\n    {\n      availability,\n      kind: \"function\",\n      name: \"publish_research_summary\",\n      notes: `Official tool({ needsApproval: true }) path for a human approval interruption before sharing research externally.${functionToolContinuationNote}`,\n      sdkPrimitive: \"tool({ needsApproval: true })\",\n    },\n    {\n      availability: deferredToolAvailability,\n      kind: \"function\",\n      name: \"draft_financial_follow_up\",\n      notes: supportsToolSearch\n        ? \"Deferred top-level function tool loaded on demand through the official tool search flow.\"\n        : \"Requires an OpenAI Responses model with tool_search support, such as openai/gpt-5.4-mini or newer.\",\n      sdkPrimitive: \"tool({ deferLoading: true })\",\n    },\n    {\n      availability: deferredToolAvailability,\n      kind: \"function\",\n      name: \"build_risk_watchlist\",\n      notes: supportsToolSearch\n        ? \"Deferred top-level function tool loaded on demand through the official tool search flow.\"\n        : \"Requires an OpenAI Responses model with tool_search support, such as openai/gpt-5.4-mini or newer.\",\n      sdkPrimitive: \"tool({ deferLoading: true })\",\n    },\n    {\n      availability,\n      kind: \"hosted\",\n      name: \"web_search\",\n      notes:\n        \"Hosted OpenAI search through the current AI Gateway OpenAI-compatible Responses path.\",\n      sdkPrimitive: \"webSearchTool()\",\n    },\n    {\n      availability,\n      kind: \"hosted\",\n      name: \"code_interpreter\",\n      notes:\n        \"Hosted OpenAI code interpreter for math, tables, and quick analysis.\",\n      sdkPrimitive: \"codeInterpreterTool()\",\n    },\n    {\n      availability,\n      kind: \"agent-as-tool\",\n      name: \"research_memo_agent\",\n      notes:\n        \"Specialist sub-agent exposed through agent.asTool() for memo synthesis.\",\n      sdkPrimitive: \"agent.asTool()\",\n    },\n    {\n      availability,\n      kind: \"agent-as-tool\",\n      name: \"sandbox_workspace_agent\",\n      notes:\n        \"Official SandboxAgent specialist mounted over the demo docs and feature slice, then exposed through agent.asTool().\",\n      sdkPrimitive: \"SandboxAgent.asTool()\",\n    },\n    {\n      availability: hasFileSearchSetup ? \"configured\" : \"setup-required\",\n      kind: \"hosted\",\n      name: \"file_search\",\n      notes: hasFileSearchSetup\n        ? \"Official hosted file search backed by OPENAI_AGENTS_VECTOR_STORE_IDS.\"\n        : \"Set OPENAI_AGENTS_VECTOR_STORE_IDS to one or more comma-separated OpenAI vector store ids.\",\n      sdkPrimitive: \"fileSearchTool()\",\n    },\n    {\n      availability: isImageGenerationProviderBlocked\n        ? \"provider-blocked\"\n        : availability,\n      kind: \"hosted\",\n      name: \"image_generation\",\n      notes: isImageGenerationProviderBlocked\n        ? \"Registered through imageGenerationTool(), but blocked on the current AI Gateway Responses path. The hosted image tool can complete without a renderable image artifact, and streamed runs can terminate before the AI SDK UI bridge receives a usable file part.\"\n        : \"Official hosted image generation with assistant file output rendered in the current chat surface.\",\n      sdkPrimitive: \"imageGenerationTool()\",\n    },\n    {\n      availability: deferredToolAvailability,\n      kind: \"hosted\",\n      name: \"tool_search\",\n      notes: supportsToolSearch\n        ? \"Official hosted tool search for loading deferred namespace members on demand.\"\n        : \"Requires an OpenAI Responses model with tool_search support, such as openai/gpt-5.4-mini or newer.\",\n      sdkPrimitive: \"toolSearchTool()\",\n    },\n  ];\n}\n\nfunction getToolName(rawItem: { name?: string; type?: string }): string | null {\n  if (\n    rawItem.type === \"function_call\" ||\n    rawItem.type === \"function_call_result\" ||\n    rawItem.type === \"hosted_tool_call\"\n  ) {\n    if (!rawItem.name) {\n      return null;\n    }\n\n    if (rawItem.type === \"hosted_tool_call\" && rawItem.name.endsWith(\"_call\")) {\n      return rawItem.name.slice(0, -\"_call\".length);\n    }\n\n    return rawItem.name;\n  }\n\n  if (\n    rawItem.type === \"tool_search_call\" ||\n    rawItem.type === \"tool_search_output\"\n  ) {\n    return \"tool_search\";\n  }\n\n  if (rawItem.type === \"image_generation_call\") {\n    return \"image_generation\";\n  }\n\n  return null;\n}\n\nexport function getOpenAiAgentsSdkDemoRunUsageMetadata(\n  newItems: RunItem[]\n): OpenAiAgentsSdkDemoMessageMetadata | undefined {\n  const usedToolNames = Array.from(\n    new Set(\n      newItems\n        .map((item) =>\n          getToolName((item.rawItem ?? {}) as { name?: string; type?: string })\n        )\n        .filter((value): value is string => Boolean(value))\n    )\n  );\n\n  if (usedToolNames.length === 0) {\n    return;\n  }\n\n  const usedGuideIds = [\"tools\"];\n\n  if (usedToolNames.includes(\"research_memo_agent\")) {\n    usedGuideIds.push(\"agent-orchestration\");\n  }\n\n  if (usedToolNames.some((name) => isOpenAiAgentsSdkDemoMcpToolName(name))) {\n    usedGuideIds.push(\"mcp\");\n  }\n\n  return {\n    usedGuideIds,\n    usedToolNames,\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/tools.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/tracing.ts",
      "content": "import { generateTraceId } from \"@openai/agents\";\nimport type { UIMessage } from \"ai\";\nimport { z } from \"zod\";\n\nimport type { OpenAiAgentsSdkDemoMessageMetadata } from \"../message-metadata\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst traceDisableEnvVar = \"OPENAI_AGENTS_DISABLE_TRACING\";\nconst traceSensitiveDataEnvVar = \"OPENAI_AGENTS_TRACE_INCLUDE_SENSITIVE_DATA\";\nconst traceApiKeyEnvVar = \"OPENAI_AGENTS_TRACING_API_KEY\";\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\";\n\nexport const openAiAgentsSdkDemoTraceSummarySchema = z.object({\n  exportApiKeySource: z.enum([\n    \"OPENAI_AGENTS_TRACING_API_KEY\",\n    \"OPENAI_API_KEY\",\n    \"missing\",\n  ]),\n  groupId: z.string().optional(),\n  metadataKeys: z.array(z.string()),\n  traceId: z.string(),\n  traceIncludeSensitiveData: z.boolean(),\n  tracingDisabled: z.boolean(),\n  workflowName: z.string(),\n});\n\nexport interface OpenAiAgentsSdkDemoTraceProfile {\n  defaultServerRuntimeTracing: \"enabled\";\n  disableEnvVar: typeof traceDisableEnvVar;\n  exportApiKeySource:\n    | \"OPENAI_AGENTS_TRACING_API_KEY\"\n    | \"OPENAI_API_KEY\"\n    | \"missing\";\n  groupingStrategy: \"session-id\";\n  sdkPrimitives: [\n    \"generateTraceId\",\n    \"run({ workflowName, traceId, groupId, traceMetadata, tracingDisabled, traceIncludeSensitiveData, tracing })\",\n  ];\n  traceIncludeSensitiveData: boolean;\n  tracingDisabled: boolean;\n  usesPerRunExportOverride: true;\n  workflowNameSource: \"RunConfig.workflowName\";\n}\n\nfunction getTracingExportApiKeySource(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoTraceProfile[\"exportApiKeySource\"] {\n  if (env[traceApiKeyEnvVar]) {\n    return traceApiKeyEnvVar;\n  }\n\n  if (env[openAiApiKeyEnvVar]) {\n    return openAiApiKeyEnvVar;\n  }\n\n  return \"missing\";\n}\n\nfunction getTracingExportApiKey(env: DemoEnv = process.env) {\n  return env[traceApiKeyEnvVar] ?? env[openAiApiKeyEnvVar] ?? undefined;\n}\n\nfunction isTracingDisabled(env: DemoEnv = process.env) {\n  return env[traceDisableEnvVar] === \"1\";\n}\n\nfunction shouldIncludeSensitiveData(env: DemoEnv = process.env) {\n  return env[traceSensitiveDataEnvVar] !== \"0\";\n}\n\nfunction getLatestAssistantTraceSummary(messages: UIMessage[]) {\n  return (\n    [...messages].reverse().find((message) => message.role === \"assistant\")\n      ?.metadata as OpenAiAgentsSdkDemoMessageMetadata | undefined\n  )?.traceSummary;\n}\n\nexport function getOpenAiAgentsSdkDemoTraceProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoTraceProfile {\n  return {\n    defaultServerRuntimeTracing: \"enabled\",\n    disableEnvVar: traceDisableEnvVar,\n    exportApiKeySource: getTracingExportApiKeySource(env),\n    groupingStrategy: \"session-id\",\n    sdkPrimitives: [\n      \"generateTraceId\",\n      \"run({ workflowName, traceId, groupId, traceMetadata, tracingDisabled, traceIncludeSensitiveData, tracing })\",\n    ],\n    traceIncludeSensitiveData: shouldIncludeSensitiveData(env),\n    tracingDisabled: isTracingDisabled(env),\n    usesPerRunExportOverride: true,\n    workflowNameSource: \"RunConfig.workflowName\",\n  };\n}\n\nexport function createOpenAiAgentsSdkDemoTraceRunConfig({\n  env = process.env,\n  sessionId,\n  workflowName,\n}: {\n  env?: DemoEnv;\n  sessionId?: string;\n  workflowName: string;\n}) {\n  const exportApiKey = getTracingExportApiKey(env);\n  const summary = {\n    exportApiKeySource: getTracingExportApiKeySource(env),\n    ...(sessionId ? { groupId: sessionId } : {}),\n    metadataKeys: [\"demo\", \"session_id\"],\n    traceId: generateTraceId(),\n    traceIncludeSensitiveData: shouldIncludeSensitiveData(env),\n    tracingDisabled: isTracingDisabled(env),\n    workflowName,\n  } satisfies z.infer<typeof openAiAgentsSdkDemoTraceSummarySchema>;\n\n  return {\n    options: {\n      ...(sessionId ? { groupId: sessionId } : {}),\n      traceId: summary.traceId,\n      traceIncludeSensitiveData: summary.traceIncludeSensitiveData,\n      traceMetadata: {\n        demo: \"openai-agents-sdk-demo\",\n        session_id: sessionId ?? \"unknown\",\n      },\n      tracingDisabled: summary.tracingDisabled,\n      ...(exportApiKey ? { tracing: { apiKey: exportApiKey } } : {}),\n      workflowName: summary.workflowName,\n    },\n    summary,\n  };\n}\n\nexport function getOpenAiAgentsSdkDemoTraceUsageMetadata(\n  summary: z.infer<typeof openAiAgentsSdkDemoTraceSummarySchema> | undefined\n) {\n  if (!summary) {\n    return;\n  }\n\n  return {\n    traceSummary: summary,\n    usedGuideIds: [\"tracing\"],\n  } satisfies OpenAiAgentsSdkDemoMessageMetadata;\n}\n\nexport function getOpenAiAgentsSdkDemoLatestTraceUsageMetadata(\n  messages: UIMessage[]\n) {\n  return getOpenAiAgentsSdkDemoTraceUsageMetadata(\n    getLatestAssistantTraceSummary(messages)\n  );\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/tracing.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-cloudflare-app.ts",
      "content": "import {\n  createOpenAiAgentsSdkDemoCloudflareWorkerRuntime,\n  type OpenAiAgentsSdkDemoCloudflareWorkerRuntime,\n} from \"./voice-cloudflare-worker\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\" as const;\nconst cloudflareWorkflowName =\n  \"openai-agents-sdk-demo-voice-cloudflare\" as const;\nconst cloudflareRootRoutePath = \"/\" as const;\nconst cloudflareConnectRoutePath = \"/connect\" as const;\nconst cloudflareHealthcheckMessage =\n  \"Cloudflare Realtime Worker is running!\" as const;\n\nexport interface OpenAiAgentsSdkDemoCloudflareWorkerAppProfile {\n  connectRoutePath: typeof cloudflareConnectRoutePath;\n  healthcheckMessage: typeof cloudflareHealthcheckMessage;\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  publicTransportContract: \"deployed-worker-fetch-handler\";\n  rootRoutePath: typeof cloudflareRootRoutePath;\n  sdkPrimitive: \"CloudflareRealtimeTransportLayer + RealtimeSession\";\n  serverPrimitive: \"export default { fetch(request, env, ctx) }\";\n  sourceGuide: \"https://openai.github.io/openai-agents-js/extensions/cloudflare/\";\n  status: \"configured\" | \"setup-required\";\n  websocketUpgradePrimitive: \"fetch() + Upgrade: websocket\";\n  workerCompatibilityFlag: \"nodejs_compat\";\n  workflowName: typeof cloudflareWorkflowName;\n}\n\nexport interface OpenAiAgentsSdkDemoCloudflareWorkerApp {\n  handleRequest: (request: Request) => Promise<Response>;\n  profile: OpenAiAgentsSdkDemoCloudflareWorkerAppProfile;\n}\n\ninterface OpenAiAgentsSdkDemoCloudflareWorkerAppDependencies {\n  createRuntime?: (env?: DemoEnv) => OpenAiAgentsSdkDemoCloudflareWorkerRuntime;\n}\n\nfunction buildUpgradeRequiredResponse() {\n  return Response.json(\n    {\n      error:\n        \"Cloudflare worker connect route requires Upgrade: websocket so the deployed runtime matches the official workerd transport contract.\",\n    },\n    { status: 426 }\n  );\n}\n\nfunction isWebSocketUpgrade(request: Request) {\n  return request.headers.get(\"upgrade\")?.toLowerCase() === \"websocket\";\n}\n\nexport function getOpenAiAgentsSdkDemoCloudflareWorkerAppProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoCloudflareWorkerAppProfile {\n  return {\n    connectRoutePath: cloudflareConnectRoutePath,\n    healthcheckMessage: cloudflareHealthcheckMessage,\n    openAiApiKeyEnvVar,\n    publicTransportContract: \"deployed-worker-fetch-handler\",\n    rootRoutePath: cloudflareRootRoutePath,\n    sdkPrimitive: \"CloudflareRealtimeTransportLayer + RealtimeSession\",\n    serverPrimitive: \"export default { fetch(request, env, ctx) }\",\n    sourceGuide:\n      \"https://openai.github.io/openai-agents-js/extensions/cloudflare/\",\n    status: env[openAiApiKeyEnvVar] ? \"configured\" : \"setup-required\",\n    websocketUpgradePrimitive: \"fetch() + Upgrade: websocket\",\n    workerCompatibilityFlag: \"nodejs_compat\",\n    workflowName: cloudflareWorkflowName,\n  };\n}\n\nexport function createOpenAiAgentsSdkDemoCloudflareWorkerApp(\n  env: DemoEnv = process.env,\n  dependencies: OpenAiAgentsSdkDemoCloudflareWorkerAppDependencies = {}\n): OpenAiAgentsSdkDemoCloudflareWorkerApp {\n  const createRuntime =\n    dependencies.createRuntime ??\n    ((environment: DemoEnv) =>\n      createOpenAiAgentsSdkDemoCloudflareWorkerRuntime(environment));\n\n  return {\n    handleRequest: async (request) => {\n      const pathname = new URL(request.url).pathname;\n\n      if (pathname === cloudflareRootRoutePath) {\n        return Response.json({\n          message: cloudflareHealthcheckMessage,\n        });\n      }\n\n      if (pathname !== cloudflareConnectRoutePath) {\n        return new Response(\"Not Found\", { status: 404 });\n      }\n\n      if (!isWebSocketUpgrade(request)) {\n        return buildUpgradeRequiredResponse();\n      }\n\n      const runtime = createRuntime(env);\n\n      await runtime.connect();\n\n      try {\n        return Response.json({\n          state: runtime.getState(),\n          transportName: runtime.transportName,\n          workflowName: runtime.profile.workflowName,\n        });\n      } finally {\n        runtime.close();\n      }\n    },\n    profile: getOpenAiAgentsSdkDemoCloudflareWorkerAppProfile(env),\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-cloudflare-app.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-cloudflare-worker-module.ts",
      "content": "import {\n  createOpenAiAgentsSdkDemoCloudflareWorkerApp,\n  type OpenAiAgentsSdkDemoCloudflareWorkerApp,\n} from \"./voice-cloudflare-app\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\" as const;\nconst cloudflareWorkflowName =\n  \"openai-agents-sdk-demo-voice-cloudflare\" as const;\n\nexport interface OpenAiAgentsSdkDemoCloudflareWorkerModuleProfile {\n  modulePrimitive: \"export default { fetch(request, env, ctx) }\";\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  runtimeContract: \"cloudflare-worker-module\";\n  sdkPrimitive: \"CloudflareRealtimeTransportLayer + RealtimeSession\";\n  sourceGuide: \"https://openai.github.io/openai-agents-js/extensions/cloudflare/\";\n  status: \"configured\" | \"setup-required\";\n  workflowName: typeof cloudflareWorkflowName;\n}\n\nexport interface OpenAiAgentsSdkDemoCloudflareWorkerModule {\n  fetch: (\n    request: Request,\n    env: DemoEnv,\n    ctx: {\n      waitUntil?: (promise: Promise<unknown>) => void;\n    }\n  ) => Promise<Response>;\n  profile: OpenAiAgentsSdkDemoCloudflareWorkerModuleProfile;\n}\n\ninterface OpenAiAgentsSdkDemoCloudflareWorkerModuleDependencies {\n  createApp?: (env?: DemoEnv) => OpenAiAgentsSdkDemoCloudflareWorkerApp;\n}\n\nexport function getOpenAiAgentsSdkDemoCloudflareWorkerModuleProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoCloudflareWorkerModuleProfile {\n  return {\n    modulePrimitive: \"export default { fetch(request, env, ctx) }\",\n    openAiApiKeyEnvVar,\n    runtimeContract: \"cloudflare-worker-module\",\n    sdkPrimitive: \"CloudflareRealtimeTransportLayer + RealtimeSession\",\n    sourceGuide:\n      \"https://openai.github.io/openai-agents-js/extensions/cloudflare/\",\n    status: env[openAiApiKeyEnvVar] ? \"configured\" : \"setup-required\",\n    workflowName: cloudflareWorkflowName,\n  };\n}\n\nexport function createOpenAiAgentsSdkDemoCloudflareWorkerModule(\n  defaultEnv: DemoEnv = process.env,\n  dependencies: OpenAiAgentsSdkDemoCloudflareWorkerModuleDependencies = {}\n): OpenAiAgentsSdkDemoCloudflareWorkerModule {\n  const createApp =\n    dependencies.createApp ??\n    ((env: DemoEnv) => createOpenAiAgentsSdkDemoCloudflareWorkerApp(env));\n\n  return {\n    fetch: async (request, env) => {\n      const app = createApp({\n        ...defaultEnv,\n        ...env,\n      });\n\n      return app.handleRequest(request);\n    },\n    profile: getOpenAiAgentsSdkDemoCloudflareWorkerModuleProfile(defaultEnv),\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-cloudflare-worker-module.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-cloudflare-worker.ts",
      "content": "import type { RealtimeSession } from \"@openai/agents/realtime\";\n\nimport {\n  buildOpenAiAgentsSdkDemoCloudflareVoiceSession,\n  type OpenAiAgentsSdkDemoVoiceExtensionHandle,\n} from \"./voice-extensions\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\" as const;\nconst cloudflareWorkflowName =\n  \"openai-agents-sdk-demo-voice-cloudflare\" as const;\n\ntype CloudflareSessionLike = Pick<RealtimeSession, \"close\" | \"connect\">;\n\nexport interface OpenAiAgentsSdkDemoCloudflareWorkerProfile {\n  connectPrimitive: \"RealtimeSession.connect({ apiKey, model })\";\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  openEventBehavior: \"skipOpenEventListeners: true\";\n  runtimeEntryPoint: \"Cloudflare Worker fetch()\";\n  sdkPrimitive: \"CloudflareRealtimeTransportLayer + RealtimeSession\";\n  sourceGuide: \"https://openai.github.io/openai-agents-js/extensions/cloudflare/\";\n  status: \"configured\" | \"setup-required\";\n  transport: \"WebSocket\";\n  websocketUpgradePrimitive: \"fetch() + Upgrade: websocket\";\n  workerCompatibilityFlag: \"nodejs_compat\";\n  workflowName: typeof cloudflareWorkflowName;\n}\n\nexport interface OpenAiAgentsSdkDemoCloudflareWorkerState {\n  closeCount: number;\n  connectCount: number;\n  isConnected: boolean;\n}\n\nexport interface OpenAiAgentsSdkDemoCloudflareWorkerRuntime {\n  close: () => void;\n  connect: () => Promise<void>;\n  getState: () => OpenAiAgentsSdkDemoCloudflareWorkerState;\n  profile: OpenAiAgentsSdkDemoCloudflareWorkerProfile;\n  session: CloudflareSessionLike;\n  transportName: string;\n}\n\ninterface OpenAiAgentsSdkDemoCloudflareWorkerDependencies {\n  buildSessionHandle?: (env?: DemoEnv) =>\n    | OpenAiAgentsSdkDemoVoiceExtensionHandle<any>\n    | {\n        connectOptions: {\n          apiKey: string;\n          model: string;\n        };\n        profile: {\n          status: \"configured\" | \"setup-required\";\n        };\n        session: CloudflareSessionLike;\n        transport: {\n          constructor: {\n            name: string;\n          };\n        };\n      };\n}\n\nexport function getOpenAiAgentsSdkDemoCloudflareWorkerProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoCloudflareWorkerProfile {\n  return {\n    connectPrimitive: \"RealtimeSession.connect({ apiKey, model })\",\n    openAiApiKeyEnvVar,\n    openEventBehavior: \"skipOpenEventListeners: true\",\n    runtimeEntryPoint: \"Cloudflare Worker fetch()\",\n    sdkPrimitive: \"CloudflareRealtimeTransportLayer + RealtimeSession\",\n    sourceGuide:\n      \"https://openai.github.io/openai-agents-js/extensions/cloudflare/\",\n    status: env[openAiApiKeyEnvVar] ? \"configured\" : \"setup-required\",\n    transport: \"WebSocket\",\n    websocketUpgradePrimitive: \"fetch() + Upgrade: websocket\",\n    workerCompatibilityFlag: \"nodejs_compat\",\n    workflowName: cloudflareWorkflowName,\n  };\n}\n\nexport function createOpenAiAgentsSdkDemoCloudflareWorkerRuntime(\n  env: DemoEnv = process.env,\n  dependencies: OpenAiAgentsSdkDemoCloudflareWorkerDependencies = {}\n): OpenAiAgentsSdkDemoCloudflareWorkerRuntime {\n  const buildSessionHandle =\n    dependencies.buildSessionHandle ??\n    buildOpenAiAgentsSdkDemoCloudflareVoiceSession;\n  const handle = buildSessionHandle(env);\n  const state: OpenAiAgentsSdkDemoCloudflareWorkerState = {\n    closeCount: 0,\n    connectCount: 0,\n    isConnected: false,\n  };\n\n  return {\n    close: () => {\n      state.closeCount += 1;\n      state.isConnected = false;\n      handle.session.close();\n    },\n    connect: async () => {\n      await handle.session.connect(handle.connectOptions);\n      state.connectCount += 1;\n      state.isConnected = true;\n    },\n    getState: () => ({\n      ...state,\n    }),\n    profile: getOpenAiAgentsSdkDemoCloudflareWorkerProfile(env),\n    session: handle.session,\n    transportName: handle.transport.constructor.name,\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-cloudflare-worker.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-extensions.ts",
      "content": "import {\n  RealtimeSession,\n  type RealtimeSessionConnectOptions,\n  type RealtimeTransportLayer,\n} from \"@openai/agents/realtime\";\nimport {\n  CloudflareRealtimeTransportLayer,\n  TwilioRealtimeTransportLayer,\n} from \"@openai/agents-extensions\";\n\nimport { createOpenAiAgentsSdkDemoVoiceAgentBundle } from \"../voice-lane\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\" as const;\nconst defaultVoiceSessionModel = \"gpt-realtime-2\" as const;\nconst defaultVoiceSessionVoice = \"marin\" as const;\nconst cloudflareWorkflowName =\n  \"openai-agents-sdk-demo-voice-cloudflare\" as const;\nconst twilioWorkflowName = \"openai-agents-sdk-demo-voice-twilio\" as const;\n\nexport interface OpenAiAgentsSdkDemoVoiceExtensionProfile {\n  credentialContract: \"server-api-key\";\n  id: \"cloudflare\" | \"twilio\";\n  label: \"Cloudflare Workers\" | \"Twilio Media Streams\";\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  runtimeContract:\n    | \"bring-your-own-websocket-server\"\n    | \"cloudflare-worker-runtime\";\n  sdkPrimitive:\n    | \"CloudflareRealtimeTransportLayer\"\n    | \"TwilioRealtimeTransportLayer\";\n  sourceGuide: string;\n  status: \"configured\" | \"setup-required\";\n  transport: \"WebSocket\";\n  workflowName: typeof cloudflareWorkflowName | typeof twilioWorkflowName;\n}\n\nexport interface OpenAiAgentsSdkDemoVoiceExtensionHandle<\n  TTransport extends\n    | CloudflareRealtimeTransportLayer\n    | TwilioRealtimeTransportLayer,\n> {\n  connectOptions: RealtimeSessionConnectOptions;\n  profile: OpenAiAgentsSdkDemoVoiceExtensionProfile;\n  session: RealtimeSession;\n  transport: TTransport;\n}\n\ntype TwilioTransportSocket = ConstructorParameters<\n  typeof TwilioRealtimeTransportLayer\n>[0][\"twilioWebSocket\"];\n\nfunction getRequiredOpenAiApiKey(env: DemoEnv = process.env) {\n  const apiKey = env[openAiApiKeyEnvVar];\n\n  if (!apiKey) {\n    throw new Error(\n      \"OPENAI_API_KEY is missing. Provider-specific voice transports require a native OpenAI API key.\"\n    );\n  }\n\n  return apiKey;\n}\n\nfunction getVoiceExtensionProfileStatus(env: DemoEnv = process.env) {\n  return env[openAiApiKeyEnvVar] ? \"configured\" : \"setup-required\";\n}\n\nfunction getOpenAiAgentsSdkDemoCloudflareVoiceExtensionProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoVoiceExtensionProfile {\n  return {\n    credentialContract: \"server-api-key\",\n    id: \"cloudflare\",\n    label: \"Cloudflare Workers\",\n    openAiApiKeyEnvVar,\n    runtimeContract: \"cloudflare-worker-runtime\",\n    sdkPrimitive: \"CloudflareRealtimeTransportLayer\",\n    sourceGuide:\n      \"https://openai.github.io/openai-agents-js/extensions/cloudflare/\",\n    status: getVoiceExtensionProfileStatus(env),\n    transport: \"WebSocket\",\n    workflowName: cloudflareWorkflowName,\n  };\n}\n\nfunction getOpenAiAgentsSdkDemoTwilioVoiceExtensionProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoVoiceExtensionProfile {\n  return {\n    credentialContract: \"server-api-key\",\n    id: \"twilio\",\n    label: \"Twilio Media Streams\",\n    openAiApiKeyEnvVar,\n    runtimeContract: \"bring-your-own-websocket-server\",\n    sdkPrimitive: \"TwilioRealtimeTransportLayer\",\n    sourceGuide: \"https://openai.github.io/openai-agents-js/extensions/twilio/\",\n    status: getVoiceExtensionProfileStatus(env),\n    transport: \"WebSocket\",\n    workflowName: twilioWorkflowName,\n  };\n}\n\nfunction createOpenAiAgentsSdkDemoVoiceSession({\n  transport,\n  workflowName,\n}: {\n  transport: RealtimeTransportLayer;\n  workflowName: typeof cloudflareWorkflowName | typeof twilioWorkflowName;\n}) {\n  const { primaryAgent } = createOpenAiAgentsSdkDemoVoiceAgentBundle();\n\n  return new RealtimeSession(primaryAgent, {\n    config: {\n      audio: {\n        output: {\n          voice: defaultVoiceSessionVoice,\n        },\n      },\n    },\n    model: defaultVoiceSessionModel,\n    transport,\n    workflowName,\n  });\n}\n\nexport function getOpenAiAgentsSdkDemoVoiceExtensionProfiles(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoVoiceExtensionProfile[] {\n  return [\n    getOpenAiAgentsSdkDemoCloudflareVoiceExtensionProfile(env),\n    getOpenAiAgentsSdkDemoTwilioVoiceExtensionProfile(env),\n  ];\n}\n\nexport function buildOpenAiAgentsSdkDemoCloudflareVoiceSession(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoVoiceExtensionHandle<CloudflareRealtimeTransportLayer> {\n  const apiKey = getRequiredOpenAiApiKey(env);\n  const profile = getOpenAiAgentsSdkDemoCloudflareVoiceExtensionProfile(env);\n  const transport = new CloudflareRealtimeTransportLayer({\n    useInsecureApiKey: true,\n  });\n  const session = createOpenAiAgentsSdkDemoVoiceSession({\n    transport,\n    workflowName: cloudflareWorkflowName,\n  });\n\n  return {\n    connectOptions: {\n      apiKey,\n      model: defaultVoiceSessionModel,\n    },\n    profile,\n    session,\n    transport,\n  };\n}\n\nexport function buildOpenAiAgentsSdkDemoTwilioVoiceSession({\n  env = process.env,\n  twilioWebSocket,\n}: {\n  env?: DemoEnv;\n  twilioWebSocket: TwilioTransportSocket;\n}): OpenAiAgentsSdkDemoVoiceExtensionHandle<TwilioRealtimeTransportLayer> {\n  const apiKey = getRequiredOpenAiApiKey(env);\n  const profile = getOpenAiAgentsSdkDemoTwilioVoiceExtensionProfile(env);\n  const transport = new TwilioRealtimeTransportLayer({\n    twilioWebSocket,\n    useInsecureApiKey: true,\n  });\n  const session = createOpenAiAgentsSdkDemoVoiceSession({\n    transport,\n    workflowName: twilioWorkflowName,\n  });\n\n  return {\n    connectOptions: {\n      apiKey,\n      model: defaultVoiceSessionModel,\n    },\n    profile,\n    session,\n    transport,\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-extensions.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-realtime.ts",
      "content": "import { createHash } from \"node:crypto\";\n\nimport OpenAI from \"openai\";\nimport type { ClientSecretCreateResponse } from \"openai/resources/realtime\";\nimport { z } from \"zod\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\";\nconst voiceClientSecretRoutePath =\n  \"/api/demos/openai-agents-sdk-demo/realtime/client-secrets\" as const;\nconst defaultVoiceSessionModel = \"gpt-realtime-2\" as const;\nconst defaultVoiceSessionVoice = \"marin\" as const;\nconst defaultVoiceClientSecretTtlSeconds = 600 as const;\nconst invalidRequestBodyError =\n  'Expected a JSON body with an optional \"sessionId\" string.';\n\nconst voiceClientSecretRequestBodySchema = z.object({\n  sessionId: z.string().trim().min(1).max(200).optional(),\n});\n\ninterface OpenAiAgentsSdkDemoVoiceClientSecretRequestBody {\n  sessionId?: string;\n}\n\nexport interface OpenAiAgentsSdkDemoVoiceClientSecretRouteState {\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  routePath: typeof voiceClientSecretRoutePath;\n  sdkPrimitive: \"client.realtime.clientSecrets.create()\";\n  sessionModel: typeof defaultVoiceSessionModel;\n  sessionVoice: typeof defaultVoiceSessionVoice;\n  status: \"configured\" | \"setup-required\";\n}\n\nexport type OpenAiAgentsSdkDemoVoiceClientSecretResponse =\n  ClientSecretCreateResponse;\n\ninterface OpenAiAgentsSdkDemoVoiceClientSecretDependencies {\n  createClientSecret: (options: {\n    apiKey: string;\n    params: {\n      expires_after: {\n        anchor: \"created_at\";\n        seconds: typeof defaultVoiceClientSecretTtlSeconds;\n      };\n      session: {\n        audio: {\n          output: {\n            voice: typeof defaultVoiceSessionVoice;\n          };\n        };\n        model: typeof defaultVoiceSessionModel;\n        type: \"realtime\";\n      };\n    };\n    safetyIdentifier: string;\n  }) => Promise<OpenAiAgentsSdkDemoVoiceClientSecretResponse>;\n}\n\nfunction getRequiredOpenAiApiKey(env: DemoEnv = process.env) {\n  const apiKey = env[openAiApiKeyEnvVar];\n\n  if (!apiKey) {\n    throw new Error(\n      \"OPENAI_API_KEY is missing. Voice Agents client-secret minting requires a native OpenAI API key; AI Gateway keys do not work for this realtime route.\"\n    );\n  }\n\n  return apiKey;\n}\n\nfunction getOpenAiAgentsSdkDemoVoiceClientSecretParams() {\n  return {\n    expires_after: {\n      anchor: \"created_at\" as const,\n      seconds: defaultVoiceClientSecretTtlSeconds,\n    },\n    session: {\n      audio: {\n        output: {\n          voice: defaultVoiceSessionVoice,\n        },\n      },\n      model: defaultVoiceSessionModel,\n      type: \"realtime\" as const,\n    },\n  };\n}\n\nfunction getOpenAiAgentsSdkDemoVoiceSafetyIdentifier(sessionId?: string) {\n  return createHash(\"sha256\")\n    .update(`openai-agents-sdk-demo:${sessionId ?? \"anonymous\"}`)\n    .digest(\"hex\");\n}\n\nasync function createOpenAiAgentsSdkDemoVoiceClientSecret({\n  apiKey,\n  params,\n  safetyIdentifier,\n}: Parameters<\n  OpenAiAgentsSdkDemoVoiceClientSecretDependencies[\"createClientSecret\"]\n>[0]): Promise<OpenAiAgentsSdkDemoVoiceClientSecretResponse> {\n  const client = new OpenAI({\n    apiKey,\n  });\n\n  const response = await client.realtime.clientSecrets.create(params, {\n    headers: {\n      \"OpenAI-Safety-Identifier\": safetyIdentifier,\n    },\n  });\n\n  return response;\n}\n\nasync function readOpenAiAgentsSdkDemoVoiceClientSecretBody(request: Request) {\n  const bodyText = await request.text();\n\n  if (!bodyText.trim()) {\n    return {} satisfies OpenAiAgentsSdkDemoVoiceClientSecretRequestBody;\n  }\n\n  try {\n    return voiceClientSecretRequestBodySchema.parse(JSON.parse(bodyText));\n  } catch {\n    throw new Error(invalidRequestBodyError);\n  }\n}\n\nexport function getOpenAiAgentsSdkDemoVoiceClientSecretRouteState(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoVoiceClientSecretRouteState {\n  return {\n    openAiApiKeyEnvVar,\n    routePath: voiceClientSecretRoutePath,\n    sdkPrimitive: \"client.realtime.clientSecrets.create()\",\n    sessionModel: defaultVoiceSessionModel,\n    sessionVoice: defaultVoiceSessionVoice,\n    status: env[openAiApiKeyEnvVar] ? \"configured\" : \"setup-required\",\n  };\n}\n\nexport async function handleOpenAiAgentsSdkDemoVoiceClientSecretRequest(\n  request: Request,\n  env: DemoEnv = process.env,\n  dependencies: OpenAiAgentsSdkDemoVoiceClientSecretDependencies = {\n    createClientSecret: createOpenAiAgentsSdkDemoVoiceClientSecret,\n  }\n) {\n  try {\n    const body = await readOpenAiAgentsSdkDemoVoiceClientSecretBody(request);\n    const params = getOpenAiAgentsSdkDemoVoiceClientSecretParams();\n    const response = await dependencies.createClientSecret({\n      apiKey: getRequiredOpenAiApiKey(env),\n      params,\n      safetyIdentifier: getOpenAiAgentsSdkDemoVoiceSafetyIdentifier(\n        body.sessionId\n      ),\n    });\n\n    return Response.json(response);\n  } catch (error) {\n    const message =\n      error instanceof Error ? error.message : \"Failed to mint client secret.\";\n    const status = message === invalidRequestBodyError ? 400 : 500;\n\n    return Response.json(\n      {\n        error: message,\n      },\n      { status }\n    );\n  }\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-realtime.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-server-audio.ts",
      "content": "import type {\n  RealtimeSession,\n  RealtimeSessionConnectOptions,\n  TransportEvent,\n  TransportLayerAudio,\n} from \"@openai/agents/realtime\";\n\nimport {\n  buildOpenAiAgentsSdkDemoServerVoiceSession,\n  type OpenAiAgentsSdkDemoServerVoiceSessionHandle,\n} from \"./voice-websocket\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\ntype VoiceSessionLike = Pick<\n  RealtimeSession,\n  \"close\" | \"connect\" | \"interrupt\" | \"mute\" | \"on\" | \"sendAudio\" | \"transport\"\n>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\" as const;\nconst defaultVoiceSessionModel = \"gpt-realtime-2\" as const;\nconst defaultVoiceSessionVoice = \"marin\" as const;\nconst defaultVoiceWorkflowName =\n  \"openai-agents-sdk-demo-voice-websocket\" as const;\n\nexport interface OpenAiAgentsSdkDemoServerAudioLaneProfile {\n  inputPrimitive: \"RealtimeSession.sendAudio()\";\n  interruptPrimitive: \"RealtimeSession.interrupt()\";\n  model: typeof defaultVoiceSessionModel;\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  outputAudioEvent: \"session.on('audio')\";\n  outputTranscriptEvent: \"session.on('transport_event')\";\n  requestResponsePrimitive: \"session.transport.requestResponse()\";\n  sessionVoice: typeof defaultVoiceSessionVoice;\n  sourceGuide: \"https://openai.github.io/openai-agents-js/guides/voice-agents/transport/\";\n  status: \"configured\" | \"setup-required\";\n  transport: \"WebSocket\";\n  workflowName: typeof defaultVoiceWorkflowName;\n}\n\nexport interface OpenAiAgentsSdkDemoServerAudioLaneState {\n  interruptionCount: number;\n  isOutputActive: boolean;\n  outputAudioChunkCount: number;\n  transportEventCount: number;\n}\n\nexport interface OpenAiAgentsSdkDemoServerAudioLane {\n  close: () => void;\n  connect: () => Promise<void>;\n  getState: () => OpenAiAgentsSdkDemoServerAudioLaneState;\n  interrupt: () => void;\n  mute: (muted: boolean) => void;\n  profile: OpenAiAgentsSdkDemoServerAudioLaneProfile;\n  requestResponse: (response?: Record<string, unknown>) => void;\n  sendAudio: (audio: ArrayBuffer, options?: { commit?: boolean }) => void;\n  session: VoiceSessionLike;\n  takeOutputAudio: () => TransportLayerAudio[];\n  takeTransportEvents: () => TransportEvent[];\n}\n\ninterface OpenAiAgentsSdkDemoServerAudioLaneDependencies {\n  buildSessionHandle?: (env: DemoEnv) =>\n    | OpenAiAgentsSdkDemoServerVoiceSessionHandle\n    | {\n        connectOptions: RealtimeSessionConnectOptions;\n        profile: OpenAiAgentsSdkDemoServerVoiceSessionHandle[\"profile\"];\n        session: VoiceSessionLike;\n      };\n}\n\nfunction cloneAudioEvent(event: TransportLayerAudio): TransportLayerAudio {\n  return {\n    data: event.data.slice(0),\n    responseId: event.responseId,\n    type: event.type,\n  };\n}\n\nexport function getOpenAiAgentsSdkDemoServerAudioLaneProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoServerAudioLaneProfile {\n  return {\n    inputPrimitive: \"RealtimeSession.sendAudio()\",\n    interruptPrimitive: \"RealtimeSession.interrupt()\",\n    model: defaultVoiceSessionModel,\n    openAiApiKeyEnvVar,\n    outputAudioEvent: \"session.on('audio')\",\n    outputTranscriptEvent: \"session.on('transport_event')\",\n    requestResponsePrimitive: \"session.transport.requestResponse()\",\n    sessionVoice: defaultVoiceSessionVoice,\n    sourceGuide:\n      \"https://openai.github.io/openai-agents-js/guides/voice-agents/transport/\",\n    status: env[openAiApiKeyEnvVar] ? \"configured\" : \"setup-required\",\n    transport: \"WebSocket\",\n    workflowName: defaultVoiceWorkflowName,\n  };\n}\n\nexport function buildOpenAiAgentsSdkDemoServerAudioLane(\n  env: DemoEnv = process.env,\n  dependencies: OpenAiAgentsSdkDemoServerAudioLaneDependencies = {}\n): OpenAiAgentsSdkDemoServerAudioLane {\n  const buildSessionHandle =\n    dependencies.buildSessionHandle ??\n    buildOpenAiAgentsSdkDemoServerVoiceSession;\n  const handle = buildSessionHandle(env);\n  const outputAudio: TransportLayerAudio[] = [];\n  const transportEvents: TransportEvent[] = [];\n  const state: OpenAiAgentsSdkDemoServerAudioLaneState = {\n    interruptionCount: 0,\n    isOutputActive: false,\n    outputAudioChunkCount: 0,\n    transportEventCount: 0,\n  };\n\n  handle.session.on(\"audio\", (event: TransportLayerAudio) => {\n    outputAudio.push(cloneAudioEvent(event));\n    state.outputAudioChunkCount += 1;\n  });\n  handle.session.on(\"transport_event\", (event: TransportEvent) => {\n    transportEvents.push(event);\n    state.transportEventCount += 1;\n  });\n  handle.session.on(\"audio_start\", () => {\n    state.isOutputActive = true;\n  });\n  handle.session.on(\"audio_stopped\", () => {\n    state.isOutputActive = false;\n  });\n  handle.session.on(\"audio_interrupted\", () => {\n    state.interruptionCount += 1;\n    state.isOutputActive = false;\n  });\n\n  return {\n    close: () => {\n      handle.session.close();\n    },\n    connect: async () => {\n      await handle.session.connect(handle.connectOptions);\n    },\n    getState: () => ({\n      ...state,\n    }),\n    interrupt: () => {\n      handle.session.interrupt();\n    },\n    mute: (muted: boolean) => {\n      handle.session.mute(muted);\n    },\n    profile: getOpenAiAgentsSdkDemoServerAudioLaneProfile(env),\n    requestResponse: (response) => {\n      const requestResponse = handle.session.transport.requestResponse;\n\n      if (typeof requestResponse !== \"function\") {\n        throw new Error(\n          \"The current realtime transport does not expose requestResponse().\"\n        );\n      }\n\n      requestResponse(response);\n    },\n    sendAudio: (audio, options) => {\n      handle.session.sendAudio(audio, options);\n    },\n    session: handle.session,\n    takeOutputAudio: () => outputAudio.splice(0, outputAudio.length),\n    takeTransportEvents: () =>\n      transportEvents.splice(0, transportEvents.length),\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-server-audio.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-sip-route.ts",
      "content": "import { z } from \"zod\";\n\nimport { buildOpenAiAgentsSdkDemoSipInitialConfig } from \"./voice-sip\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\";\nconst sipRoutePath = \"/api/demos/openai-agents-sdk-demo/realtime/sip\" as const;\nconst invalidRequestBodyError =\n  'Expected a JSON body with a required \"callId\" string.';\n\nconst sipRequestBodySchema = z.object({\n  callId: z.string().trim().min(1).max(200),\n});\n\ninterface OpenAiAgentsSdkDemoSipRequestBody {\n  callId: string;\n}\n\nexport interface OpenAiAgentsSdkDemoSipRouteState {\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  routePath: typeof sipRoutePath;\n  sdkPrimitive: \"OpenAIRealtimeSIP.buildInitialConfig()\";\n  status: \"configured\" | \"setup-required\";\n}\n\ninterface OpenAiAgentsSdkDemoSipRouteDependencies {\n  buildInitialConfig: () => Promise<unknown>;\n}\n\nfunction getRequiredOpenAiApiKey(env: DemoEnv = process.env) {\n  const apiKey = env[openAiApiKeyEnvVar];\n\n  if (!apiKey) {\n    throw new Error(\n      \"OPENAI_API_KEY is missing. SIP accept-payload generation requires a native OpenAI API key because the downstream realtime call-control lane still depends on it.\"\n    );\n  }\n\n  return apiKey;\n}\n\nasync function readOpenAiAgentsSdkDemoSipBody(request: Request) {\n  const bodyText = await request.text();\n\n  if (!bodyText.trim()) {\n    throw new Error(invalidRequestBodyError);\n  }\n\n  try {\n    return sipRequestBodySchema.parse(\n      JSON.parse(bodyText)\n    ) satisfies OpenAiAgentsSdkDemoSipRequestBody;\n  } catch {\n    throw new Error(invalidRequestBodyError);\n  }\n}\n\nexport function getOpenAiAgentsSdkDemoSipRouteState(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoSipRouteState {\n  return {\n    openAiApiKeyEnvVar,\n    routePath: sipRoutePath,\n    sdkPrimitive: \"OpenAIRealtimeSIP.buildInitialConfig()\",\n    status: env[openAiApiKeyEnvVar] ? \"configured\" : \"setup-required\",\n  };\n}\n\nexport async function handleOpenAiAgentsSdkDemoSipRequest(\n  request: Request,\n  env: DemoEnv = process.env,\n  dependencies: OpenAiAgentsSdkDemoSipRouteDependencies = {\n    buildInitialConfig: buildOpenAiAgentsSdkDemoSipInitialConfig,\n  }\n) {\n  try {\n    getRequiredOpenAiApiKey(env);\n\n    const body = await readOpenAiAgentsSdkDemoSipBody(request);\n    const acceptPayload = await dependencies.buildInitialConfig();\n\n    return Response.json({\n      acceptPayload,\n      callId: body.callId,\n    });\n  } catch (error) {\n    const message =\n      error instanceof Error ? error.message : \"Failed to build SIP payload.\";\n    const status = message === invalidRequestBodyError ? 400 : 500;\n\n    return Response.json(\n      {\n        error: message,\n      },\n      { status }\n    );\n  }\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-sip-route.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-sip.ts",
      "content": "import {\n  OpenAIRealtimeSIP,\n  RealtimeSession,\n  type RealtimeSessionConnectOptions,\n} from \"@openai/agents/realtime\";\n\nimport { createOpenAiAgentsSdkDemoVoiceAgentBundle } from \"../voice-lane\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\" as const;\nconst defaultVoiceSessionModel = \"gpt-realtime-2\" as const;\nconst defaultVoiceSessionVoice = \"marin\" as const;\nconst defaultSipWorkflowName = \"openai-agents-sdk-demo-voice-sip\" as const;\n\nexport interface OpenAiAgentsSdkDemoSipVoiceSessionProfile {\n  callControlContract: \"provider-or-openai-call-accept-route\";\n  connectPrimitive: \"RealtimeSession.connect({ apiKey, callId })\";\n  initialConfigPrimitive: \"OpenAIRealtimeSIP.buildInitialConfig()\";\n  model: typeof defaultVoiceSessionModel;\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  routePath: \"/api/demos/openai-agents-sdk-demo/realtime/sip\";\n  sessionVoice: typeof defaultVoiceSessionVoice;\n  sourceGuide: \"https://openai.github.io/openai-agents-js/guides/voice-agents/transport/\";\n  status: \"configured\" | \"setup-required\";\n  transport: \"SIP\";\n  workflowName: typeof defaultSipWorkflowName;\n}\n\nexport interface OpenAiAgentsSdkDemoSipVoiceSessionHandle {\n  connectOptions: RealtimeSessionConnectOptions;\n  profile: OpenAiAgentsSdkDemoSipVoiceSessionProfile;\n  session: RealtimeSession;\n  transport: OpenAIRealtimeSIP;\n}\n\nfunction getRequiredOpenAiApiKey(env: DemoEnv = process.env) {\n  const apiKey = env[openAiApiKeyEnvVar];\n\n  if (!apiKey) {\n    throw new Error(\n      \"OPENAI_API_KEY is missing. SIP voice transport requires a native OpenAI API key.\"\n    );\n  }\n\n  return apiKey;\n}\n\nfunction getRequiredCallId(callId: string) {\n  if (callId.trim().length === 0) {\n    throw new Error(\n      \"OpenAIRealtimeSIP requires a non-empty callId from an existing SIP-initiated Realtime call.\"\n    );\n  }\n\n  return callId;\n}\n\nexport function getOpenAiAgentsSdkDemoSipVoiceSessionProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoSipVoiceSessionProfile {\n  return {\n    callControlContract: \"provider-or-openai-call-accept-route\",\n    connectPrimitive: \"RealtimeSession.connect({ apiKey, callId })\",\n    initialConfigPrimitive: \"OpenAIRealtimeSIP.buildInitialConfig()\",\n    model: defaultVoiceSessionModel,\n    openAiApiKeyEnvVar,\n    routePath: \"/api/demos/openai-agents-sdk-demo/realtime/sip\",\n    sessionVoice: defaultVoiceSessionVoice,\n    sourceGuide:\n      \"https://openai.github.io/openai-agents-js/guides/voice-agents/transport/\",\n    status: env[openAiApiKeyEnvVar] ? \"configured\" : \"setup-required\",\n    transport: \"SIP\",\n    workflowName: defaultSipWorkflowName,\n  };\n}\n\nexport async function buildOpenAiAgentsSdkDemoSipInitialConfig() {\n  const { primaryAgent } = createOpenAiAgentsSdkDemoVoiceAgentBundle();\n\n  return await OpenAIRealtimeSIP.buildInitialConfig(primaryAgent, {\n    model: defaultVoiceSessionModel,\n    config: {\n      audio: {\n        output: {\n          voice: defaultVoiceSessionVoice,\n        },\n      },\n    },\n    workflowName: defaultSipWorkflowName,\n  });\n}\n\nexport function buildOpenAiAgentsSdkDemoSipVoiceSession({\n  callId,\n  env = process.env,\n}: {\n  callId: string;\n  env?: DemoEnv;\n}): OpenAiAgentsSdkDemoSipVoiceSessionHandle {\n  const profile = getOpenAiAgentsSdkDemoSipVoiceSessionProfile(env);\n  const apiKey = getRequiredOpenAiApiKey(env);\n  const { primaryAgent } = createOpenAiAgentsSdkDemoVoiceAgentBundle();\n  const transport = new OpenAIRealtimeSIP({\n    useInsecureApiKey: true,\n  });\n  const session = new RealtimeSession(primaryAgent, {\n    config: {\n      audio: {\n        output: {\n          voice: defaultVoiceSessionVoice,\n        },\n      },\n    },\n    model: defaultVoiceSessionModel,\n    transport,\n    workflowName: defaultSipWorkflowName,\n  });\n\n  return {\n    connectOptions: {\n      apiKey,\n      callId: getRequiredCallId(callId),\n      model: defaultVoiceSessionModel,\n    },\n    profile,\n    session,\n    transport,\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-sip.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-twilio-app.ts",
      "content": "import {\n  createOpenAiAgentsSdkDemoTwilioMediaStreamBridge,\n  type OpenAiAgentsSdkDemoTwilioMediaStreamBridge,\n} from \"./voice-twilio-bridge\";\nimport {\n  buildOpenAiAgentsSdkDemoTwilioIncomingCallTwiml,\n  getOpenAiAgentsSdkDemoTwilioCallControlProfile,\n} from \"./voice-twilio-route\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\" as const;\nconst twilioWorkflowName = \"openai-agents-sdk-demo-voice-twilio\" as const;\nconst twilioRootRoutePath = \"/\" as const;\nconst twilioIncomingCallRoutePath = \"/incoming-call\" as const;\nconst twilioMediaStreamRoutePath = \"/media-stream\" as const;\nconst twilioHealthcheckMessage =\n  \"Twilio Media Stream Server is running!\" as const;\n\ntype TwilioSocket = Parameters<\n  typeof createOpenAiAgentsSdkDemoTwilioMediaStreamBridge\n>[2];\n\nexport interface OpenAiAgentsSdkDemoTwilioMediaStreamServerProfile {\n  healthcheckMessage: typeof twilioHealthcheckMessage;\n  incomingCallRoutePath: typeof twilioIncomingCallRoutePath;\n  mediaStreamRoutePath: typeof twilioMediaStreamRoutePath;\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  publicTransportContract: \"public-https-host + websocket-server\";\n  rootRoutePath: typeof twilioRootRoutePath;\n  sdkPrimitive: \"TwilioRealtimeTransportLayer + RealtimeSession\";\n  serverPrimitive: \"Fastify + @fastify/websocket\";\n  sourceGuide: \"https://openai.github.io/openai-agents-js/extensions/twilio/\";\n  status: \"configured\" | \"setup-required\";\n  twimlTransport: \"Twilio <Connect><Stream>\";\n  websocketProtocol: \"wss\";\n  workflowName: typeof twilioWorkflowName;\n}\n\nexport interface OpenAiAgentsSdkDemoTwilioMediaStreamServer {\n  connectMediaStream: (\n    websocket: TwilioSocket\n  ) => Promise<OpenAiAgentsSdkDemoTwilioMediaStreamBridge>;\n  createHealthResponse: () => Response;\n  handleIncomingCallRequest: (request: Request) => Promise<Response>;\n  profile: OpenAiAgentsSdkDemoTwilioMediaStreamServerProfile;\n}\n\ninterface OpenAiAgentsSdkDemoTwilioMediaStreamServerDependencies {\n  createBridge?: (\n    env: DemoEnv,\n    websocket: TwilioSocket\n  ) => OpenAiAgentsSdkDemoTwilioMediaStreamBridge;\n}\n\nfunction getRequiredPublicHost(request: Request) {\n  const forwardedHost = request.headers.get(\"x-forwarded-host\")?.trim();\n  const host = forwardedHost || request.headers.get(\"host\")?.trim();\n\n  if (host) {\n    return host;\n  }\n\n  const url = new URL(request.url);\n\n  if (!url.host) {\n    throw new Error(\n      \"Twilio media-stream app could not determine a public host for the websocket endpoint.\"\n    );\n  }\n\n  return url.host;\n}\n\nfunction buildOpenAiAgentsSdkDemoTwilioMediaStreamUrl(request: Request) {\n  return `wss://${getRequiredPublicHost(request)}${twilioMediaStreamRoutePath}`;\n}\n\nexport function getOpenAiAgentsSdkDemoTwilioMediaStreamServerProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoTwilioMediaStreamServerProfile {\n  return {\n    healthcheckMessage: twilioHealthcheckMessage,\n    incomingCallRoutePath: twilioIncomingCallRoutePath,\n    mediaStreamRoutePath: twilioMediaStreamRoutePath,\n    openAiApiKeyEnvVar,\n    publicTransportContract: \"public-https-host + websocket-server\",\n    rootRoutePath: twilioRootRoutePath,\n    sdkPrimitive: \"TwilioRealtimeTransportLayer + RealtimeSession\",\n    serverPrimitive: \"Fastify + @fastify/websocket\",\n    sourceGuide: \"https://openai.github.io/openai-agents-js/extensions/twilio/\",\n    status: env[openAiApiKeyEnvVar] ? \"configured\" : \"setup-required\",\n    twimlTransport: \"Twilio <Connect><Stream>\",\n    websocketProtocol: \"wss\",\n    workflowName: twilioWorkflowName,\n  };\n}\n\nexport function createOpenAiAgentsSdkDemoTwilioMediaStreamServer(\n  env: DemoEnv = process.env,\n  dependencies: OpenAiAgentsSdkDemoTwilioMediaStreamServerDependencies = {}\n): OpenAiAgentsSdkDemoTwilioMediaStreamServer {\n  const createBridge =\n    dependencies.createBridge ??\n    ((environment: DemoEnv, websocket: TwilioSocket) =>\n      createOpenAiAgentsSdkDemoTwilioMediaStreamBridge(\n        environment,\n        {},\n        websocket\n      ));\n\n  return {\n    connectMediaStream: async (websocket) => {\n      const bridge = createBridge(env, websocket);\n\n      await bridge.connect();\n\n      return bridge;\n    },\n    createHealthResponse: () =>\n      Response.json({\n        message: twilioHealthcheckMessage,\n      }),\n    handleIncomingCallRequest: async (request) => {\n      const mediaStreamUrl =\n        buildOpenAiAgentsSdkDemoTwilioMediaStreamUrl(request);\n      const twiml = buildOpenAiAgentsSdkDemoTwilioIncomingCallTwiml({\n        mediaStreamUrl,\n      });\n\n      return new Response(twiml, {\n        headers: {\n          \"content-type\":\n            getOpenAiAgentsSdkDemoTwilioCallControlProfile(env)\n              .responseContentType,\n        },\n        status: 200,\n      });\n    },\n    profile: getOpenAiAgentsSdkDemoTwilioMediaStreamServerProfile(env),\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-twilio-app.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-twilio-bridge.ts",
      "content": "import type { RealtimeSession } from \"@openai/agents/realtime\";\n\nimport {\n  buildOpenAiAgentsSdkDemoTwilioVoiceSession,\n  type OpenAiAgentsSdkDemoVoiceExtensionHandle,\n} from \"./voice-extensions\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\" as const;\nconst twilioWorkflowName = \"openai-agents-sdk-demo-voice-twilio\" as const;\n\ntype TwilioSocket = Parameters<\n  typeof buildOpenAiAgentsSdkDemoTwilioVoiceSession\n>[0][\"twilioWebSocket\"];\n\ntype TwilioSessionLike = Pick<RealtimeSession, \"close\" | \"connect\">;\n\nexport interface OpenAiAgentsSdkDemoTwilioMediaStreamBridgeProfile {\n  closeBehavior: \"session.close() on websocket close\";\n  connectPrimitive: \"RealtimeSession.connect({ apiKey, model })\";\n  hostingContract: \"external-websocket-server\";\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  sdkPrimitive: \"TwilioRealtimeTransportLayer + RealtimeSession\";\n  sourceGuide: \"https://openai.github.io/openai-agents-js/extensions/twilio/\";\n  status: \"configured\" | \"setup-required\";\n  transport: \"WebSocket\";\n  workflowName: typeof twilioWorkflowName;\n}\n\nexport interface OpenAiAgentsSdkDemoTwilioMediaStreamBridgeState {\n  closeEventCount: number;\n  errorEventCount: number;\n  isConnected: boolean;\n}\n\nexport interface OpenAiAgentsSdkDemoTwilioMediaStreamBridge {\n  close: () => void;\n  connect: () => Promise<void>;\n  getState: () => OpenAiAgentsSdkDemoTwilioMediaStreamBridgeState;\n  profile: OpenAiAgentsSdkDemoTwilioMediaStreamBridgeProfile;\n  session: TwilioSessionLike;\n  transportName: string;\n}\n\ninterface OpenAiAgentsSdkDemoTwilioMediaStreamBridgeDependencies {\n  buildSessionHandle?: (options: {\n    env?: DemoEnv;\n    twilioWebSocket: TwilioSocket;\n  }) =>\n    | OpenAiAgentsSdkDemoVoiceExtensionHandle<any>\n    | {\n        connectOptions: {\n          apiKey: string;\n          model: string;\n        };\n        profile: {\n          status: \"configured\" | \"setup-required\";\n        };\n        session: TwilioSessionLike;\n        transport: {\n          constructor: {\n            name: string;\n          };\n        };\n      };\n}\n\nexport function getOpenAiAgentsSdkDemoTwilioMediaStreamBridgeProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoTwilioMediaStreamBridgeProfile {\n  return {\n    closeBehavior: \"session.close() on websocket close\",\n    connectPrimitive: \"RealtimeSession.connect({ apiKey, model })\",\n    hostingContract: \"external-websocket-server\",\n    openAiApiKeyEnvVar,\n    sdkPrimitive: \"TwilioRealtimeTransportLayer + RealtimeSession\",\n    sourceGuide: \"https://openai.github.io/openai-agents-js/extensions/twilio/\",\n    status: env[openAiApiKeyEnvVar] ? \"configured\" : \"setup-required\",\n    transport: \"WebSocket\",\n    workflowName: twilioWorkflowName,\n  };\n}\n\nexport function createOpenAiAgentsSdkDemoTwilioMediaStreamBridge(\n  env: DemoEnv,\n  dependencies: OpenAiAgentsSdkDemoTwilioMediaStreamBridgeDependencies,\n  twilioWebSocket: TwilioSocket\n): OpenAiAgentsSdkDemoTwilioMediaStreamBridge {\n  const buildSessionHandle =\n    dependencies.buildSessionHandle ??\n    buildOpenAiAgentsSdkDemoTwilioVoiceSession;\n  const handle = buildSessionHandle({\n    env,\n    twilioWebSocket,\n  });\n  const state: OpenAiAgentsSdkDemoTwilioMediaStreamBridgeState = {\n    closeEventCount: 0,\n    errorEventCount: 0,\n    isConnected: false,\n  };\n\n  twilioWebSocket.addEventListener(\"close\", () => {\n    state.closeEventCount += 1;\n    state.isConnected = false;\n    handle.session.close();\n  });\n  twilioWebSocket.addEventListener(\"error\", () => {\n    state.errorEventCount += 1;\n  });\n\n  return {\n    close: () => {\n      state.isConnected = false;\n      handle.session.close();\n    },\n    connect: async () => {\n      await handle.session.connect(handle.connectOptions);\n      state.isConnected = true;\n    },\n    getState: () => ({\n      ...state,\n    }),\n    profile: getOpenAiAgentsSdkDemoTwilioMediaStreamBridgeProfile(env),\n    session: handle.session,\n    transportName: handle.transport.constructor.name,\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-twilio-bridge.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-twilio-route.ts",
      "content": "type DemoEnv = Record<string, string | undefined>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\" as const;\nconst twilioMediaStreamUrlEnvVar =\n  \"OPENAI_AGENTS_TWILIO_MEDIA_STREAM_URL\" as const;\nconst twilioIncomingCallRoutePath =\n  \"/api/demos/openai-agents-sdk-demo/realtime/twilio/incoming-call\" as const;\n\nexport interface OpenAiAgentsSdkDemoTwilioCallControlProfile {\n  mediaStreamUrlEnvVar: typeof twilioMediaStreamUrlEnvVar;\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  requiredMediaStreamProtocol: \"wss\";\n  responseContentType: \"text/xml; charset=utf-8\";\n  routePath: typeof twilioIncomingCallRoutePath;\n  sdkPrimitive: \"TwilioRealtimeTransportLayer\";\n  sourceGuide: \"https://openai.github.io/openai-agents-js/extensions/twilio/\";\n  status: \"configured\" | \"setup-required\";\n  transportContract: \"Twilio <Connect><Stream>\";\n}\n\nfunction getRequiredTwilioMediaStreamUrl(env: DemoEnv = process.env) {\n  const mediaStreamUrl = env[twilioMediaStreamUrlEnvVar]?.trim();\n\n  if (!mediaStreamUrl) {\n    throw new Error(\n      \"OPENAI_AGENTS_TWILIO_MEDIA_STREAM_URL is missing. Twilio incoming-call control needs a public wss:// media-stream server that can host TwilioRealtimeTransportLayer.\"\n    );\n  }\n\n  const parsedUrl = new URL(mediaStreamUrl);\n\n  if (parsedUrl.protocol !== \"wss:\") {\n    throw new Error(\n      \"OPENAI_AGENTS_TWILIO_MEDIA_STREAM_URL must use wss:// so Twilio can stream call audio securely.\"\n    );\n  }\n\n  return mediaStreamUrl;\n}\n\nfunction escapeXml(value: string) {\n  return value\n    .replaceAll(\"&\", \"&amp;\")\n    .replaceAll('\"', \"&quot;\")\n    .replaceAll(\"<\", \"&lt;\")\n    .replaceAll(\">\", \"&gt;\");\n}\n\nexport function getOpenAiAgentsSdkDemoTwilioCallControlProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoTwilioCallControlProfile {\n  return {\n    mediaStreamUrlEnvVar: twilioMediaStreamUrlEnvVar,\n    openAiApiKeyEnvVar,\n    requiredMediaStreamProtocol: \"wss\",\n    responseContentType: \"text/xml; charset=utf-8\",\n    routePath: twilioIncomingCallRoutePath,\n    sdkPrimitive: \"TwilioRealtimeTransportLayer\",\n    sourceGuide: \"https://openai.github.io/openai-agents-js/extensions/twilio/\",\n    status:\n      env[openAiApiKeyEnvVar] && env[twilioMediaStreamUrlEnvVar]\n        ? \"configured\"\n        : \"setup-required\",\n    transportContract: \"Twilio <Connect><Stream>\",\n  };\n}\n\nexport function buildOpenAiAgentsSdkDemoTwilioIncomingCallTwiml({\n  mediaStreamUrl,\n}: {\n  mediaStreamUrl: string;\n}) {\n  return `<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response><Connect><Stream url=\"${escapeXml(\n    mediaStreamUrl\n  )}\" /></Connect></Response>`;\n}\n\nexport async function handleOpenAiAgentsSdkDemoTwilioIncomingCallRequest(\n  _request: Request,\n  env: DemoEnv = process.env\n) {\n  try {\n    const mediaStreamUrl = getRequiredTwilioMediaStreamUrl(env);\n    const twiml = buildOpenAiAgentsSdkDemoTwilioIncomingCallTwiml({\n      mediaStreamUrl,\n    });\n\n    return new Response(twiml, {\n      headers: {\n        \"content-type\": \"text/xml; charset=utf-8\",\n      },\n      status: 200,\n    });\n  } catch (error) {\n    const message =\n      error instanceof Error ? error.message : \"Failed to build Twilio TwiML.\";\n\n    return new Response(message, {\n      headers: {\n        \"content-type\": \"text/plain; charset=utf-8\",\n      },\n      status: 500,\n    });\n  }\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-twilio-route.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice-websocket.ts",
      "content": "import {\n  OpenAIRealtimeWebSocket,\n  RealtimeSession,\n  type RealtimeSessionConnectOptions,\n} from \"@openai/agents/realtime\";\n\nimport { createOpenAiAgentsSdkDemoVoiceAgentBundle } from \"../voice-lane\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nconst openAiApiKeyEnvVar = \"OPENAI_API_KEY\" as const;\nconst defaultVoiceSessionModel = \"gpt-realtime-2\" as const;\nconst defaultVoiceSessionVoice = \"marin\" as const;\nconst defaultVoiceWorkflowName =\n  \"openai-agents-sdk-demo-voice-websocket\" as const;\n\nexport interface OpenAiAgentsSdkDemoServerVoiceSessionProfile {\n  model: typeof defaultVoiceSessionModel;\n  openAiApiKeyEnvVar: typeof openAiApiKeyEnvVar;\n  rawEventAccess: \"session.transport.sendEvent()\";\n  sdkPrimitives: [\n    \"OpenAIRealtimeWebSocket\",\n    \"RealtimeSession\",\n    \"RealtimeSession.connect({ apiKey })\",\n  ];\n  sessionVoice: typeof defaultVoiceSessionVoice;\n  status: \"configured\" | \"setup-required\";\n  transport: \"WebSocket\";\n  useInsecureApiKey: true;\n  workflowName: typeof defaultVoiceWorkflowName;\n}\n\nexport interface OpenAiAgentsSdkDemoServerVoiceSessionHandle {\n  connectOptions: RealtimeSessionConnectOptions;\n  profile: OpenAiAgentsSdkDemoServerVoiceSessionProfile;\n  session: RealtimeSession;\n}\n\nfunction getRequiredOpenAiApiKey(env: DemoEnv = process.env) {\n  const apiKey = env[openAiApiKeyEnvVar];\n\n  if (!apiKey) {\n    throw new Error(\n      \"OPENAI_API_KEY is missing. Server-side Realtime WebSocket transport requires a native OpenAI API key.\"\n    );\n  }\n\n  return apiKey;\n}\n\nexport function getOpenAiAgentsSdkDemoServerVoiceSessionProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoServerVoiceSessionProfile {\n  return {\n    model: defaultVoiceSessionModel,\n    openAiApiKeyEnvVar,\n    rawEventAccess: \"session.transport.sendEvent()\",\n    sdkPrimitives: [\n      \"OpenAIRealtimeWebSocket\",\n      \"RealtimeSession\",\n      \"RealtimeSession.connect({ apiKey })\",\n    ],\n    sessionVoice: defaultVoiceSessionVoice,\n    status: env[openAiApiKeyEnvVar] ? \"configured\" : \"setup-required\",\n    transport: \"WebSocket\",\n    useInsecureApiKey: true,\n    workflowName: defaultVoiceWorkflowName,\n  };\n}\n\nexport function buildOpenAiAgentsSdkDemoServerVoiceSession(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoServerVoiceSessionHandle {\n  const profile = getOpenAiAgentsSdkDemoServerVoiceSessionProfile(env);\n  const apiKey = getRequiredOpenAiApiKey(env);\n  const { primaryAgent } = createOpenAiAgentsSdkDemoVoiceAgentBundle();\n  const transport = new OpenAIRealtimeWebSocket({\n    useInsecureApiKey: true,\n  });\n  const session = new RealtimeSession(primaryAgent, {\n    config: {\n      audio: {\n        output: {\n          voice: defaultVoiceSessionVoice,\n        },\n      },\n    },\n    model: defaultVoiceSessionModel,\n    transport,\n    workflowName: defaultVoiceWorkflowName,\n  });\n\n  return {\n    connectOptions: {\n      apiKey,\n      model: defaultVoiceSessionModel,\n    },\n    profile,\n    session,\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice-websocket.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/server/voice.ts",
      "content": "import { getOpenAiAgentsSdkDemoVoiceLaneProfile } from \"../voice-lane\";\nimport {\n  getOpenAiAgentsSdkDemoCloudflareWorkerAppProfile,\n  type OpenAiAgentsSdkDemoCloudflareWorkerAppProfile,\n} from \"./voice-cloudflare-app\";\nimport {\n  getOpenAiAgentsSdkDemoCloudflareWorkerProfile,\n  type OpenAiAgentsSdkDemoCloudflareWorkerProfile,\n} from \"./voice-cloudflare-worker\";\nimport {\n  getOpenAiAgentsSdkDemoCloudflareWorkerModuleProfile,\n  type OpenAiAgentsSdkDemoCloudflareWorkerModuleProfile,\n} from \"./voice-cloudflare-worker-module\";\nimport { getOpenAiAgentsSdkDemoVoiceExtensionProfiles } from \"./voice-extensions\";\nimport { getOpenAiAgentsSdkDemoVoiceClientSecretRouteState } from \"./voice-realtime\";\nimport {\n  getOpenAiAgentsSdkDemoServerAudioLaneProfile,\n  type OpenAiAgentsSdkDemoServerAudioLaneProfile,\n} from \"./voice-server-audio\";\nimport {\n  getOpenAiAgentsSdkDemoSipVoiceSessionProfile,\n  type OpenAiAgentsSdkDemoSipVoiceSessionProfile,\n} from \"./voice-sip\";\nimport {\n  getOpenAiAgentsSdkDemoTwilioMediaStreamServerProfile,\n  type OpenAiAgentsSdkDemoTwilioMediaStreamServerProfile,\n} from \"./voice-twilio-app\";\nimport {\n  getOpenAiAgentsSdkDemoTwilioMediaStreamBridgeProfile,\n  type OpenAiAgentsSdkDemoTwilioMediaStreamBridgeProfile,\n} from \"./voice-twilio-bridge\";\nimport {\n  getOpenAiAgentsSdkDemoTwilioCallControlProfile,\n  type OpenAiAgentsSdkDemoTwilioCallControlProfile,\n} from \"./voice-twilio-route\";\nimport {\n  getOpenAiAgentsSdkDemoServerVoiceSessionProfile,\n  type OpenAiAgentsSdkDemoServerVoiceSessionProfile,\n} from \"./voice-websocket\";\n\ntype DemoEnv = Record<string, string | undefined>;\n\nexport interface OpenAiAgentsSdkDemoVoiceProfile {\n  agentPrimitive: \"RealtimeAgent\";\n  browserTransport: {\n    credentialContract: \"ephemeral-client-secret\";\n    routePath: \"/api/demos/openai-agents-sdk-demo/realtime/client-secrets\";\n    sessionModel: \"gpt-realtime-2\";\n    sessionVoice: \"marin\";\n    sdkPrimitive: \"RealtimeSession.connect({ apiKey })\";\n    status: \"configured\" | \"setup-required\";\n    transport: \"WebRTC\";\n  };\n  cloudflareWorkerApp: OpenAiAgentsSdkDemoCloudflareWorkerAppProfile;\n  cloudflareWorkerModule: OpenAiAgentsSdkDemoCloudflareWorkerModuleProfile;\n  cloudflareWorkerRuntime: OpenAiAgentsSdkDemoCloudflareWorkerProfile;\n  lane: ReturnType<typeof getOpenAiAgentsSdkDemoVoiceLaneProfile>;\n  notes: string;\n  providerExtensions: ReturnType<\n    typeof getOpenAiAgentsSdkDemoVoiceExtensionProfiles\n  >;\n  serverAudioLane: OpenAiAgentsSdkDemoServerAudioLaneProfile;\n  serverTransport: OpenAiAgentsSdkDemoServerVoiceSessionProfile & {\n    credentialContract: \"server-api-key\";\n    sdkPrimitive: \"new OpenAIRealtimeWebSocket({ useInsecureApiKey: true }) + RealtimeSession\";\n  };\n  sessionPrimitive: \"RealtimeSession\";\n  sipTransport: OpenAiAgentsSdkDemoSipVoiceSessionProfile;\n  sourceGuide: string;\n  supportedInsideCurrentChatRoute: false;\n  supportedInsideCurrentWorkspace: true;\n  twilioCallControl: OpenAiAgentsSdkDemoTwilioCallControlProfile;\n  twilioMediaStreamBridge: OpenAiAgentsSdkDemoTwilioMediaStreamBridgeProfile;\n  twilioMediaStreamServer: OpenAiAgentsSdkDemoTwilioMediaStreamServerProfile;\n}\n\nexport function getOpenAiAgentsSdkDemoVoiceProfile(\n  env: DemoEnv = process.env\n): OpenAiAgentsSdkDemoVoiceProfile {\n  const routeState = getOpenAiAgentsSdkDemoVoiceClientSecretRouteState(env);\n  const serverTransportProfile =\n    getOpenAiAgentsSdkDemoServerVoiceSessionProfile(env);\n  const sipTransportProfile = getOpenAiAgentsSdkDemoSipVoiceSessionProfile(env);\n\n  return {\n    agentPrimitive: \"RealtimeAgent\",\n    browserTransport: {\n      credentialContract: \"ephemeral-client-secret\",\n      routePath: routeState.routePath,\n      sessionModel: routeState.sessionModel,\n      sessionVoice: routeState.sessionVoice,\n      sdkPrimitive: \"RealtimeSession.connect({ apiKey })\",\n      status: routeState.status,\n      transport: \"WebRTC\",\n    },\n    lane: getOpenAiAgentsSdkDemoVoiceLaneProfile(),\n    notes:\n      \"Client-secret minting now feeds a dedicated browser voice panel on this page. The realtime lane carries official voice tools, approval events, and handoff state. Separate server-side factories now cover OpenAIRealtimeWebSocket, a raw server audio loop on top of RealtimeSession.sendAudio(), OpenAIRealtimeSIP, TwilioRealtimeTransportLayer, CloudflareRealtimeTransportLayer, a Cloudflare worker runtime wrapper, a Cloudflare worker fetch app, a deployable Cloudflare worker module, a Twilio incoming-call control route, and a deployed-shape Twilio media-stream app factory for custom audio pipelines and provider-specific bridges. The text chat route still stays separate; voice runs through RealtimeSession over WebRTC instead of the AI SDK UI chat stream.\",\n    cloudflareWorkerApp: getOpenAiAgentsSdkDemoCloudflareWorkerAppProfile(env),\n    cloudflareWorkerModule:\n      getOpenAiAgentsSdkDemoCloudflareWorkerModuleProfile(env),\n    cloudflareWorkerRuntime: getOpenAiAgentsSdkDemoCloudflareWorkerProfile(env),\n    providerExtensions: getOpenAiAgentsSdkDemoVoiceExtensionProfiles(env),\n    serverAudioLane: getOpenAiAgentsSdkDemoServerAudioLaneProfile(env),\n    sipTransport: sipTransportProfile,\n    serverTransport: {\n      ...serverTransportProfile,\n      credentialContract: \"server-api-key\",\n      sdkPrimitive:\n        \"new OpenAIRealtimeWebSocket({ useInsecureApiKey: true }) + RealtimeSession\",\n    },\n    sessionPrimitive: \"RealtimeSession\",\n    sourceGuide:\n      \"https://openai.github.io/openai-agents-js/guides/voice-agents/\",\n    supportedInsideCurrentWorkspace: true,\n    supportedInsideCurrentChatRoute: false,\n    twilioCallControl: getOpenAiAgentsSdkDemoTwilioCallControlProfile(env),\n    twilioMediaStreamBridge:\n      getOpenAiAgentsSdkDemoTwilioMediaStreamBridgeProfile(env),\n    twilioMediaStreamServer:\n      getOpenAiAgentsSdkDemoTwilioMediaStreamServerProfile(env),\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/server/voice.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/lib/openai-agents-sdk-demo/voice-lane.ts",
      "content": "import { RealtimeAgent, tool } from \"@openai/agents/realtime\";\nimport { z } from \"zod\";\n\nexport interface OpenAiAgentsSdkDemoVoiceLaneProfile {\n  approvalToolNames: string[];\n  emittedSessionEvents: string[];\n  handoffAgentNames: string[];\n  recommendedSmokePrompts: string[];\n  toolNames: string[];\n  transportEscapeHatch: \"session.transport.sendEvent()\";\n}\n\nconst buildResearchBriefInput = z.object({\n  company: z.string().min(1),\n  focus: z.string().min(1).optional(),\n});\n\nconst publishResearchSummaryInput = z.object({\n  audience: z.string().min(1),\n  company: z.string().min(1),\n  summary: z.string().min(1),\n});\n\nconst openAiAgentsSdkDemoVoiceLaneProfile: OpenAiAgentsSdkDemoVoiceLaneProfile =\n  {\n    approvalToolNames: [\"publish_research_summary\"],\n    emittedSessionEvents: [\n      \"history_updated\",\n      \"transport_event\",\n      \"agent_start\",\n      \"agent_end\",\n      \"agent_handoff\",\n      \"agent_tool_start\",\n      \"agent_tool_end\",\n      \"tool_approval_requested\",\n      \"guardrail_tripped\",\n      \"mcp_tools_changed\",\n      \"audio_start\",\n      \"audio_stopped\",\n      \"audio_interrupted\",\n      \"error\",\n    ],\n    handoffAgentNames: [\"Voice Risk Reviewer\"],\n    recommendedSmokePrompts: [\n      \"为特斯拉生成一个简短的投研 brief。\",\n      \"请挑战一下特斯拉多头观点，把任务交给风险 reviewer。\",\n      \"给我一段准备对外发布的特斯拉研究摘要，并准备发布。\",\n    ],\n    toolNames: [\"build_research_brief\", \"publish_research_summary\"],\n    transportEscapeHatch: \"session.transport.sendEvent()\",\n  };\n\nfunction createBuildResearchBriefTool() {\n  return tool({\n    description:\n      \"Create a compact public-company research brief before deeper analysis.\",\n    execute: ({ company, focus }) => {\n      const researchFocus = focus?.trim() || \"overall business quality\";\n\n      return [\n        `Company: ${company}`,\n        `Focus: ${researchFocus}`,\n        \"Research brief:\",\n        \"- Confirm the latest facts before citing them.\",\n        \"- Separate business quality, financial performance, capital allocation, and key risks.\",\n        \"- Call out what still needs deeper validation before a publishable conclusion.\",\n      ].join(\"\\n\");\n    },\n    name: \"build_research_brief\",\n    parameters: buildResearchBriefInput,\n  });\n}\n\nfunction createPublishResearchSummaryTool() {\n  return tool({\n    description:\n      \"Publish a finished research takeaway to an external audience after a human approval checkpoint.\",\n    execute: ({ audience, company, summary }) =>\n      [\n        `Audience: ${audience}`,\n        `Company: ${company}`,\n        \"Status: approved and ready to share.\",\n        `Summary: ${summary}`,\n      ].join(\"\\n\"),\n    name: \"publish_research_summary\",\n    needsApproval: true,\n    parameters: publishResearchSummaryInput,\n  });\n}\n\nexport function getOpenAiAgentsSdkDemoVoiceLaneProfile() {\n  return openAiAgentsSdkDemoVoiceLaneProfile;\n}\n\nexport function createOpenAiAgentsSdkDemoVoiceAgentBundle() {\n  const voiceRiskReviewer = new RealtimeAgent({\n    instructions:\n      \"You are the bearish risk reviewer for this realtime research lane. Stress-test the thesis, surface weak evidence, and keep the answer concise.\",\n    name: \"Voice Risk Reviewer\",\n  });\n\n  const primaryAgent = new RealtimeAgent({\n    handoffs: [voiceRiskReviewer],\n    instructions:\n      \"You are the realtime voice lane for the OpenAI Agents SDK demo. Keep spoken responses concise, useful, and natural. Use build_research_brief for a compact public-company kickoff. When the user asks to publish or share a finished summary externally, call publish_research_summary so the human approval step is exercised. When the user explicitly asks for a skeptical review or risk challenge, hand off to Voice Risk Reviewer.\",\n    name: \"Voice Analyst\",\n    tools: [createBuildResearchBriefTool(), createPublishResearchSummaryTool()],\n  });\n\n  return {\n    handoffAgents: [voiceRiskReviewer],\n    primaryAgent,\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/openai-agents-sdk-demo/voice-lane.ts"
    },
    {
      "path": "registry/openai-agents-sdk-demo/docs/frontend/openai-agents-sdk-demo.md",
      "content": "---\ntitle: OpenAI Agents SDK Demo\ndescription: Complete copy-boundary, capability coverage, and bridge conventions for the ultra OpenAI Agents SDK demo running through AI Gateway.\nupdateAt: 2026-06-03\n---\n\n# OpenAI Agents SDK Demo\n\n## Scope\n\n- Covers the `openai-agents-sdk-demo` roadmap entry under `apps/web/features`.\n- Covers the ultra version target: a complete OpenAI Agents SDK TypeScript **Agent Demo** that can be copied as one coherent feature slice.\n- Covers the product expectation that all OpenAI Agents SDK capabilities relevant to this demo live inside this one demo, without splitting capability families into separate demos.\n- Covers the backend orchestration boundary, the AI SDK UI bridge, AI Gateway transport, run state, approvals, sessions, tracing, and the frontend surfaces needed to show them.\n- Does not cover `trace-eval-agent`; that feature is being worked on separately and should not be edited for this demo unless the user explicitly widens scope.\n\n## Domain Language\n\n- **Ultra OpenAI Agents SDK demo**: A complete **Agent Demo** whose demo-related code is expected to be copied away together and whose capability surface covers every stable OpenAI Agents SDK TypeScript capability family that can be shown in this app: agents, models, tools, guardrails, running, streaming, orchestration, handoffs, results, human-in-the-loop, sessions, context, MCP, tracing, sandbox agents, voice-adjacent references, and extensions.\n- **Whole-demo copy boundary**: The complete `openai-agents-sdk-demo` feature slice, including route entries, UI, server modules, tests, docs, setup contract, and any demo-local runtime adapters needed to move the demo into a compatible project.\n- **Official-source parity**: The implementation standard that the demo should preserve the AI SDK and OpenAI Agents SDK documentation behavior, helper names, run surfaces, and runtime semantics before adding project-specific polish.\n- **Non-pruned capability coverage**: The rule that a capability should not be omitted just because it adds UI or backend complexity. If the SDK capability is stable enough to demonstrate and fits this app, implement it inside this demo.\n- **Document-by-document integration path**: The implementation order for the ultra demo: walk the official OpenAI Agents SDK TypeScript docs one guide at a time, integrate the documented capability, then expose only enough UI to prove that capability works.\n- **Thin demo adapter**: The smallest route, state, or UI adapter needed to fit an official SDK capability into this app without replacing the official SDK pattern with custom orchestration.\n- **SDK-first backend**: Backend code shaped around official SDK primitives such as `Agent`, `Runner`, `run()`, `RunContext`, `RunState`, `RunResult`, `StreamedRunResult`, `tool()`, `handoff()`, sessions, and tracing.\n- **Run state surface**: The persisted or returned data needed to continue a run safely: `history`, `newItems`, `finalOutput`, `interruptions`, `state`, `lastAgent` / `activeAgent`, `lastResponseId`, `runContext`, usage, and trace metadata.\n- **AI SDK UI bridge**: The official adapter from `@openai/agents-extensions/ai-sdk-ui` that converts an OpenAI Agents SDK stream into AI SDK UI-compatible chunks.\n- **Gateway-backed OpenAI client**: The official `openai` client configured against AI Gateway's OpenAI-compatible Responses endpoint and injected into the OpenAI Agents SDK with `setDefaultOpenAIClient(...)`.\n- **Provider transport boundary**: The narrow layer where `AI_GATEWAY_API_KEY`, base URL, model id, and reasoning settings are configured. Agent behavior must still be modeled through OpenAI Agents SDK primitives.\n- **Provider capability block**: An explicit setup or runtime state used when AI Gateway or the selected model/provider cannot support an official OpenAI Agents SDK capability.\n- **Capability lane**: One coherent mode or section inside the ultra demo, such as \"tools and approvals\" or \"handoffs and typed results\", with its own backend module, focused tests, and visible UI states while remaining inside the same **Whole-demo copy boundary**.\n- **General OpenAI Agent**: The product shape for the ultra demo: a ChatGPT-like conversational agent built from OpenAI Agents SDK primitives, with official SDK capabilities exposed through one coherent chat workspace.\n- **Official Guide Coverage Panel**: The right-side inspector surface that maps the current run and implemented capabilities back to the OpenAI Agents SDK guide families.\n- **Runtime Inspector Panel**: The right-side operational surface that shows run state, active agent, tool calls, handoffs, approvals, sessions, traces, and which official guide capabilities were exercised.\n- **Runtime Inspector Module**: The feature-local UI module that derives the current **Runtime Inspector Panel** state from AI SDK UI messages, guide coverage, and setup profiles before rendering.\n- **Developer-verifiable coverage evidence**: The per-capability proof shown in the inspector: source guide, SDK primitive, run item or stream event, implementation status, and whether the current run exercised it.\n- **Investment Research Task**: The default suggested task for the general agent: an investment-company analyst running deep research on a public company.\n- **Company Research Target**: The public company being analyzed in the workspace. Tesla should be the default target because the user can judge whether the analysis quality is credible.\n- **Analyst Review Packet**: The final artifact produced by the demo: a structured, sourced research packet that a human analyst can review, challenge, and refine.\n\n## Business Shape\n\n- This demo should model a complete OpenAI Agents SDK capability workspace, not a thin bridge or minimal smoke test.\n- The primary product goal is official capability coverage. The implementation should follow the OpenAI Agents SDK docs guide by guide, preserving each guide's source-core behavior before adding demo polish.\n- The expected evaluator is the repository's **Technical Evaluator**: a developer or technical reviewer who wants to judge whether the whole feature slice can be copied into another compatible project.\n- The product shape is a **General OpenAI Agent**: effectively a ChatGPT-like conversational agent rebuilt on top of OpenAI Agents SDK capabilities.\n- The **Investment Research Task** is the default suggested task for exercising the agent. The default **Company Research Target** should be Tesla, because the task is familiar enough for the user to judge output quality and broad enough to exercise the SDK capability surface.\n- The user-facing form remains a conversational **Demo Workspace**, consistent with `skills-agent`: the evaluator chats with the agent, while the workspace exposes runtime state, tools, handoffs, approvals, traces, sessions, and final artifacts around the conversation.\n- The main left surface should stay ChatGPT-like: prompt input, assistant messages, streaming output, and retry/stop controls. Do not turn the primary experience into a docs checklist UI.\n- The right-side panel should combine the **Official Guide Coverage Panel** and **Runtime Inspector Panel**. It should show which OpenAI Agents SDK guide families are implemented and which ones were actually exercised in the current run.\n- Coverage should optimize for **Technical Evaluator** verification. Each guide-family row should expose **Developer-verifiable coverage evidence** such as `Handoffs -> handoff() -> handoff call/output item -> used this run`.\n- The default workflow can feel like a Tesla deep research task: collect source material, inspect financials and market context, split work across specialist agents, request human approval for sensitive or high-cost actions, challenge conclusions with guardrails, and produce an **Analyst Review Packet**. Do not over-optimize around this artifact; use it mainly to make official SDK capabilities easy to judge.\n- The demo should produce research analysis and cited reasoning, not investment advice. It should avoid buy/sell/hold recommendations unless the user explicitly asks for a simulated analyst opinion, and even then it must label the result as a demo artifact for human review.\n- Financial facts are time-sensitive. Do not hardcode current Tesla metrics, prices, filings, or earnings claims as durable product truth; fetch, upload, or cite source material at runtime, or ask the user for the analysis period.\n- All demo-related code is expected to travel together. In a registry consumer, keep the **Copy Boundary** centered on `lib/openai-agents-sdk-demo`, `components/openai-agents-sdk-demo`, and the thin `app/demos/openai-agents-sdk-demo` / `app/api/demos/openai-agents-sdk-demo` route entries that invoke them.\n- Follow the AI SDK and OpenAI Agents SDK docs closely. Preserve the **Source Core** for official runtime behavior before adding repo-specific UI organization.\n- Use `skills-agent` as the local quality reference: preserve the official source core, expose the real runtime capability surface, show generated artifacts or runtime events visibly, keep setup errors explicit, and keep the feature slice portable.\n- Do not intentionally reduce the SDK capability surface to make implementation easier. If a stable SDK feature needs UI, state, or route work, add that work inside this demo instead of cutting the feature.\n- Avoid custom orchestration, custom tool frameworks, or custom agent abstractions when the official OpenAI Agents SDK docs already provide the pattern. Add a **Thin demo adapter** only to connect the official SDK behavior to the existing route, chat UI, runtime panel, and tests.\n- Do not silently degrade provider-specific OpenAI Agents SDK behavior. If AI Gateway, the selected model, or missing native OpenAI credentials cannot support an official capability, show a **Provider capability block**.\n- Do not use custom tools, custom fetchers, or custom orchestration to pretend an unsupported official capability is covered. The coverage panel should say `blocked` or `requires OPENAI_API_KEY` instead of counting a custom substitute as official coverage.\n- Use internal modes or lanes to keep the UX navigable, but do not turn major OpenAI Agents SDK capability families into separate homepage demos.\n\n## Canonical Source Set\n\n- Treat the OpenAI Agents SDK TypeScript docs as the canonical backend source set for the ultra demo:\n  - `https://openai.github.io/openai-agents-js/`\n  - `https://openai.github.io/openai-agents-js/guides/quickstart/`\n  - `https://openai.github.io/openai-agents-js/guides/agents/`\n  - `https://openai.github.io/openai-agents-js/guides/models/`\n  - `https://openai.github.io/openai-agents-js/guides/tools/`\n  - `https://openai.github.io/openai-agents-js/guides/guardrails/`\n  - `https://openai.github.io/openai-agents-js/guides/running-agents/`\n  - `https://openai.github.io/openai-agents-js/guides/streaming/`\n  - `https://openai.github.io/openai-agents-js/guides/multi-agent/`\n  - `https://openai.github.io/openai-agents-js/guides/handoffs/`\n  - `https://openai.github.io/openai-agents-js/guides/results/`\n  - `https://openai.github.io/openai-agents-js/guides/human-in-the-loop/`\n  - `https://openai.github.io/openai-agents-js/guides/sessions/`\n  - `https://openai.github.io/openai-agents-js/guides/context/`\n  - `https://openai.github.io/openai-agents-js/guides/mcp/`\n  - `https://openai.github.io/openai-agents-js/guides/tracing/`\n  - `https://openai.github.io/openai-agents-js/guides/sandbox-agents`\n  - `https://openai.github.io/openai-agents-js/guides/voice-agents/`\n  - `https://openai.github.io/openai-agents-js/guides/voice-agents/quickstart/`\n  - `https://openai.github.io/openai-agents-js/guides/voice-agents/transport/`\n  - `https://openai.github.io/openai-agents-js/extensions/ai-sdk/`\n- Treat `https://vercel.com/docs/ai-gateway/sdks-and-apis/openai-compat/responses` as the canonical AI Gateway source for OpenAI-compatible Responses API usage, model ids in `provider/model` format, streaming, reasoning settings, and error codes.\n- Treat `openai/openai-agents-js` examples as implementation references after the public docs are checked:\n  - `examples/ai-sdk-ui` for the AI SDK UI bridge.\n  - `examples/nextjs` for app wiring and approval UX references.\n  - `examples/agent-patterns` for orchestration patterns.\n- Refresh all of the above before implementation batches. Model names, default model settings, bridge helpers, and beta sandbox APIs are drift-prone.\n- Implement capabilities in the same order as the docs when practical. If a later guide depends on an earlier one, integrate the earlier guide first instead of inventing a shortcut.\n\n## Current Implementation Status\n\n- The registry feature slice installs backend code under `lib/openai-agents-sdk-demo` and UI code under `components/openai-agents-sdk-demo`.\n- The route contract is split as:\n  - `app/demos/openai-agents-sdk-demo/page.tsx`\n  - `app/api/demos/openai-agents-sdk-demo/route.ts`\n- The backend currently uses a feature-local `Agent`, `run(..., { stream: true })`, and `createAiSdkUiMessageStream(...)` wrapped in AI SDK UI response helpers.\n- The provider path currently uses `setOpenAIAPI(\"responses\")` plus `setDefaultOpenAIClient(new OpenAI(...))` to route through AI Gateway's OpenAI-compatible Responses endpoint.\n- The current default demo model is `openai/gpt-5-mini`, inherited from the repo's AI Gateway config style. Keep the default configurable through `AI_GATEWAY_CHAT_MODEL` and `OPENAI_AGENTS_MODEL`.\n- The `Agents` guide first slice is now separated into `lib/openai-agents-sdk-demo/server/agents.ts`, which constructs the official `Agent` used by the chat route.\n- The `Models` guide first slice is now separated into `lib/openai-agents-sdk-demo/server/models.ts`, which centralizes model id, Responses API selection, HTTP transport, AI Gateway base URL, reasoning effort, and text verbosity.\n- The current agent now passes both `modelSettings.reasoning.effort` and `modelSettings.text.verbosity` into the official `Agent` constructor. This matters because once custom `modelSettings` are provided, the SDK no longer applies the GPT-5 default `text.verbosity = low` automatically.\n- The `Tools` guide first slice is now separated into `lib/openai-agents-sdk-demo/server/tools.ts`, which configures thin official `tool()` examples, one approval-required `tool({ needsApproval: true })`, hosted OpenAI tools, and one `agent.asTool()` specialist for the current demo.\n- The `Guardrails` guide first slice is now separated into `lib/openai-agents-sdk-demo/server/guardrails.ts`, which configures one input guardrail and one output guardrail on the main agent.\n- The `Running Agents` guide first slice is now separated into `lib/openai-agents-sdk-demo/server/running.ts`, which owns continuation strategy, provider-aware `previousResponseId` selection, `MemorySession` fallback, explicit `maxTurns`, and request-signal usage.\n- The `Streaming` guide first slice is now separated into `lib/openai-agents-sdk-demo/server/streaming.ts`, which wraps the official `RunStreamEvent` source with a thin observer and records stream metadata without replacing the official AI SDK UI bridge.\n- The `Agent Orchestration` guide first slice currently reuses the existing `research_memo_agent` specialist in `server/tools.ts` as the official `agent.asTool()` path and marks that guide from real run metadata when the specialist executes.\n- The `Handoffs` guide first slice now lives in `server/handoffs.ts`. The main demo agent exposes one direct specialist agent in `handoffs: [agent]` and one explicit `handoff()` with `inputType`, `onHandoff`, and a local copy of the official `removeAllTools` / prompt-prefix helpers because the published `@openai/agents-core/extensions` entry is not resolvable through the current app package graph.\n- The `Results` guide first slice now lives in `server/results.ts`. The chat route waits for `await agentStream.completed` and then maps the settled `RunResult` surface into message metadata: `activeAgent`, `lastAgent`, `finalOutput`, `history`, `newItems`, `output`, `interruptions`, `state.usage`, and `rawResponses`.\n- The `Human-in-the-loop` guide first slice now lives in `server/approvals.ts`. The chat route serializes paused `RunState`, surfaces real `RunToolApprovalItem` metadata, and resumes the same run through `RunState.fromString(...)` plus `state.approve(...)` / `state.reject(...)`.\n- The `Sessions` guide first slice now lives in `server/sessions.ts`. The chat route passes an official `MemorySession` into every `run(...)`, carries the session id through assistant metadata, reuses the same session for AI Gateway follow-ups, true `previousResponseId` follow-ups, and approval resume, and rehydrates a process-local session from visible transcript history after a cold store miss.\n- The `Tracing` guide first slice now lives in `server/tracing.ts`. The chat route passes explicit per-run tracing config through the official `run(...)` path: `workflowName`, `traceId`, `groupId`, `traceMetadata`, `tracingDisabled`, `traceIncludeSensitiveData`, and optional `tracing.apiKey`.\n- The `Sandbox Agents` guide first slice now lives in `server/sandbox.ts`. It defines one official `SandboxAgent` with `Manifest`, `localDir(...)`, and `Capabilities.default()`, passes a `UnixLocalSandboxClient` through `run(..., { sandbox })`, persists `sessionState` by demo `sessionId`, and exposes sandbox profile plus latest sandbox summary in the runtime inspector.\n- The `Extensions / AI SDK Integration` first slice now lives in `server/extensions.ts`. The route keeps the official `@openai/agents-extensions/ai-sdk-ui` bridge on `createAiSdkUiMessageStream(...)`, writes bridge usage metadata after each settled run, and exposes the beta `aisdk(model)` adapter as an explicit `not-used` boundary because this demo's main run still needs deferred Responses tool loading.\n- The `Voice Agents` slice now spans `server/voice.ts`, `server/voice-realtime.ts`, `server/voice-websocket.ts`, `server/voice-server-audio.ts`, `server/voice-sip.ts`, `server/voice-sip-route.ts`, `server/voice-cloudflare-worker.ts`, `server/voice-cloudflare-app.ts`, `server/voice-cloudflare-worker-module.ts`, `server/voice-twilio-route.ts`, `server/voice-twilio-bridge.ts`, `server/voice-twilio-app.ts`, `server/voice-extensions.ts`, and `ui/openai-agents-sdk-demo-voice-panel.tsx`. It exposes the official `RealtimeAgent` / `RealtimeSession` primitives, wires a real `/api/demos/openai-agents-sdk-demo/realtime/client-secrets` route through `client.realtime.clientSecrets.create()`, meters successful client-secret minting as `send_message` usage for `openai-agents-sdk-demo`, starts a browser `OpenAIRealtimeWebRTC` session through `session.connect({ apiKey })` on the page itself, includes dedicated server-side factories for `OpenAIRealtimeWebSocket`, a raw server audio loop on `RealtimeSession.sendAudio()` plus `session.on(\"audio\")`, `OpenAIRealtimeSIP`, `CloudflareRealtimeTransportLayer`, and `TwilioRealtimeTransportLayer`, and now exposes a Cloudflare worker runtime wrapper, a deployed-shape Cloudflare fetch app, a deployable Cloudflare worker module, and a deployed-shape Twilio media-stream app factory. The workspace layout keeps `Conversation + Screen` as the only persistent regions inside the `100svh` demo surface; the right-side screen rail owns the compact voice entry strip, and the full realtime controls live in a dialog so the voice lane does not keep compressing the chat column.\n- The first guide-family coverage registry lives in `lib/openai-agents-sdk-demo/server/guide-coverage.ts`. It tracks source guide URL, SDK primitive, observable runtime evidence, implementation status, provider capability status, and current-run readiness for each planned guide family.\n- The current-run inspector derivation lives in `components/openai-agents-sdk-demo/openai-agents-sdk-demo-runtime-inspector.ts`. The **Demo Workspace** passes messages, guide coverage, and setup profiles into this **Runtime Inspector Module** instead of scanning assistant metadata inline.\n- The chat route now writes tool-usage and guardrail-evaluation metadata after the streamed run settles, so the workspace can mark `Tools`, `Guardrails`, and individual catalog rows from real SDK results instead of inferring usage from visible text alone.\n- The chat route now also writes stream-summary metadata after the streamed run settles. The current summary captures agent names plus counts and unique names for `raw_model_stream_event` and `run_item_stream_event`.\n- The chat route now also writes handoff-summary metadata after the streamed run settles. The current summary captures `lastAgent`, handoff target names, and handoff transitions from real `handoff_call_item` / `handoff_output_item` values.\n- The chat route now also writes result-summary metadata after the streamed run settles. The current summary captures the final output preview, active/last agent, history/output/new-item counts, interruption count, `state.usage`, raw-response count, and whether resumable run state was captured.\n- The route also converts guardrail tripwires into explicit user-visible error messages. Input tripwires return a 400 before the stream starts, and output tripwires surface through the AI SDK UI error channel.\n- The right-side workspace panel now renders the initial **Official Guide Coverage Panel** plus visible model, running, session, streaming, AI SDK extension, voice, handoff, approval, result, tracing, sandbox, tool, and guardrail inspector sections. The `Agents`, `Models`, `Tools`, `Guardrails`, `Running Agents`, `Streaming`, `Agent Orchestration`, `Handoffs`, `Results`, `Human-in-the-loop`, `Sessions`, `Tracing`, `Sandbox Agents`, `Voice Agents`, and `AI SDK Extension` rows are marked `implemented` before chat. `Voice Agents` stays `blocked` only when `OPENAI_API_KEY` is missing for the realtime client-secret route, moves to `ready` once configured, and moves to `used this run` after the browser voice lane connects or sends a realtime turn. The voice inspector now also surfaces explicit SIP route, Cloudflare worker runtime/app/module, and Twilio/Cloudflare transport contracts.\n- The frontend currently renders assistant text, tool parts, approval cards, file attachments, running state, session-summary metadata, stream-summary metadata, AI SDK extension metadata, handoff-summary metadata, approval-summary metadata, settled result-summary metadata, trace-summary metadata, and sandbox-summary metadata. It still does not expose typed final output cards or raw trace drilldowns.\n- The current message replay path manually converts `UIMessage` text into `AgentInputItem[]`. This is enough for a basic chat smoke test and too lossy for the ultra version because it drops non-text parts, tool outputs, handoff boundaries, approval state, response ids, and SDK item metadata.\n- The current core-contract tests cover request validation, setup errors, guide coverage exposure, AI Gateway client injection, message conversion, bridge invocation, stream-error surfacing, tool and guardrail catalog exposure, tool and guardrail injection into the main `Agent`, tool-usage metadata emission, and explicit input-guardrail 400 handling.\n- The first tools QA probe confirmed that AI Gateway's OpenAI-compatible Responses path in this repo accepts both hosted `web_search` and hosted `code_interpreter`.\n\n## Architecture Direction\n\n- Keep the frontend stack: Next.js route entries, the existing demo catalog shape, AI SDK UI state, AI Elements primitives, and feature-local workspace components.\n- Keep the demo copyable as one feature slice. Shared abstractions should appear only when reuse is already real, and any new shared dependency must not make the demo harder to lift into a compatible project.\n- Keep capability modules aligned to official guide names and examples. A future reader should be able to map one module or mode back to one OpenAI Agents SDK guide without reverse-engineering repo-specific orchestration.\n- Move the backend toward a SDK-first deep module before adding more visible features:\n  - `server/agents.ts`: agent graph definitions, `Agent.create(...)` where handoff output types matter, tools, guardrails, and output schemas.\n  - `server/runner.ts`: `Runner` construction, run options, cancellation, `maxTurns`, tracing metadata, and Gateway client bootstrap.\n  - `server/state.ts`: session ids, serialized `RunState`, approval resolution, `history`, `lastResponseId`, and any local persistence boundary.\n  - `server/stream.ts`: full SDK event handling plus the AI SDK UI bridge. Text streaming should stay bridged; richer SDK events should be emitted through explicit UI message parts or feature-local side-channel metadata.\n  - `server/capabilities/*`: one file per official-document capability lane when the module gets large enough. These are internal lanes inside one demo, not separate Agent Demos.\n- Configure SDK-wide defaults in one stable bootstrap path, not ad hoc inside every request branch. The OpenAI Agents SDK config docs describe `setDefaultOpenAIClient(...)` and `setOpenAIAPI(...)` as process-wide defaults.\n- Keep AI Gateway as a transport/provider layer. Do not let provider swapping erase SDK semantics. Features that depend on OpenAI Responses-specific behavior should fail explicitly when the selected Gateway model/provider cannot support them.\n- Capability support must be explicit. A capability can be `implemented`, `used this run`, `not exercised`, `blocked by provider`, or `requires native OpenAI credentials`; it should never be treated as covered by a silent fallback.\n- Prefer OpenAI Responses API chaining, sessions, or serialized `RunState` over manual UI transcript reconstruction once tools, approvals, handoffs, or MCP enter the flow.\n- Preserve the official AI SDK UI bridge for assistant text and standard AI SDK UI chunks. Add feature-local rendering for SDK-native artifacts that the bridge does not expose well enough.\n- Mirror the `skills-agent` product posture: real runtime capability first, visible state second, explanatory copy only where it helps the evaluator operate the workspace.\n- Keep official guide coverage visible in the right-side inspector. A user should be able to see that a Tesla research conversation exercised `Tools`, `Handoffs`, `Results`, `Sessions`, `Tracing`, or other official guide families without reading the code.\n- Make coverage evidence concrete enough for code review. Avoid vague \"supported\" badges unless the row also names the official guide, SDK primitive, observable run item/event, and current-run usage state.\n- Keep all credentials explicit:\n  - `AI_GATEWAY_API_KEY` is required for the Gateway-backed OpenAI client.\n  - `OPENAI_AGENTS_GATEWAY_BASE_URL` remains an optional demo-local override.\n  - `OPENAI_AGENTS_MODEL` remains an optional demo-local model override.\n  - Add hosted-tool, MCP, tracing, sandbox, or voice credentials only when the matching lane is implemented.\n\n## Implementation Rhythm\n\n- First build the coverage skeleton before deepening any one feature:\n  - guide-family registry\n  - implementation status\n  - provider capability block status\n  - per-run usage status\n  - run-state normalization shape\n  - right-side coverage/runtime inspector\n- Then integrate OpenAI Agents SDK capabilities one official guide at a time.\n- After finishing one guide, run a narrow QA pass only for that newly added capability. Do not expand QA into full Tesla research until the guide-family coverage is complete.\n- Keep each QA pass simple and capability-specific:\n  - can the user trigger the new capability from the chat?\n  - does the inspector mark the matching official guide as `used this run`?\n  - does the visible runtime evidence name the SDK primitive and run item/event?\n  - does provider-blocked behavior show an explicit blocked/setup state?\n- Run the full Tesla investment-research task only after the official guide-family coverage has been implemented and smoke-tested guide by guide.\n\n## Capability Matrix\n\n| Official guide family | Ultra-demo target                                                                                                                                  | Current status                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | First implementation target                                                                                                                                                          |\n| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| Agents                | Agent graph with dynamic instructions, typed context, lifecycle hooks, and output schemas                                                          | First slice implemented: `Agent` construction is feature-local and visible in guide coverage                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Complete the remaining Agents-guide surface: dynamic instructions, lifecycle hooks, clone/copy behavior, `handoffDescription`, and output type where it is not owned by later guides |\n| Sandbox agents        | Show sandbox agent shape and workspace lifecycle inside the same demo if the SDK/browser runtime path is stable enough                             | First slice implemented: `SandboxAgent`, `Manifest`, `Capabilities.default()`, `UnixLocalSandboxClient`, `RunConfig.sandbox`, repo-docs/feature `localDir(...)` mounts, and process-local `sessionState` persistence are visible in runtime metadata                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | Add Docker/hosted clients, snapshots, memory, and skills only after the local Unix sandbox lane is stable                                                                            |\n| Models                | Model settings, reasoning effort, provider data, Gateway model ids, and unsupported-provider errors                                                | First slice implemented: centralized model profile, explicit Responses API + HTTP transport, and visible reasoning/verbosity diagnostics                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Add provider-specific capability blocks for unsupported transport or deferred-tool-loading paths                                                                                     |\n| Tools                 | Official function tools, hosted tools where supported, agents-as-tools, and tool states                                                            | Implemented with one explicit provider block: `tool()`, `webSearchTool()`, `fileSearchTool()` (setup-gated), `codeInterpreterTool()`, `imageGenerationTool()` (registered but provider-blocked on the current AI Gateway Responses path), `toolSearchTool()`, `agent.asTool()`, tool catalog, and per-run tool metadata                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | Keep the provider block visible until AI Gateway returns renderable image artifacts for hosted image generation on the official streamed run path                                    |\n| Guardrails            | Input, output, and tool guardrails with visible tripwire/error states                                                                              | First slice implemented: one input guardrail, one output guardrail, explicit tripwire errors, guardrail catalog, and per-run guardrail metadata                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Add tool guardrails and richer tripwire/result rendering once run-state and results surfaces land                                                                                    |\n| Running Agents        | `run()`, provider-aware `previousResponseId` / `MemorySession` continuation, `maxTurns`, request abort signal, and visible run profile             | First slice implemented: feature-local running module, explicit `maxTurns`, request-scope `AbortSignal`, `resp_*` `lastResponseId` continuation, AI Gateway `gen_*` MemorySession continuation, and visible runtime inspector state                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Add richer trace metadata and explicit error-handler coverage                                                                                                                        |\n| Streaming             | Text deltas plus full SDK event stream for tools, handoffs, approvals, agent switches                                                              | First slice implemented: keep `createAiSdkUiMessageStream()` on the main path and collect `RunStreamEvent` metadata for the inspector                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Expand beyond counts into visible handoff, approval, and richer item-event rendering                                                                                                 |\n| Agent orchestration   | Official agents-as-tools and code-orchestrated examples                                                                                            | First slice implemented: `research_memo_agent` runs through `agent.asTool()` and marks guide usage from real tool metadata                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | Add code-orchestrated examples only after the documented agents-as-tools path is fully visible in the workspace                                                                      |\n| Handoffs              | Official handoff examples, typed final output, active-agent continuation                                                                           | First slice implemented: one direct specialist agent in `handoffs: [agent]`, one explicit `handoff()`, handoff catalog, and per-run handoff metadata in the inspector                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Add typed final-output unions and richer active-agent transitions now that Results summary exists                                                                                    |\n| Results               | `finalOutput`, `newItems`, `history`, `interruptions`, `state`, usage, raw diagnostics                                                             | First slice implemented: settled result summary mapped from `StreamedRunResult` after `completed`, exposed through message metadata and the runtime inspector                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | Deepen from summary counts/previews into typed result cards, resumable state actions, and richer raw-response diagnostics                                                            |\n| Human-in-the-loop     | Approval interruptions, approve/reject UI, resume from `RunState`                                                                                  | First slice implemented: one approval-required `tool({ needsApproval: true })`, AI SDK approval UI, serialized paused `RunState`, and resume via `RunState.fromString(...)` plus `state.approve(...)` / `state.reject(...)`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Add multi-approval batches, sticky decisions, and session-aware approval resume                                                                                                      |\n| Sessions              | SDK session memory, session input callback, approval compatibility                                                                                 | First slice implemented: process-local `MemorySession`, assistant-metadata session id handoff, reuse across AI Gateway follow-ups, true `previousResponseId` follow-ups, and approval resume, and session inspector metadata                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Add `sessionInputCallback`, visible CRUD actions, and compaction-aware session options                                                                                               |\n| Context management    | Local `RunContext<T>` for dependencies and user/session metadata                                                                                   | First slice implemented: typed demo context built from the latest user turn plus `MemorySession`, passed through `run(..., { context })`, consumed by dynamic instructions, `tool()`, `agent.asTool`, and guardrails, with context profile/summary visible in the inspector                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Add lifecycle-hook usage and any later dependency objects only when a visible lane needs them                                                                                        |\n| MCP                   | Hosted MCP and/or local MCP server tool path with approval behavior                                                                                | First slice implemented: one demo-local `MCPServerStreamableHttp` route, `connectMcpServers(...)`, `Agent.mcpServers`, `Agent.mcpConfig.includeServerInToolNames`, MCP catalog, and per-run MCP connection/usage metadata in the runtime inspector                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Add hosted MCP and approval-specific MCP examples after the local Streamable HTTP path is stable                                                                                     |\n| Tracing               | Built-in tracing metadata, trace/group ids, usage, optional flush in serverless runtime                                                            | First slice implemented: explicit per-run `workflowName`, `traceId`, `groupId`, `traceMetadata`, `tracingDisabled`, `traceIncludeSensitiveData`, optional `tracing.apiKey` override, trace summary metadata, and a visible tracing panel in the runtime inspector                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Add exporter/flush visibility and deeper span drilldowns once a native OpenAI trace credential path is configured                                                                    |\n| Voice agents          | Cover the voice-agent docs inside this demo's planning surface, then implement realtime voice only through the official realtime route/UI contract | Current slice implemented: `RealtimeAgent`, `RealtimeSession`, `OpenAIRealtimeWebRTC`, `OpenAIRealtimeWebSocket`, a server audio loop on `RealtimeSession.sendAudio()` plus `session.on(\"audio\")`, `OpenAIRealtimeSIP`, `CloudflareRealtimeTransportLayer`, `TwilioRealtimeTransportLayer`, `/api/.../realtime/client-secrets`, `/api/.../realtime/sip`, `/api/.../realtime/twilio/incoming-call`, a Cloudflare worker runtime wrapper on `CloudflareRealtimeTransportLayer + RealtimeSession.connect(...)`, a deployed-shape Cloudflare fetch app with `/` and `/connect`, a deployable Cloudflare worker module with `export default { fetch(...) }`, a Twilio websocket media-stream bridge on `TwilioRealtimeTransportLayer + RealtimeSession.connect(...)`, a deployed-shape Twilio app factory with `/`, `/incoming-call`, and `/media-stream`, browser microphone controls, realtime text smoke-test input, native `OPENAI_API_KEY` setup state, one realtime handoff target, two realtime function tools, approval controls, visible session-event state, and dedicated server-side transport factories; text chat still is not counted as voice support | Deploy one of the worker/server wrappers in its target runtime and verify a live provider call or socket path end to end                                                             |\n| Extensions            | AI SDK model adapter and AI SDK UI bridge                                                                                                          | First slice implemented: official AI SDK UI bridge is visible in coverage/runtime metadata; beta `aisdk(model)` remains an explicit `not-used` boundary because deferred Responses tool loading is required by this demo                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Keep UI bridge on the main path; revisit AI SDK model adapter only for models that do not need deferred Responses tool loading                                                       |\n\n## Ultra Implementation Checklist\n\n- [ ] Refresh official source docs and examples before coding the next slice.\n- [ ] Build the first-stage coverage skeleton before deepening individual capabilities.\n- [ ] Implement the OpenAI Agents SDK guides through a **Document-by-document integration path**.\n- [ ] After each official guide is integrated, run a narrow QA pass for only that newly added capability.\n- [ ] Defer full Tesla investment-research QA until the official guide-family coverage is complete.\n- [ ] For each capability, preserve the official source core before writing custom orchestration, tools, prompts, or state machines.\n- [ ] Treat the Tesla investment-research flow as the default suggested task for a **General OpenAI Agent**, not as permission to replace official SDK patterns with domain-specific custom logic.\n- [ ] Add explicit **Provider capability block** states for official capabilities that AI Gateway or the selected model/provider cannot support.\n- [ ] Do not count custom tools, custom fetchers, or custom orchestration as official capability coverage.\n- [ ] Refactor backend into SDK-first modules without changing the route or frontend contract.\n- [ ] Keep every implemented capability inside the `openai-agents-sdk-demo` copy boundary unless the user explicitly approves shared extraction.\n- [ ] Add a stable Gateway/OpenAI bootstrap path and tests proving it is not repeatedly reconfigured with conflicting per-request state.\n- [x] Replace lossy text-only replay with a continuation strategy: `history`, `session`, `RunState`, `lastResponseId`, or an explicit combination chosen per capability lane.\n- [x] Add a run-result normalization layer that preserves `finalOutput`, `newItems`, `interruptions`, `state`, `history`, `lastAgent`, `lastResponseId`, and usage. Keep trace metadata for the Tracing guide.\n- [x] Extend the UI beyond text: tool calls, tool results, agent switches, handoffs, approval cards, guardrail failures, session status, result summary, and trace summary.\n- [x] Add the initial **Official Guide Coverage Panel** on the right side of the chat workspace.\n- [x] Add the full **Runtime Inspector Panel** for SDK run items, state, approvals, sessions, and traces.\n- [ ] Mark both implementation coverage and per-run usage for each official guide family.\n- [x] For each coverage row, show source guide, SDK primitive, run item/event, implementation status, and current-run status.\n- [ ] Match the `skills-agent` quality bar: real source-core runtime, visible configured capability surface, visible runtime artifacts/events, explicit setup contract, and a portable README.\n- [x] Add focused tests around the core 10 percent: run state normalization, approval resume, handoff active-agent continuation, guardrail tripwire behavior, and session continuation.\n- [x] Add a first official MCP lane using the local `MCPServerStreamableHttp` route before attempting hosted MCP or connector-backed MCP.\n- [ ] Keep typecheck/lint failures from unrelated work out of this demo's acceptance notes.\n\n## Agents Guide Checklist\n\n- [x] Keep the first `Agent` on the official SDK construction path and route it through `run(agent, input, { stream: true })`.\n- [x] Move the first agent definition into `server/agents.ts` so later guide work can deepen the SDK graph without bloating `server/chat.ts`.\n- [x] Expose the official `Agents` guide evidence in runtime state and the right-side coverage panel.\n- [x] Add a core-contract test that proves runtime state exposes `Agents -> Agent -> Agent instance passed to run()`.\n- [x] Add dynamic instructions from the `Agents` guide and pass typed run context through the official SDK path.\n- [ ] Add visible lifecycle hook capture for `agent_start` and `agent_end` before claiming hook coverage.\n- [ ] Add clone/copy behavior only when there is a visible mode or test proving the cloned agent is used.\n- [ ] Add `handoffDescription` with the handoffs guide, unless an earlier `Agents` guide slice needs it for visible coverage.\n- [ ] Add `outputType` with the results / structured-output slice, unless the `Agents` guide slice can show it without breaking chat text output.\n\n## Models Guide Checklist\n\n- [x] Centralize model id, Gateway base URL, Responses API selection, and transport in `server/models.ts`.\n- [x] Pass explicit `modelSettings.reasoning.effort` and `modelSettings.text.verbosity` into the official `Agent` constructor.\n- [x] Expose a developer-verifiable model profile in runtime state and the right-side inspector.\n- [x] Mark the `Models` guide row as `implemented` / `ready`, then `used this run` after a successful assistant turn.\n- [x] Add a core-contract test that proves runtime state exposes the active model profile for the Models guide.\n- [x] Add a chat-path test that proves GPT-5 text verbosity remains explicit when custom reasoning settings are provided.\n- [ ] Add provider capability blocks for Responses WebSocket transport and deferred Responses tool loading before claiming those paths.\n- [ ] Add visible provider-data diagnostics when a later guide needs provider-specific settings beyond reasoning and verbosity.\n\n## Tools Guide Checklist\n\n- [x] Add one thin official `tool()` example that fits the Tesla-style research flow without pretending to cover unsupported capabilities.\n- [x] Add hosted `webSearchTool()` to the main agent and expose it in the right-side tool catalog.\n- [x] Add hosted `codeInterpreterTool()` to the main agent and expose it in the right-side tool catalog.\n- [x] Add one specialist `agent.asTool()` path and keep it inside the same demo copy boundary.\n- [x] Mark the `Tools` guide row as `implemented` / `ready`, then `used this run` from actual run-item metadata.\n- [x] Add a core-contract test that proves runtime state exposes the current tool catalog and the Tools guide coverage row.\n- [x] Add a chat-path test that proves the main `Agent` receives the configured tool set and that tool usage is returned to the UI as message metadata.\n- [x] Wire `fileSearchTool()` behind a real vector-store contract: `OPENAI_AGENTS_VECTOR_STORE_IDS`.\n- [x] Render assistant file outputs as first-class chat artifacts when the upstream run returns image bytes.\n- [x] Wire `toolSearchTool()` into the Tools slice and gate it by model support.\n- [x] Register `imageGenerationTool()` and show an explicit provider capability block when the current AI Gateway Responses path cannot deliver a renderable artifact.\n\n## Guardrails Guide Checklist\n\n- [x] Add one input guardrail on the official `inputGuardrails` path and keep it inside the main demo agent.\n- [x] Add one output guardrail on the official `outputGuardrails` path and keep it inside the main demo agent.\n- [x] Mark the `Guardrails` guide row as `implemented` / `ready`, then `used this run` from actual guardrail-result metadata.\n- [x] Expose a developer-verifiable guardrail catalog in runtime state and the right-side inspector.\n- [x] Return explicit input-guardrail errors instead of letting the request collapse into `failed to fetch`.\n- [x] Surface output-guardrail tripwires through the AI SDK UI error handler with a demo-specific explanation.\n- [x] Add focused tests that prove the main `Agent` receives the configured guardrails, that guardrail metadata reaches the UI, and that input tripwires return a 400 response.\n- [ ] Add tool guardrails when the later Tools and Results slices expose tool input/output state as first-class runtime artifacts.\n\n## Running Agents Guide Checklist\n\n- [x] Keep the official `run()` helper on the main execution path and expose that choice in the coverage panel.\n- [x] Move run-shaping logic into `server/running.ts` so the guide slice has one focused module for continuation strategy and run options.\n- [x] Pass explicit `maxTurns` into the run options instead of relying on hidden SDK defaults.\n- [x] Pass the request `AbortSignal` from the route/runtime layer into the OpenAI Agents SDK run call.\n- [x] Continue follow-up turns with `previousResponseId` only when a settled assistant turn exposed a true OpenAI Responses `resp_*` id; otherwise continue with latest-user input backed by the official `MemorySession` so AI Gateway `gen_*` ids do not break history.\n- [x] Surface the running profile in runtime state and the right-side inspector: workflow name, continuation strategy, max turns, and last response id.\n- [x] Mark the `Running Agents` guide row as `implemented` / `ready`, then `used this run` from real run metadata.\n- [x] Add focused tests that prove runtime state exposes the running profile, the route passes request abort state through, `resp_*` follow-ups use `previousResponseId`, and AI Gateway `gen_*` follow-ups use `MemorySession` continuation.\n- [x] Add typed `RunContext<T>` once the Context Management guide lands.\n- [ ] Add `errorHandlers` coverage once the demo can visibly distinguish handled `maxTurns` / `modelRefusal` results from thrown errors.\n\n## Streaming Guide Checklist\n\n- [x] Keep the official `createAiSdkUiMessageStream()` bridge on the main execution path.\n- [x] Add a feature-local `server/streaming.ts` module that observes `RunStreamEvent` values without changing the bridged event shape.\n- [x] Capture developer-verifiable stream metadata for `agent_updated_stream_event`, `raw_model_stream_event`, and `run_item_stream_event`.\n- [x] Surface the latest stream summary in the right-side inspector with counts and unique event names/sources.\n- [x] Mark the `Streaming` guide row as `implemented` / `ready`, then `used this run` from actual stream metadata.\n- [x] Add focused tests that prove the observer preserves event order, metadata collection works, and the chat route returns the stream summary to the UI.\n- [ ] Expand stream rendering beyond summary metadata now that Handoffs, Human-in-the-loop, and Results are on the main path.\n\n## Agent Orchestration Guide Checklist\n\n- [x] Keep the first orchestration slice on the official `agent.asTool()` path that already exists inside the demo tool graph.\n- [x] Mark the `Agent Orchestration` guide row as `implemented` / `ready` in runtime state.\n- [x] Mark the `Agent Orchestration` guide row as `used this run` when `research_memo_agent` executes.\n- [x] Add focused tests that prove runtime state exposes the guide row and run metadata maps `research_memo_agent` usage back to `agent-orchestration`.\n- [ ] Add code-orchestrated multi-agent examples only after the current `agent.asTool()` path is visibly exercised in the workspace.\n\n## Handoffs Guide Checklist\n\n- [x] Add one direct specialist agent through `handoffs: [agent]` and keep it inside the demo copy boundary.\n- [x] Add one explicit `handoff()` example with `inputType`, `onHandoff`, and an input filter that strips tool history before transfer.\n- [x] Prefix specialist-agent instructions with the official recommended handoff prompt semantics, using a demo-local helper because `@openai/agents-core/extensions` is not resolvable from the current app package graph.\n- [x] Surface a developer-verifiable handoff catalog in runtime state and the right-side inspector.\n- [x] Mark the `Handoffs` guide row as `implemented` / `ready`, then `used this run` from real `handoff_call_item` / `handoff_output_item` metadata.\n- [x] Add focused tests that prove runtime state exposes the handoff catalog, the main `Agent` receives the configured handoff surface, and run metadata maps real handoff items back to the `handoffs` guide.\n- [ ] Add typed final-output unions and richer current-agent transitions on top of the current Results summary.\n\n## Results Guide Checklist\n\n- [x] Wait for `await agentStream.completed` before reading settled `RunResult` fields.\n- [x] Move result-summary shaping into `server/results.ts` so the guide slice stays isolated from the stream bridge.\n- [x] Persist/show `activeAgent`, `lastAgent`, `finalOutput`, `history`, `newItems`, `output`, `interruptions`, `state.usage`, and `rawResponses` through message metadata.\n- [x] Surface the latest settled result summary in the right-side inspector with final-output preview, counts, usage totals, and resumable-state status.\n- [x] Mark the `Results` guide row as `implemented` / `ready`, then `used this run` from real settled result metadata.\n- [x] Add focused tests that prove result-summary shaping works, the chat route emits settled result metadata, and runtime state exposes the Results guide coverage row.\n- [ ] Deepen the inspector from summary-level counts into typed result cards and raw-response drilldowns.\n\n## Human-in-the-loop Guide Checklist\n\n- [x] Add one approval-required `tool({ needsApproval: true })` inside the demo copy boundary.\n- [x] Keep the approval pause/resume flow on the official `RunToolApprovalItem` plus `RunState` path.\n- [x] Surface approval-requested tool parts through AI SDK UI approval cards instead of a custom prompt workaround.\n- [x] Serialize paused `RunState` into assistant metadata and resume through `RunState.fromString(...)`.\n- [x] Apply approve/reject decisions with `state.approve(...)` and `state.reject(...)`, then continue the same root run with `run(agent, state, { stream: true })`.\n- [x] Surface approval summary metadata in the right-side inspector with pending approvals, decisions, and paused-state status.\n- [x] Mark the `Human-in-the-loop` guide row as `implemented` / `ready`, then `used this run` from real approval metadata.\n- [x] Add focused tests that prove paused-state serialization, approval resume, and pending-approval blocking behavior.\n- [ ] Add multi-approval batches, sticky decisions, and session-aware approval resume.\n\n## Sessions Guide Checklist\n\n- [x] Add one official `MemorySession` on the main run path and keep the session primitive visible in runtime state.\n- [x] Carry the session id through assistant message metadata so later turns can reuse the same session without widening the route contract.\n- [x] Reuse the same session for AI Gateway follow-up turns, true `previousResponseId` follow-up turns, and `RunState` approval resume.\n- [x] Rehydrate a missing process-local session from visible transcript history so the demo can recover after a store miss without inventing custom session abstractions.\n- [x] Surface session profile and latest session summary in the right-side inspector: primitive, storage scope, transport, session id, and history item count.\n- [x] Mark the `Sessions` guide row as `implemented` / `ready`, then `used this run` from real message metadata.\n- [x] Add focused tests that prove session creation, session reuse, session rehydration, and chat-path session wiring.\n- [ ] Add `sessionInputCallback`, visible history CRUD actions, and compaction-aware session options after Context Management is on the main path.\n\n## Context Management Guide Checklist\n\n- [x] Define one typed demo `RunContext<T>` object and keep it feature-local under `server/context.ts`.\n- [x] Build the context from the latest user turn plus the current `MemorySession` id before the initial `run(...)`.\n- [x] Pass the typed context through the official `run(..., { context })` path.\n- [x] Use dynamic main-agent instructions to expose only the LLM-visible parts of local context.\n- [x] Read `runContext.context` inside at least one `tool()` callback and one guardrail callback.\n- [x] Read `runContext.toolInput` inside the specialist `agent.asTool()` lane.\n- [x] Surface context profile and latest context summary in the right-side inspector.\n- [x] Mark the `Context Management` guide row as `implemented` / `ready`, then `used this run` from real assistant metadata.\n- [x] Add focused tests that prove context creation, usage metadata, runtime-state exposure, and chat-path `run(..., { context })` wiring.\n- [ ] Add lifecycle-hook usage or richer dependency objects only when a later guide exposes them visibly.\n\n## Tracing Guide Checklist\n\n- [x] Keep the first tracing slice on the official per-run `RunConfig` path instead of a demo-local trace wrapper.\n- [x] Move tracing-profile and trace-run shaping into `server/tracing.ts`.\n- [x] Pass explicit `workflowName`, `traceId`, `groupId`, `traceMetadata`, `tracingDisabled`, and `traceIncludeSensitiveData` through `run(...)`.\n- [x] Use the current `MemorySession` id as the visible trace `groupId` so follow-up turns share one conversation grouping key.\n- [x] Keep trace-export credentials explicit by using only the official `tracing.apiKey` override path when `OPENAI_AGENTS_TRACING_API_KEY` or `OPENAI_API_KEY` is present.\n- [x] Persist the latest trace summary into assistant message metadata and expose it in the right-side inspector.\n- [x] Mark the `Tracing` guide row as `implemented` / `ready`, then `used this run` from real trace metadata.\n- [x] Add focused tests that prove trace profile shaping, trace run-config shaping, latest-trace reuse on approval resume, and chat-path trace option wiring.\n- [ ] Add exporter-loop / flush visibility only after the demo has a deliberate native OpenAI tracing credential contract.\n\n## Sandbox Agents Guide Checklist\n\n- [x] Keep the first sandbox slice on the official `SandboxAgent` path and expose it as an `agent.asTool()` specialist inside the main demo agent.\n- [x] Define the fresh workspace with `Manifest`, `localDir(...)`, and a stable `/workspace` root that mounts the demo docs and feature slice read-only.\n- [x] Use `Capabilities.default()` so the sandbox specialist gets the SDK's default filesystem, shell, and compaction capability surface.\n- [x] Pass `UnixLocalSandboxClient` through the official `run(..., { sandbox })` config on both initial runs and approval-resume runs.\n- [x] Persist and reuse serialized sandbox `sessionState` by demo `sessionId` without inventing a custom sandbox lifecycle manager.\n- [x] Surface sandbox profile and latest sandbox summary in the right-side inspector: backend, specialist model, manifest root, mounted paths, workspace readiness, capabilities, SDK primitives, and persisted session count.\n- [x] Mark the `Sandbox Agents` guide row as `implemented` / `ready`, then `used this run` when the sandbox specialist tool executes or SDK sandbox state is serialized.\n- [x] Add focused tests that prove sandbox profile exposure, run-config session reuse, guide coverage status, chat-path sandbox config wiring, and sandbox usage metadata emission.\n- [ ] Add Docker/hosted sandbox clients only when the demo needs stronger isolation than `UnixLocalSandboxClient`.\n- [ ] Add snapshots, sandbox memory, and lazy sandbox skills after the local Unix sandbox lane is stable and visible in QA.\n\n## Voice Agents Guide Checklist\n\n- [x] Document the official `RealtimeAgent` / `RealtimeSession` primitive boundary inside the demo copy surface.\n- [x] Expose the browser WebRTC path as requiring an ephemeral client secret generated by a backend route.\n- [x] Expose the server WebSocket path as requiring a native OpenAI API key and realtime model support.\n- [x] Mark the `Voice Agents` guide row from the real browser voice lane instead of counting the current text chat route as realtime voice support.\n- [x] Add a runtime inspector panel for the Voice Agents provider/transport block.\n- [x] Add focused tests that prove the voice profile and guide coverage block are visible.\n- [x] Add a backend route that calls `POST /v1/realtime/client_secrets` with a server API key.\n- [x] Add browser audio UI that constructs a real `RealtimeAgent`, `RealtimeSession`, and `session.connect({ apiKey })`.\n- [x] Add one realtime handoff target, one approval-required realtime tool, and visible `tool_approval_requested` / `agent_handoff` / `agent_tool_*` state inside the browser voice lane.\n- [x] Add a real server-side `OpenAIRealtimeWebSocket` session factory with explicit `OPENAI_API_KEY` gating, `session.transport.sendEvent()` escape hatch, and focused contract tests.\n- [x] Add a real server audio lane on top of `RealtimeSession.sendAudio()`, `session.on(\"audio\")`, `session.on(\"transport_event\")`, `session.transport.requestResponse()`, and `RealtimeSession.interrupt()`.\n- [x] Add a real `OpenAIRealtimeSIP` session factory plus `OpenAIRealtimeSIP.buildInitialConfig()` helper with explicit `callId` and provider call-control contract.\n- [x] Add a real `/api/demos/openai-agents-sdk-demo/realtime/sip` route that validates `callId` and returns the official SIP accept payload from `OpenAIRealtimeSIP.buildInitialConfig()`.\n- [x] Add real `CloudflareRealtimeTransportLayer` and `TwilioRealtimeTransportLayer` factories with explicit `OPENAI_API_KEY` setup contracts.\n- [x] Add a real Cloudflare worker runtime wrapper on top of `CloudflareRealtimeTransportLayer`, `RealtimeSession.connect(...)`, and the workerd `fetch() + Upgrade: websocket` contract.\n- [x] Add a real deployed-shape Cloudflare worker fetch app with `/` health and `/connect` session-bootstrap behavior on top of the current Cloudflare runtime wrapper.\n- [x] Add a deployable Cloudflare worker module that exports `fetch(request, env, ctx)` on top of the current worker app factory.\n- [x] Add a real `/api/demos/openai-agents-sdk-demo/realtime/twilio/incoming-call` route that returns TwiML `<Connect><Stream />` and gates on `OPENAI_AGENTS_TWILIO_MEDIA_STREAM_URL`.\n- [x] Add a real Twilio websocket bridge on top of `TwilioRealtimeTransportLayer`, `RealtimeSession.connect(...)`, and websocket close events.\n- [x] Surface SIP route plus SIP/Twilio/Cloudflare transport lanes in runtime state and the right-side inspector without pretending the current page is already a telephony or worker product.\n- [x] Add a real deployed-shape Twilio media-stream app factory with `/`, `/incoming-call`, and `/media-stream` entry points on top of the current Twilio transport wrapper.\n- [ ] Deploy one of the provider wrappers outside the Next app and verify a live provider or browser-connected socket path end to end.\n\n## Extensions / AI SDK Integration Checklist\n\n- [x] Keep the official `createAiSdkUiMessageStream()` bridge on the main route instead of replacing it with a custom stream protocol.\n- [x] Add a feature-local `server/extensions.ts` module that records the official AI SDK extension profile and usage metadata.\n- [x] Expose the AI SDK UI bridge profile in runtime state and the right-side inspector: bridge primitive, response helper, and current bridge status.\n- [x] Mark the `AI SDK Extension` guide row as `implemented` / `ready`, then `used this run` from real assistant metadata after the bridge path settles.\n- [x] Keep the beta `aisdk(model)` model adapter boundary explicit as `not-used` while this demo depends on deferred Responses tool loading.\n- [x] Add focused tests that prove extension profile exposure, guide coverage status, and chat-path bridge metadata emission.\n- [ ] Revisit `aisdk(model)` only when a specific model lane can run without deferred Responses tool loading or when the extension supports that SDK capability.\n\n## Update Triggers\n\n- Update this file when the canonical OpenAI Agents SDK TypeScript guide routes change.\n- Update this file when the AI SDK UI bridge package or helper names change.\n- Update this file when the demo moves from the base bridge slice into an ultra capability lane.\n- Update this file when AI Gateway model/provider support changes the safe feature matrix for tools, reasoning, MCP, tracing, or structured output.\n",
      "type": "registry:file",
      "target": "docs/frontend/openai-agents-sdk-demo.md"
    }
  ],
  "envVars": {
    "AI_GATEWAY_API_KEY": "",
    "AI_GATEWAY_CHAT_MODEL": "openai/gpt-5-mini",
    "OPENAI_AGENTS_GATEWAY_BASE_URL": "https://ai-gateway.vercel.sh/v1",
    "OPENAI_AGENTS_MODEL": "",
    "OPENAI_AGENTS_REASONING_EFFORT": "medium",
    "OPENAI_AGENTS_TEXT_VERBOSITY": "low",
    "OPENAI_AGENTS_MAX_TURNS": "6",
    "OPENAI_AGENTS_VECTOR_STORE_IDS": "",
    "OPENAI_AGENTS_TRACING_API_KEY": "",
    "OPENAI_AGENTS_DISABLE_TRACING": "",
    "OPENAI_AGENTS_TRACE_INCLUDE_SENSITIVE_DATA": "",
    "OPENAI_API_KEY": "",
    "OPENAI_AGENTS_TWILIO_MEDIA_STREAM_URL": ""
  },
  "docs": "Install into a Next.js App Router project initialized with shadcn/ui. The main local happy path needs AI_GATEWAY_API_KEY and runs through the installed chat route. Native realtime voice, SIP, Twilio, and Cloudflare transport lanes are included in source and require OPENAI_API_KEY plus their public transport wiring before live verification.",
  "type": "registry:block"
}
