feat(23-01): add handoff and status-update routes to chat API
- POST /conversations/:id/handoff: resolves companyId, inserts handoff message, creates issue from spec, inserts task_created message - POST /conversations/:id/status-update: inserts status_update message - Import issueService and handoffSchema; instantiate issueSvc in chatRoutes - Both routes use assertBoard for authentication
This commit is contained in:
parent
2a0c5769f3
commit
7feacb121f
1 changed files with 63 additions and 0 deletions
|
|
@ -2,15 +2,18 @@ import { Router } from "express";
|
||||||
import type { Db } from "@paperclipai/db";
|
import type { Db } from "@paperclipai/db";
|
||||||
import { assertBoard, assertCompanyAccess } from "./authz.js";
|
import { assertBoard, assertCompanyAccess } from "./authz.js";
|
||||||
import { chatService } from "../services/chat.js";
|
import { chatService } from "../services/chat.js";
|
||||||
|
import { issueService } from "../services/issues.js";
|
||||||
import {
|
import {
|
||||||
createConversationSchema,
|
createConversationSchema,
|
||||||
updateConversationSchema,
|
updateConversationSchema,
|
||||||
createMessageSchema,
|
createMessageSchema,
|
||||||
|
handoffSchema,
|
||||||
} from "@paperclipai/shared";
|
} from "@paperclipai/shared";
|
||||||
|
|
||||||
export function chatRoutes(db: Db): Router {
|
export function chatRoutes(db: Db): Router {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
const svc = chatService(db);
|
const svc = chatService(db);
|
||||||
|
const issueSvc = issueService(db);
|
||||||
|
|
||||||
// GET /api/companies/:companyId/conversations
|
// GET /api/companies/:companyId/conversations
|
||||||
router.get("/companies/:companyId/conversations", async (req, res) => {
|
router.get("/companies/:companyId/conversations", async (req, res) => {
|
||||||
|
|
@ -144,5 +147,65 @@ export function chatRoutes(db: Db): Router {
|
||||||
res.status(204).end();
|
res.status(204).end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// POST /api/conversations/:id/handoff -- Brainstormer handoff to PM: inserts handoff + task_created messages and creates an issue
|
||||||
|
router.post("/conversations/:id/handoff", async (req, res) => {
|
||||||
|
assertBoard(req);
|
||||||
|
const data = handoffSchema.parse(req.body);
|
||||||
|
|
||||||
|
// Resolve companyId from conversation
|
||||||
|
const conversation = await svc.getConversation(req.params.id!);
|
||||||
|
const companyId = conversation.companyId;
|
||||||
|
|
||||||
|
// 1. Insert handoff system message
|
||||||
|
const handoffMsg = await svc.addSystemMessage(req.params.id!, {
|
||||||
|
content: `Brainstormer \u2192 PM: spec handed off`,
|
||||||
|
messageType: "handoff",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Create issue from spec
|
||||||
|
const specDescription = [
|
||||||
|
`**What:** ${data.spec.what}`,
|
||||||
|
`**Why:** ${data.spec.why}`,
|
||||||
|
data.spec.constraints ? `**Constraints:** ${data.spec.constraints}` : "",
|
||||||
|
data.spec.success ? `**Success:** ${data.spec.success}` : "",
|
||||||
|
].filter(Boolean).join("\n\n");
|
||||||
|
|
||||||
|
const issue = await issueSvc.create(companyId, {
|
||||||
|
title: data.spec.what.slice(0, 100),
|
||||||
|
description: specDescription,
|
||||||
|
status: "backlog",
|
||||||
|
priority: "medium",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Insert task_created system message
|
||||||
|
await svc.addSystemMessage(req.params.id!, {
|
||||||
|
content: JSON.stringify({
|
||||||
|
taskId: issue.identifier,
|
||||||
|
taskTitle: issue.title,
|
||||||
|
taskUrl: `/issues/${issue.id}`,
|
||||||
|
}),
|
||||||
|
messageType: "task_created",
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ handoffMessageId: handoffMsg.id, issues: [issue] });
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST /api/conversations/:id/status-update -- Agent completion notification in chat
|
||||||
|
router.post("/conversations/:id/status-update", async (req, res) => {
|
||||||
|
assertBoard(req);
|
||||||
|
const { agentName, taskId, taskTitle, taskUrl } = req.body;
|
||||||
|
if (!agentName || !taskId) {
|
||||||
|
res.status(400).json({ error: "agentName and taskId are required" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = await svc.addSystemMessage(req.params.id!, {
|
||||||
|
content: JSON.stringify({ agentName, taskId, taskTitle, taskUrl }),
|
||||||
|
messageType: "status_update",
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(201).json(message);
|
||||||
|
});
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue