feat: add master-agent prompts and memory management

This commit is contained in:
kris
2026-04-01 04:10:11 +08:00
parent 9000a9f185
commit d316f0490e
31 changed files with 4398 additions and 32 deletions

View File

@@ -0,0 +1,128 @@
import { NextRequest, NextResponse } from "next/server";
import { requireRequestSession } from "@/lib/boss-auth";
import {
archiveUserMasterMemory,
updateUserMasterMemory,
type MasterMemoryScope,
type MasterMemoryType,
} from "@/lib/boss-data";
export const runtime = "nodejs";
const memoryScopes = new Set<MasterMemoryScope>(["global", "project"]);
const memoryTypes = new Set<MasterMemoryType>([
"user_preference",
"project_progress",
"decision",
"risk",
"blocking_issue",
"research_note",
"workflow_rule",
]);
export async function PATCH(
request: NextRequest,
context: { params: Promise<{ memoryId: string }> },
) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const { memoryId } = await context.params;
const rawBody = await request.text().catch(() => "");
let body: unknown;
try {
body = JSON.parse(rawBody);
} catch {
return NextResponse.json({ ok: false, message: "INVALID_JSON_PAYLOAD" }, { status: 400 });
}
if (!body || typeof body !== "object" || Array.isArray(body)) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_PAYLOAD" }, { status: 400 });
}
const payload = body as {
scope?: unknown;
projectId?: unknown;
title?: unknown;
content?: unknown;
memoryType?: unknown;
tags?: unknown;
sourceMessageId?: unknown;
lastUsedAt?: unknown;
};
const allowedKeys = new Set([
"scope",
"projectId",
"title",
"content",
"memoryType",
"tags",
"sourceMessageId",
"lastUsedAt",
]);
if (Object.keys(payload).some((key) => !allowedKeys.has(key))) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_PAYLOAD" }, { status: 400 });
}
if (payload.scope !== undefined && !memoryScopes.has(payload.scope as MasterMemoryScope)) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_SCOPE" }, { status: 400 });
}
if (payload.memoryType !== undefined && !memoryTypes.has(payload.memoryType as MasterMemoryType)) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_TYPE" }, { status: 400 });
}
if (payload.tags !== undefined && !Array.isArray(payload.tags)) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_PAYLOAD" }, { status: 400 });
}
try {
const memory = await updateUserMasterMemory(memoryId, session.account, {
...(payload.scope !== undefined ? { scope: payload.scope as MasterMemoryScope } : {}),
...(payload.projectId !== undefined ? { projectId: typeof payload.projectId === "string" ? payload.projectId : "" } : {}),
...(payload.title !== undefined ? { title: typeof payload.title === "string" ? payload.title : "" } : {}),
...(payload.content !== undefined ? { content: typeof payload.content === "string" ? payload.content : "" } : {}),
...(payload.memoryType !== undefined ? { memoryType: payload.memoryType as MasterMemoryType } : {}),
...(payload.tags !== undefined ? { tags: payload.tags as string[] } : {}),
...(payload.sourceMessageId !== undefined
? { sourceMessageId: typeof payload.sourceMessageId === "string" ? payload.sourceMessageId : "" }
: {}),
...(payload.lastUsedAt !== undefined
? { lastUsedAt: typeof payload.lastUsedAt === "string" ? payload.lastUsedAt : "" }
: {}),
});
return NextResponse.json({ ok: true, memory });
} catch (error) {
const message = error instanceof Error ? error.message : "UNKNOWN_ERROR";
return NextResponse.json(
{
ok: false,
message,
},
{ status: message === "USER_MASTER_MEMORY_NOT_FOUND" ? 404 : 400 },
);
}
}
export async function DELETE(
request: NextRequest,
context: { params: Promise<{ memoryId: string }> },
) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const { memoryId } = await context.params;
try {
const memory = await archiveUserMasterMemory(memoryId, session.account);
return NextResponse.json({ ok: true, memory });
} catch (error) {
const message = error instanceof Error ? error.message : "UNKNOWN_ERROR";
return NextResponse.json(
{
ok: false,
message,
},
{ status: message === "USER_MASTER_MEMORY_NOT_FOUND" ? 404 : 400 },
);
}
}

