Chatverlauf (LotseConversation/-Message) und Aktionskarten (LotseActionProposal) als Mandantentabellen mit RLS; Schalter lotseChatEnabled. Tool-Use-Schleife mit Runden- und Tokengrenze, Datenminimierung per Platzhalter, Auftragszuordnung im Sichtbarkeits-Scope, Bestätigen/Verwerfen/Alle bestätigen mit Hash, Ablauf und Idempotenz, Transkriptions-API. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
28 lines
1.5 KiB
TypeScript
28 lines
1.5 KiB
TypeScript
import { assertCan, can, ServiceError, type ServiceCtx } from "@/server/services/context";
|
|
import { isLotseEnabled } from "../settings";
|
|
|
|
/**
|
|
* Gate of the Lotse chat for technicians (lane L16): `lotse:use` + `field:execute`, Lotse module on
|
|
* (TenantModule `lotse`) and the tenant switch „Lotse-Chat für Monteure" (TenantSettings.lotseChatEnabled,
|
|
* default on). Switched off → `blocked` (reason disabled / chat_disabled).
|
|
*/
|
|
|
|
export async function isLotseChatEnabled(ctx: Pick<ServiceCtx, "db" | "tenantId">): Promise<boolean> {
|
|
if (!(await isLotseEnabled(ctx))) return false;
|
|
const s = await ctx.db.tenantSettings.findFirst({ select: { lotseChatEnabled: true } });
|
|
return s?.lotseChatEnabled ?? true;
|
|
}
|
|
|
|
/** UI convenience: may the user open the chat at all (permissions + switches)? Never a security check. */
|
|
export async function canUseLotseChat(ctx: ServiceCtx): Promise<boolean> {
|
|
return can(ctx, "lotse:use") && can(ctx, "field:execute") && (await isLotseChatEnabled(ctx));
|
|
}
|
|
|
|
export async function assertLotseChatUsable(ctx: ServiceCtx): Promise<void> {
|
|
assertCan(ctx, "lotse:use");
|
|
assertCan(ctx, "field:execute");
|
|
if (!(await isLotseEnabled(ctx))) throw new ServiceError("blocked", "lotse disabled for tenant", { reason: "disabled" });
|
|
const s = await ctx.db.tenantSettings.findFirst({ select: { lotseChatEnabled: true } });
|
|
if (s && !s.lotseChatEnabled) throw new ServiceError("blocked", "lotse chat disabled for tenant", { reason: "chat_disabled" });
|
|
}
|