feat: 管理面板补齐(RAG导入/记忆添加/会话接history/诚实表述) + design/16 §4.6 IM多会话隔离+第三方身份+黑白名单
agent-98 面板批次:
- 会话管理面板改接 history.ListSessions(主web+次级) + 新建次级会话按钮
- 记忆面板手动添加入口 POST /admin/memories(owner)
- 知识库真正实现 IndexDocument(分块+落projectindex) + POST /admin/rag 导入端点
- 三面板空态诚实表述(去除'后端M10+提供')
- RAG嵌入降级到keyword/line(无直属embedder,不伪造语义,符合红线)
agent-99 design/16 §4.6:
- model.Message.SenderName + 迁移0006 sender_name 列
- 第三方身份边界: session.ResolveActor判定(非LLM猜) + [第三方·昵称]前缀(不改源content)
- 群聊/陌生人落独立sess-xxx(persistIsolated),不写主会话,不广播
- 黑/白名单(allowGroups/denyGroups/allowUsers/denyUsers)最前拦截,不进LLM
- 多账号 CHANNELS_JSON 配置 + 每平台多实例
- 管理只读 GET /admin/channels/{platform}/messages(该平台非主人消息流)
实机验证(JWT): 会话管理返回主会话web / 记忆添加成功 / RAG导入documents非空+Cyrene命中; 后端build/vet/test全绿 + 前端build绿
This commit is contained in:
1 parent
37e98ec584
commit
9c2a37c4ac
32 files changed
+1768
-136
No files matched your search
@@ -21,12 +21,15 @@ import type {
|
||||
LlmTestRequest,
|
||||
LlmTestResult,
|
||||
LoginResult,
|
||||
MemoryAddRequest,
|
||||
MemoryAddResult,
|
||||
MemoryRecord,
|
||||
MemorySearchRequest,
|
||||
MemorySearchResult,
|
||||
Mode,
|
||||
PersonaRecord,
|
||||
PersonaUpsertRequest,
|
||||
RagIndexRequest,
|
||||
Role,
|
||||
SessionMeta,
|
||||
SessionRecord,
|
||||
@@ -205,6 +208,8 @@ export interface MessageDto {
|
||||
platform?: string;
|
||||
/** Concrete client/device that produced the message (design/16 §4.5). */
|
||||
deviceId?: string;
|
||||
/** Sender display name of a third-party message (design/16 §4.6). Blank for the owner. */
|
||||
senderName?: string;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
@@ -221,6 +226,7 @@ function toChatMessage(m: MessageDto): ChatMessage {
|
||||
thinking: m.thinking,
|
||||
platform: m.platform,
|
||||
deviceId: m.deviceId,
|
||||
senderName: m.senderName,
|
||||
createdAt: m.createdAt,
|
||||
};
|
||||
}
|
||||
@@ -295,9 +301,11 @@ export function isNotImplemented(err: unknown): boolean {
|
||||
export function adminListPersonas(): Promise<PersonaRecord[]> {
|
||||
return request<PersonaRecord[]>("/admin/personas");
|
||||
}
|
||||
/** GET /api/v1/admin/sessions */
|
||||
export function adminListSessions(): Promise<SessionRecord[]> {
|
||||
return request<SessionRecord[]>("/admin/sessions");
|
||||
/** GET /api/v1/admin/sessions — the persistent main + secondary sessions (design/16).
|
||||
* The main session (fixed id) is always present, so the panel is never empty in a
|
||||
* history-wired deployment. */
|
||||
export function adminListSessions(): Promise<SessionMeta[]> {
|
||||
return request<SessionMeta[]>("/admin/sessions");
|
||||
}
|
||||
/** GET /api/v1/admin/memories */
|
||||
export function adminListMemories(): Promise<MemoryRecord[]> {
|
||||
@@ -364,6 +372,14 @@ export function adminDeleteRag(id: string): Promise<{ id: string }> {
|
||||
body: JSON.stringify({ id }),
|
||||
});
|
||||
}
|
||||
/** POST /api/v1/admin/rag — imports one knowledge document (chunk + index), so it
|
||||
* becomes keyword/searchable in the project index. */
|
||||
export function adminIndexRag(req: RagIndexRequest): Promise<{ name: string }> {
|
||||
return request<{ name: string }>("/admin/rag", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(req),
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Admin write endpoints (owner-only, audited; real store or honest 501). --- */
|
||||
|
||||
@@ -491,6 +507,13 @@ export function adminPluginUninstall(req: { name: string }): Promise<{ name: str
|
||||
export function adminMemorySearch(req: MemorySearchRequest): Promise<MemorySearchResult> {
|
||||
return request<MemorySearchResult>("/admin/memories/search", { method: "POST", body: JSON.stringify(req) });
|
||||
}
|
||||
/** POST /api/v1/admin/memories — writes one memory item (manual "添加记忆"). */
|
||||
export function adminAddMemory(req: MemoryAddRequest): Promise<MemoryAddResult> {
|
||||
return request<MemoryAddResult>("/admin/memories", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(req),
|
||||
});
|
||||
}
|
||||
/** GET /api/v1/admin/sessions/{id}/export — exports a session as JSON. */
|
||||
export function adminExportSession(id: string): Promise<{ session: SessionRecord }> {
|
||||
return request<{ session: SessionRecord }>(`/admin/sessions/${encodeURIComponent(id)}/export`);
|
||||
|
||||
@@ -88,6 +88,10 @@ export interface ChatMessage {
|
||||
* defaults to labeling the bubble source when this is present; absent on
|
||||
* legacy/client-optimistic messages kept without it. */
|
||||
deviceId?: string;
|
||||
/** Human-readable sender display name of a third-party message (group/stranger,
|
||||
* design/16 §4.6). Blank for the owner's private chat, so present only when a
|
||||
* non-owner is speaking; the recipient renders it as the sender label. */
|
||||
senderName?: string;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
@@ -494,3 +498,25 @@ export interface MemorySearchResult {
|
||||
semantic: boolean;
|
||||
hits: MemorySearchHit[];
|
||||
}
|
||||
|
||||
/** POST /api/v1/admin/memories body — the manual "添加记忆" write. Scope is
|
||||
* optional and defaults to persona; it must be one of user|persona|project. */
|
||||
export interface MemoryAddRequest {
|
||||
personaId?: string;
|
||||
content: string;
|
||||
scope?: "user" | "persona" | "project";
|
||||
}
|
||||
|
||||
/** POST /api/v1/admin/memories result (the stored item's identity + text). */
|
||||
export interface MemoryAddResult {
|
||||
id: string;
|
||||
scope: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
/** POST /api/v1/admin/rag body — the admin "导入文档" write. */
|
||||
export interface RagIndexRequest {
|
||||
name: string;
|
||||
desc?: string;
|
||||
content: string;
|
||||
}
|
||||
@@ -176,6 +176,7 @@ function parsePersistedMessage(v: unknown): ChatMessage | null {
|
||||
image: typeof r.image === "string" ? r.image : undefined,
|
||||
platform: typeof r.platform === "string" && r.platform ? r.platform : undefined,
|
||||
deviceId: typeof r.deviceId === "string" && r.deviceId ? r.deviceId : undefined,
|
||||
senderName: typeof r.senderName === "string" && r.senderName ? r.senderName : undefined,
|
||||
createdAt: typeof r.createdAt === "number" ? r.createdAt : now(),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user