View File

@@ -0,0 +1,133 @@
import { NextRequest, NextResponse } from "next/server";
import { requireRequestSession } from "@/lib/boss-auth";
import {
createUserMasterMemory,
listUserMasterMemories,
type MasterMemoryScope,
type MasterMemoryType,
} from "@/lib/boss-data";
export const runtime = "nodejs";
const memoryScopes = new Set<MasterMemoryScope>(["global", "project"]);
const memoryTypes = new Set<MasterMemoryType>([
"user_preference",
"project_progress",
"decision",
"risk",
"blocking_issue",
"research_note",
"workflow_rule",
]);
function parseBoolean(value: string | null) {
return value === "1" || value === "true";
}
export async function GET(request: NextRequest) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const { searchParams } = new URL(request.url);
const includeArchived = parseBoolean(searchParams.get("includeArchived"));
const scope = searchParams.get("scope") as MasterMemoryScope | null;
const projectId = searchParams.get("projectId")?.trim() || undefined;
if (scope && !memoryScopes.has(scope)) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_SCOPE" }, { status: 400 });
}
const memories = await listUserMasterMemories(session.account, {
includeArchived,
...(scope ? { scope } : {}),
...(projectId ? { projectId } : {}),
});
return NextResponse.json({ ok: true, memories });
}
export async function POST(request: NextRequest) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const rawBody = await request.text().catch(() => "");
let body: unknown;
try {
body = JSON.parse(rawBody);
} catch {
return NextResponse.json({ ok: false, message: "INVALID_JSON_PAYLOAD" }, { status: 400 });
}
if (!body || typeof body !== "object" || Array.isArray(body)) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_PAYLOAD" }, { status: 400 });
}
const payload = body as {
scope?: unknown;
projectId?: unknown;
title?: unknown;
content?: unknown;
memoryType?: unknown;
tags?: unknown;
sourceMessageId?: unknown;
};
const allowedKeys = new Set([
"scope",
"projectId",
"title",
"content",
"memoryType",
"tags",
"sourceMessageId",
]);
if (Object.keys(payload).some((key) => !allowedKeys.has(key))) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_PAYLOAD" }, { status: 400 });
}
if (!memoryScopes.has(payload.scope as MasterMemoryScope)) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_SCOPE" }, { status: 400 });
}
if (typeof payload.title !== "string" || typeof payload.content !== "string") {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_PAYLOAD" }, { status: 400 });
}
if (!memoryTypes.has(payload.memoryType as MasterMemoryType)) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_TYPE" }, { status: 400 });
}
if (payload.scope === "project" && (typeof payload.projectId !== "string" || !payload.projectId.trim())) {
return NextResponse.json({ ok: false, message: "USER_MASTER_MEMORY_PROJECT_ID_REQUIRED" }, { status: 400 });
}
if (payload.tags !== undefined && !Array.isArray(payload.tags)) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_PAYLOAD" }, { status: 400 });
}
if (
payload.sourceMessageId !== undefined &&
payload.sourceMessageId !== null &&
typeof payload.sourceMessageId !== "string"
) {
return NextResponse.json({ ok: false, message: "INVALID_MEMORY_PAYLOAD" }, { status: 400 });
}
try {
const memory = await createUserMasterMemory({
account: session.account,
scope: payload.scope as MasterMemoryScope,
projectId: typeof payload.projectId === "string" ? payload.projectId : undefined,
title: payload.title,
content: payload.content,
memoryType: payload.memoryType as MasterMemoryType,
tags: (payload.tags as string[] | undefined) ?? [],
sourceMessageId:
typeof payload.sourceMessageId === "string" ? payload.sourceMessageId : undefined,
});
return NextResponse.json({ ok: true, memory });
} catch (error) {
return NextResponse.json(
{
ok: false,
message: error instanceof Error ? error.message : "UNKNOWN_ERROR",
},
{ status: 400 },
);
}
}

View File

