/** * TASK-0123 — AI writing tools endpoints. * * POST /workspaces/:workspaceId/ai/writing (SSE — the suggestion) * POST /workspaces/:workspaceId/ai/writing/apply (JSON — per-block) * * Route style mirrors `ai.controller.ts` (explicit `@Inject`, `ZodValidate` * bodies, `requireUser`, `@Res()` passthrough-false for SSE — the service * owns the socket; pre-stream failures throw → global filter JSON envelope). */ import { Body, Controller, HttpCode, Inject, Param, Post, Req, Res } from "@nestjs/common"; import type { Request, Response } from "express"; import { AiWritingService } from "./ai-writing.service.js"; import { ZodValidate } from "../../common/pipes/zod-validation.pipe.js"; import { requireActor } from "../../common/types.js"; import { AiWritingDto, ApplyWritingDto, type AiWritingInput, type ApplyWritingInput } from "./ai-writing.dto.js"; @Controller() export class AiWritingController { constructor(@Inject(AiWritingService) private readonly writing: AiWritingService) {} /** * The writing suggestion (SSE). Frames: `delta` (notes only — the content * arrives structured), `tool_call` (the validated `generateBlocks` call), * `usage`, `error`, `done` (`assistantMessageId` = the SUGGESTION id). * Pre-stream: 422 (>50 blocks / no editable blocks), 413 (>100k chars), * 402 (budget), 429 (shared ai.calls cap), 403 (no EDIT). */ @Post("workspaces/:workspaceId/ai/writing") generate( @Param("workspaceId") workspaceId: string, @Body(ZodValidate(AiWritingDto)) body: AiWritingInput, @Req() req: Request, @Res() res: Response ) { return this.writing.generate(workspaceId, requireActor(req), body, req, res); } /** * Apply the (or a subset of the) suggestion's edits. 200 with per-block * results (applied / failed + code; 409 failures carry `actualVersion`). * 404 = unknown suggestion (ownership-scoped); 422 = smuggled blockIds. */ @Post("workspaces/:workspaceId/ai/writing/apply") @HttpCode(200) apply( @Param("workspaceId") workspaceId: string, @Body(ZodValidate(ApplyWritingDto)) body: ApplyWritingInput, @Req() req: Request ) { return this.writing.apply(workspaceId, requireActor(req), body, req); } }