(req: NextRequest)
| 100 | }); |
| 101 | |
| 102 | export async function POST(req: NextRequest) { |
| 103 | let body: unknown; |
| 104 | try { |
| 105 | body = await req.json(); |
| 106 | } catch { |
| 107 | return NextResponse.json({ error: "invalid_json" }, { status: 400 }); |
| 108 | } |
| 109 | |
| 110 | const parsed = RequestSchema.safeParse(body); |
| 111 | if (!parsed.success) { |
| 112 | return NextResponse.json( |
| 113 | { error: "invalid_request", details: parsed.error.flatten() }, |
| 114 | { status: 400 }, |
| 115 | ); |
| 116 | } |
| 117 | |
| 118 | const { provider: providerId, model, system, messages, tool_budget, mode, workspace } = |
| 119 | parsed.data; |
| 120 | const provider = getProvider(providerId as ProviderId); |
| 121 | if (!provider) { |
| 122 | return NextResponse.json({ error: "unknown_provider" }, { status: 400 }); |
| 123 | } |
| 124 | if (!provider.isAvailable()) { |
| 125 | return NextResponse.json( |
| 126 | { error: "provider_unavailable", reason: provider.unavailableReason() }, |
| 127 | { status: 400 }, |
| 128 | ); |
| 129 | } |
| 130 | |
| 131 | // 客户端断开(关浏览器 / abort())→ 触发 AbortController, |
| 132 | // agent-loop / providers 监听后 kill subprocess、停止 fetch 流 |
| 133 | const abortController = new AbortController(); |
| 134 | // Next.js Route Handler 的 req.signal 在客户端断连时 fire abort |
| 135 | req.signal.addEventListener("abort", () => abortController.abort(), { once: true }); |
| 136 | |
| 137 | const stream = new ReadableStream({ |
| 138 | async start(controller) { |
| 139 | let closed = false; |
| 140 | const safeEnqueue = (chunk: Uint8Array) => { |
| 141 | if (closed) return; |
| 142 | try { |
| 143 | controller.enqueue(chunk); |
| 144 | } catch { |
| 145 | // controller 已关闭——忽略 |
| 146 | closed = true; |
| 147 | } |
| 148 | }; |
| 149 | // 整段逻辑跑在 workspaceContext 作用域内:root_index 预热 + runAgent 深处的所有 |
| 150 | // executeTool 都会读到 workspace,并在调 web 时带上 kb_workspace cookie。 |
| 151 | await workspaceContext.run(workspace, async () => { |
| 152 | try { |
| 153 | const baseSystem = system || DEFAULT_SYSTEM_PROMPT; |
| 154 | const indexContent = await getRootIndexContent(workspace); |
| 155 | const augmentedSystem = appendPrewarmedIndex(baseSystem, indexContent); |
| 156 | |
| 157 | for await (const evt of runAgent({ |
| 158 | provider, |
| 159 | model, |
nothing calls this directly
no test coverage detected