@@ -0,0 +1,67 @@
import { NextRequest, NextResponse } from "next/server";
import { requireRequestSession } from "@/lib/boss-auth";
import {
getMasterAgentPromptPolicy,
updateMasterAgentPromptPolicy,
} from "@/lib/boss-data";
export const runtime = "nodejs";
export async function GET(request: NextRequest) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const policy = await getMasterAgentPromptPolicy();
return NextResponse.json({ ok: true, policy });
}
export async function POST(request: NextRequest) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
if (session.role !== "highest_admin") {
return NextResponse.json({ ok: false, message: "FORBIDDEN" }, { status: 403 });
}
const rawBody = await request.text().catch(() => "");
let body: unknown;
try {
body = JSON.parse(rawBody);
} catch {
return NextResponse.json({ ok: false, message: "INVALID_JSON_PAYLOAD" }, { status: 400 });
}
if (!body || typeof body !== "object" || Array.isArray(body)) {
return NextResponse.json({ ok: false, message: "INVALID_PROMPT_POLICY_PAYLOAD" }, { status: 400 });
}
const payload = body as {
globalPrompt?: unknown;
};
const allowedKeys = new Set(["globalPrompt"]);
if (Object.keys(payload).some((key) => !allowedKeys.has(key))) {
return NextResponse.json({ ok: false, message: "INVALID_PROMPT_POLICY_PAYLOAD" }, { status: 400 });
}
if (typeof payload.globalPrompt !== "string") {
return NextResponse.json({ ok: false, message: "INVALID_PROMPT_POLICY_PAYLOAD" }, { status: 400 });
}
try {
const policy = await updateMasterAgentPromptPolicy({
globalPrompt: payload.globalPrompt,
updatedBy: session.account,
});
return NextResponse.json({ ok: true, policy });
} catch (error) {
return NextResponse.json(
{
ok: false,
message: error instanceof Error ? error.message : "UNKNOWN_ERROR",
},
{ status: 400 },
);
}
}

View File

@@ -0,0 +1,77 @@
import { NextRequest, NextResponse } from "next/server";
import { requireRequestSession } from "@/lib/boss-auth";
import {
clearUserMasterPrompt,
getUserMasterPrompt,
updateUserMasterPrompt,
} from "@/lib/boss-data";
export const runtime = "nodejs";
export async function GET(request: NextRequest) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const prompt = await getUserMasterPrompt(session.account);
return NextResponse.json({ ok: true, prompt });
}
export async function POST(request: NextRequest) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const rawBody = await request.text().catch(() => "");
let body: unknown;
try {
body = JSON.parse(rawBody);
} catch {
return NextResponse.json({ ok: false, message: "INVALID_JSON_PAYLOAD" }, { status: 400 });
}
if (!body || typeof body !== "object" || Array.isArray(body)) {
return NextResponse.json({ ok: false, message: "INVALID_USER_PROMPT_PAYLOAD" }, { status: 400 });
}
const payload = body as {
content?: unknown;
};
const allowedKeys = new Set(["content"]);
if (Object.keys(payload).some((key) => !allowedKeys.has(key))) {
return NextResponse.json({ ok: false, message: "INVALID_USER_PROMPT_PAYLOAD" }, { status: 400 });
}
if (payload.content !== undefined && payload.content !== null && typeof payload.content !== "string") {
return NextResponse.json({ ok: false, message: "INVALID_USER_PROMPT_PAYLOAD" }, { status: 400 });
}
try {
const content = typeof payload.content === "string" ? payload.content : "";
if (!content.trim()) {
const result = await clearUserMasterPrompt(session.account);
return NextResponse.json({ ok: true, prompt: null, ...result });
}
const prompt = await updateUserMasterPrompt(session.account, content);
return NextResponse.json({ ok: true, prompt });
} catch (error) {
return NextResponse.json(
{
ok: false,
message: error instanceof Error ? error.message : "UNKNOWN_ERROR",
},
{ status: 400 },
);
}
}
export async function DELETE(request: NextRequest) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const result = await clearUserMasterPrompt(session.account);
return NextResponse.json({ ok: true, ...result });
}