Update chat events/transcription flow and container startup fixes
This commit is contained in:
@@ -5,7 +5,6 @@ import { clearAuthSession, setSession } from "../utils/auth";
|
||||
import { prisma } from "../utils/prisma";
|
||||
import { normalizePhone, verifyPassword } from "../utils/password";
|
||||
import { persistChatMessage, runCrmAgentFor } from "../agent/crmAgent";
|
||||
import type { AgentTraceEvent } from "../agent/crmAgent";
|
||||
import { buildChangeSet, captureSnapshot, rollbackChangeSet } from "../utils/changeSet";
|
||||
import type { ChangeSet } from "../utils/changeSet";
|
||||
|
||||
@@ -210,6 +209,55 @@ async function selectChatConversation(auth: AuthContext | null, event: H3Event,
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
async function archiveChatConversation(auth: AuthContext | null, event: H3Event, id: string) {
|
||||
const ctx = requireAuth(auth);
|
||||
const convId = (id ?? "").trim();
|
||||
if (!convId) throw new Error("id is required");
|
||||
|
||||
const conversation = await prisma.chatConversation.findFirst({
|
||||
where: {
|
||||
id: convId,
|
||||
teamId: ctx.teamId,
|
||||
createdByUserId: ctx.userId,
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!conversation) throw new Error("conversation not found");
|
||||
|
||||
const nextConversationId = await prisma.$transaction(async (tx) => {
|
||||
await tx.chatConversation.delete({ where: { id: conversation.id } });
|
||||
|
||||
if (ctx.conversationId !== conversation.id) {
|
||||
return ctx.conversationId;
|
||||
}
|
||||
|
||||
const fallback = await tx.chatConversation.findFirst({
|
||||
where: { teamId: ctx.teamId, createdByUserId: ctx.userId },
|
||||
orderBy: { updatedAt: "desc" },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (fallback) {
|
||||
return fallback.id;
|
||||
}
|
||||
|
||||
const created = await tx.chatConversation.create({
|
||||
data: { teamId: ctx.teamId, createdByUserId: ctx.userId, title: "Pilot" },
|
||||
select: { id: true },
|
||||
});
|
||||
return created.id;
|
||||
});
|
||||
|
||||
setSession(event, {
|
||||
teamId: ctx.teamId,
|
||||
userId: ctx.userId,
|
||||
conversationId: nextConversationId,
|
||||
});
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
async function getChatMessages(auth: AuthContext | null) {
|
||||
const ctx = requireAuth(auth);
|
||||
const items = await prisma.chatMessage.findMany({
|
||||
@@ -219,25 +267,18 @@ async function getChatMessages(auth: AuthContext | null) {
|
||||
});
|
||||
|
||||
return items.map((m) => {
|
||||
const debug = (m.planJson as any) ?? {};
|
||||
const cs = getChangeSetFromPlanJson(m.planJson);
|
||||
return {
|
||||
id: m.id,
|
||||
role: m.role === "USER" ? "user" : m.role === "ASSISTANT" ? "assistant" : "system",
|
||||
text: m.text,
|
||||
thinking: Array.isArray(debug.thinking) ? (debug.thinking as string[]) : [],
|
||||
tools: Array.isArray(debug.tools) ? (debug.tools as string[]) : [],
|
||||
toolRuns: Array.isArray(debug.toolRuns)
|
||||
? (debug.toolRuns as any[])
|
||||
.filter((t) => t && typeof t === "object")
|
||||
.map((t: any) => ({
|
||||
name: String(t.name ?? "crm:unknown"),
|
||||
status: t.status === "error" ? "error" : "ok",
|
||||
input: String(t.input ?? ""),
|
||||
output: String(t.output ?? ""),
|
||||
at: t.at ? String(t.at) : m.createdAt.toISOString(),
|
||||
}))
|
||||
: [],
|
||||
requestId: null,
|
||||
eventType: null,
|
||||
phase: null,
|
||||
transient: null,
|
||||
thinking: [],
|
||||
tools: [],
|
||||
toolRuns: [],
|
||||
changeSetId: cs?.id ?? null,
|
||||
changeStatus: cs?.status ?? null,
|
||||
changeSummary: cs?.summary ?? null,
|
||||
@@ -292,7 +333,10 @@ async function getDashboard(auth: AuthContext | null) {
|
||||
}),
|
||||
prisma.deal.findMany({
|
||||
where: { teamId: ctx.teamId },
|
||||
include: { contact: { select: { name: true, company: true } } },
|
||||
include: {
|
||||
contact: { select: { name: true, company: true } },
|
||||
steps: { orderBy: [{ order: "asc" }, { createdAt: "asc" }] },
|
||||
},
|
||||
orderBy: { updatedAt: "desc" },
|
||||
take: 500,
|
||||
}),
|
||||
@@ -366,6 +410,16 @@ async function getDashboard(auth: AuthContext | null) {
|
||||
amount: d.amount ? String(d.amount) : "",
|
||||
nextStep: d.nextStep ?? "",
|
||||
summary: d.summary ?? "",
|
||||
currentStepId: d.currentStepId ?? "",
|
||||
steps: d.steps.map((step) => ({
|
||||
id: step.id,
|
||||
title: step.title,
|
||||
description: step.description ?? "",
|
||||
status: step.status,
|
||||
dueAt: step.dueAt?.toISOString() ?? "",
|
||||
order: step.order,
|
||||
completedAt: step.completedAt?.toISOString() ?? "",
|
||||
})),
|
||||
}));
|
||||
|
||||
const feed = feedRaw.map((c) => ({
|
||||
@@ -596,6 +650,7 @@ async function sendPilotMessage(auth: AuthContext | null, textInput: string) {
|
||||
const ctx = requireAuth(auth);
|
||||
const text = (textInput ?? "").trim();
|
||||
if (!text) throw new Error("text is required");
|
||||
const requestId = `req_${Date.now()}_${Math.floor(Math.random() * 1_000_000)}`;
|
||||
|
||||
const snapshotBefore = await captureSnapshot(prisma, ctx.teamId);
|
||||
|
||||
@@ -605,24 +660,19 @@ async function sendPilotMessage(auth: AuthContext | null, textInput: string) {
|
||||
authorUserId: ctx.userId,
|
||||
role: "USER",
|
||||
text,
|
||||
requestId,
|
||||
eventType: "user",
|
||||
phase: "final",
|
||||
transient: false,
|
||||
});
|
||||
|
||||
const reply = await runCrmAgentFor({
|
||||
teamId: ctx.teamId,
|
||||
userId: ctx.userId,
|
||||
userText: text,
|
||||
onTrace: async (event: AgentTraceEvent) => {
|
||||
await persistChatMessage({
|
||||
teamId: ctx.teamId,
|
||||
conversationId: ctx.conversationId,
|
||||
authorUserId: null,
|
||||
role: "SYSTEM",
|
||||
text: event.text,
|
||||
thinking: [],
|
||||
tools: event.toolRun ? [event.toolRun.name] : [],
|
||||
toolRuns: event.toolRun ? [event.toolRun] : [],
|
||||
});
|
||||
},
|
||||
requestId,
|
||||
conversationId: ctx.conversationId,
|
||||
onTrace: async () => {},
|
||||
});
|
||||
|
||||
const snapshotAfter = await captureSnapshot(prisma, ctx.teamId);
|
||||
@@ -634,9 +684,10 @@ async function sendPilotMessage(auth: AuthContext | null, textInput: string) {
|
||||
authorUserId: null,
|
||||
role: "ASSISTANT",
|
||||
text: reply.text,
|
||||
thinking: reply.thinking ?? [],
|
||||
tools: reply.tools,
|
||||
toolRuns: reply.toolRuns ?? [],
|
||||
requestId,
|
||||
eventType: "assistant",
|
||||
phase: "final",
|
||||
transient: false,
|
||||
changeSet,
|
||||
});
|
||||
|
||||
@@ -654,9 +705,6 @@ async function logPilotNote(auth: AuthContext | null, textInput: string) {
|
||||
authorUserId: null,
|
||||
role: "ASSISTANT",
|
||||
text,
|
||||
thinking: [],
|
||||
tools: [],
|
||||
toolRuns: [],
|
||||
});
|
||||
|
||||
return { ok: true };
|
||||
@@ -675,6 +723,7 @@ export const crmGraphqlSchema = buildSchema(`
|
||||
logout: MutationResult!
|
||||
createChatConversation(title: String): Conversation!
|
||||
selectChatConversation(id: ID!): MutationResult!
|
||||
archiveChatConversation(id: ID!): MutationResult!
|
||||
sendPilotMessage(text: String!): MutationResult!
|
||||
confirmLatestChangeSet: MutationResult!
|
||||
rollbackLatestChangeSet: MutationResult!
|
||||
@@ -743,6 +792,10 @@ export const crmGraphqlSchema = buildSchema(`
|
||||
id: ID!
|
||||
role: String!
|
||||
text: String!
|
||||
requestId: String
|
||||
eventType: String
|
||||
phase: String
|
||||
transient: Boolean
|
||||
thinking: [String!]!
|
||||
tools: [String!]!
|
||||
toolRuns: [PilotToolRun!]!
|
||||
@@ -822,6 +875,18 @@ export const crmGraphqlSchema = buildSchema(`
|
||||
amount: String!
|
||||
nextStep: String!
|
||||
summary: String!
|
||||
currentStepId: String!
|
||||
steps: [DealStep!]!
|
||||
}
|
||||
|
||||
type DealStep {
|
||||
id: ID!
|
||||
title: String!
|
||||
description: String!
|
||||
status: String!
|
||||
dueAt: String!
|
||||
order: Int!
|
||||
completedAt: String!
|
||||
}
|
||||
|
||||
type FeedCard {
|
||||
@@ -878,6 +943,9 @@ export const crmGraphqlRoot = {
|
||||
selectChatConversation: async (args: { id: string }, context: GraphQLContext) =>
|
||||
selectChatConversation(context.auth, context.event, args.id),
|
||||
|
||||
archiveChatConversation: async (args: { id: string }, context: GraphQLContext) =>
|
||||
archiveChatConversation(context.auth, context.event, args.id),
|
||||
|
||||
sendPilotMessage: async (args: { text: string }, context: GraphQLContext) =>
|
||||
sendPilotMessage(context.auth, args.text),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user