{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chat-routes",
  "title": "Chat Routes (Mastra proxy)",
  "description": "Same-origin Next route handlers + proxy that forward chat/threads/agent-controller/images to a Mastra server (MASTRA_SERVER_URL). Required for the chat block to function.",
  "dependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "lib/mastra-proxy.ts",
      "content": "// Same-origin proxy to the standalone Mastra server. The web app is pure\n// frontend; thread CRUD lives on the server (over Mastra Memory), so these tiny\n// Next route handlers forward to it. Keeps Mastra out of the Next webpack graph\n// and avoids CORS — identical pattern to /api/chat/[agentId].\nconst SERVER_URL = process.env.MASTRA_SERVER_URL ?? 'http://localhost:4111';\n\n/** Forward a request to the Mastra server and stream its JSON response back. */\nexport async function proxy(path: string, init?: RequestInit): Promise<Response> {\n  const upstream = await fetch(`${SERVER_URL}${path}`, { cache: 'no-store', ...init });\n  return new Response(upstream.body, {\n    status: upstream.status,\n    headers: {\n      'content-type': upstream.headers.get('content-type') ?? 'application/json',\n      'cache-control': 'no-store',\n    },\n  });\n}\n\nexport { SERVER_URL };\n",
      "type": "registry:file",
      "target": "lib/mastra-proxy.ts"
    },
    {
      "path": "app/api/agent-controller/stream/route.ts",
      "content": "// Same-origin proxy → standalone Mastra server's Agent Controller SSE route.\n// Streams the AgentControllerEvent SSE straight back to the browser. No Mastra import\n// here, so nothing heavy enters the Next webpack graph.\nconst SERVER_URL = process.env.MASTRA_SERVER_URL ?? 'http://localhost:4111';\n\nexport async function POST(req: Request) {\n  const body = await req.text();\n\n  const upstream = await fetch(`${SERVER_URL}/agent-controller/stream`, {\n    method: 'POST',\n    headers: { 'content-type': 'application/json' },\n    body,\n    // Forward client disconnects so the controller run can abort upstream.\n    signal: req.signal,\n  });\n\n  return new Response(upstream.body, {\n    status: upstream.status,\n    headers: {\n      'content-type': upstream.headers.get('content-type') ?? 'text/event-stream',\n      'cache-control': 'no-cache',\n    },\n  });\n}\n",
      "type": "registry:file",
      "target": "app/api/agent-controller/stream/route.ts"
    },
    {
      "path": "app/api/agent-controller/approve/route.ts",
      "content": "// Same-origin proxy → Mastra server's Agent Controller approval endpoint.\n// Resolves a parked tool-approval gate; the continuation streams on the\n// already-open /agent-controller/stream SSE.\nconst SERVER_URL = process.env.MASTRA_SERVER_URL ?? 'http://localhost:4111';\n\nexport async function POST(req: Request) {\n  const body = await req.text();\n  const upstream = await fetch(`${SERVER_URL}/agent-controller/approve`, {\n    method: 'POST',\n    headers: { 'content-type': 'application/json' },\n    body,\n  });\n  return new Response(upstream.body, {\n    status: upstream.status,\n    headers: { 'content-type': upstream.headers.get('content-type') ?? 'application/json' },\n  });\n}\n",
      "type": "registry:file",
      "target": "app/api/agent-controller/approve/route.ts"
    },
    {
      "path": "app/api/agent-controller/answer/route.ts",
      "content": "// Same-origin proxy → Mastra server's Agent Controller suspension endpoint.\n// Answers a parked `ask_user` prompt; the continuation streams on the\n// already-open /agent-controller/stream SSE.\nconst SERVER_URL = process.env.MASTRA_SERVER_URL ?? 'http://localhost:4111';\n\nexport async function POST(req: Request) {\n  const body = await req.text();\n  const upstream = await fetch(`${SERVER_URL}/agent-controller/answer`, {\n    method: 'POST',\n    headers: { 'content-type': 'application/json' },\n    body,\n  });\n  return new Response(upstream.body, {\n    status: upstream.status,\n    headers: { 'content-type': upstream.headers.get('content-type') ?? 'application/json' },\n  });\n}\n",
      "type": "registry:file",
      "target": "app/api/agent-controller/answer/route.ts"
    },
    {
      "path": "app/api/agent-controller/goal/route.ts",
      "content": "import { proxy } from '@/lib/mastra-proxy';\n\n// Goals are agent-driven — the chat agent sets them via its own `setGoal` tool, not a web\n// POST. These routes only back the goal card: read the current objective (hydrate on\n// reload) and clear it (the card's dismiss control).\n\n// GET /api/agent-controller/goal → the current objective for the session's active thread.\nexport async function GET() {\n  return proxy('/agent-controller/goal');\n}\n\n// DELETE /api/agent-controller/goal → clear the active thread's objective.\nexport async function DELETE() {\n  return proxy('/agent-controller/goal', { method: 'DELETE' });\n}\n",
      "type": "registry:file",
      "target": "app/api/agent-controller/goal/route.ts"
    },
    {
      "path": "app/api/agent-controller/om/route.ts",
      "content": "// Same-origin proxy → Mastra server's Observational-Memory read endpoint.\n// Returns the facts OM has distilled so the Memory panel can hydrate on load.\nconst SERVER_URL = process.env.MASTRA_SERVER_URL ?? 'http://localhost:4111';\n\nexport async function GET() {\n  const upstream = await fetch(`${SERVER_URL}/agent-controller/om`, { cache: 'no-store' });\n  return new Response(upstream.body, {\n    status: upstream.status,\n    headers: { 'content-type': upstream.headers.get('content-type') ?? 'application/json' },\n  });\n}\n",
      "type": "registry:file",
      "target": "app/api/agent-controller/om/route.ts"
    },
    {
      "path": "app/api/agent-controller/schedules/route.ts",
      "content": "// Same-origin proxy → Mastra server's schedules list endpoint.\n// Returns the agent's recurring schedules so the Schedules panel can render them.\nconst SERVER_URL = process.env.MASTRA_SERVER_URL ?? 'http://localhost:4111';\n\nexport async function GET() {\n  const upstream = await fetch(`${SERVER_URL}/agent-controller/schedules`, { cache: 'no-store' });\n  return new Response(upstream.body, {\n    status: upstream.status,\n    headers: { 'content-type': upstream.headers.get('content-type') ?? 'application/json' },\n  });\n}\n",
      "type": "registry:file",
      "target": "app/api/agent-controller/schedules/route.ts"
    },
    {
      "path": "app/api/agent-controller/threads/route.ts",
      "content": "import { proxy } from '@/lib/mastra-proxy';\n\n// GET /api/agent-controller/threads → list the controller's conversations (newest first).\nexport async function GET() {\n  return proxy('/agent-controller/threads');\n}\n",
      "type": "registry:file",
      "target": "app/api/agent-controller/threads/route.ts"
    },
    {
      "path": "app/api/agent-controller/threads/search/route.ts",
      "content": "import { proxy } from '@/lib/mastra-proxy';\n\n// GET /api/agent-controller/threads/search?q= → semantic search over the controller's\n// conversations (matches message bodies via the fastembed vector index).\nexport async function GET(req: Request) {\n  const q = new URL(req.url).searchParams.get('q') ?? '';\n  return proxy(`/agent-controller/threads/search?q=${encodeURIComponent(q)}`);\n}\n",
      "type": "registry:file",
      "target": "app/api/agent-controller/threads/search/route.ts"
    },
    {
      "path": "app/api/agent-controller/threads/[id]/route.ts",
      "content": "import { proxy } from '@/lib/mastra-proxy';\n\n// DELETE /api/agent-controller/threads/:id → hard-delete a controller conversation.\nexport async function DELETE(_req: Request, ctx: { params: Promise<{ id: string }> }) {\n  const { id } = await ctx.params;\n  return proxy(`/agent-controller/threads/${encodeURIComponent(id)}`, { method: 'DELETE' });\n}\n\n// PATCH /api/agent-controller/threads/:id → archive/unarchive or rename (body: { archived?, title? }).\nexport async function PATCH(req: Request, ctx: { params: Promise<{ id: string }> }) {\n  const { id } = await ctx.params;\n  const body = await req.text();\n  return proxy(`/agent-controller/threads/${encodeURIComponent(id)}`, {\n    method: 'PATCH',\n    headers: { 'content-type': 'application/json' },\n    body,\n  });\n}\n",
      "type": "registry:file",
      "target": "app/api/agent-controller/threads/[id]/route.ts"
    },
    {
      "path": "app/api/agent-controller/threads/[id]/messages/route.ts",
      "content": "import { proxy } from '@/lib/mastra-proxy';\n\n// GET /api/agent-controller/threads/:id/messages → one conversation's messages (v6\n// UIMessages, text-only restore) for hydrating the transcript on reopen.\nexport async function GET(_req: Request, ctx: { params: Promise<{ id: string }> }) {\n  const { id } = await ctx.params;\n  return proxy(`/agent-controller/threads/${encodeURIComponent(id)}/messages`);\n}\n",
      "type": "registry:file",
      "target": "app/api/agent-controller/threads/[id]/messages/route.ts"
    },
    {
      "path": "app/api/workspace/file/route.ts",
      "content": "import { proxy } from '@/lib/mastra-proxy';\n\n// GET /api/workspace/file?path=<rel> → one workspace file's text content.\nexport async function GET(req: Request) {\n  const path = new URL(req.url).searchParams.get('path') ?? '';\n  return proxy(`/workspace/file?path=${encodeURIComponent(path)}`);\n}\n",
      "type": "registry:file",
      "target": "app/api/workspace/file/route.ts"
    },
    {
      "path": "app/api/workspace/files/route.ts",
      "content": "import { proxy } from '@/lib/mastra-proxy';\n\n// GET /api/workspace/files → the controller agent's workspace file tree.\nexport async function GET() {\n  return proxy('/workspace/files');\n}\n",
      "type": "registry:file",
      "target": "app/api/workspace/files/route.ts"
    },
    {
      "path": "app/api/browser/screencast/route.ts",
      "content": "import { SERVER_URL } from '@/lib/mastra-proxy';\n\n// GET /api/browser/screencast → proxy the controller browser screencast SSE (base64\n// JPEG frames). Forwards client disconnects so the upstream screencast can stop.\nexport async function GET(req: Request) {\n  const upstream = await fetch(`${SERVER_URL}/browser/screencast`, {\n    signal: req.signal,\n    cache: 'no-store',\n  });\n  return new Response(upstream.body, {\n    status: upstream.status,\n    headers: {\n      'content-type': upstream.headers.get('content-type') ?? 'text/event-stream',\n      'cache-control': 'no-cache, no-transform',\n    },\n  });\n}\n",
      "type": "registry:file",
      "target": "app/api/browser/screencast/route.ts"
    },
    {
      "path": "app/api/images/[id]/route.ts",
      "content": "// Same-origin proxy → Mastra server's generated-image store. Returns\n// { base64, mediaType } for a generated image id; the UI feeds it to <Image>.\nconst SERVER_URL = process.env.MASTRA_SERVER_URL ?? 'http://localhost:4111';\n\nexport async function GET(_req: Request, ctx: { params: Promise<{ id: string }> }) {\n  const { id } = await ctx.params;\n  const upstream = await fetch(`${SERVER_URL}/images/${id}`);\n  return new Response(upstream.body, {\n    status: upstream.status,\n    headers: { 'content-type': upstream.headers.get('content-type') ?? 'application/json' },\n  });\n}\n",
      "type": "registry:file",
      "target": "app/api/images/[id]/route.ts"
    }
  ],
  "type": "registry:block"
}