feat: IM平台列 + deviceID标识 + 会话持久化完善
- agent-96: chat_messages 加 platform 列(迁移0004) + IM主人私聊落主会话web(带platform) + 前端按平台过滤契约 + message_persisted 带 platform(复用 BroadcastPersisted) - agent-97: chat_messages 加 device_id 列(迁移0005) + history.Message.DeviceID + Web发消息带deviceId(localStorage getClientId) + IM persistToMain 带 deviceID + 前端气泡展示 SourceLabel + 修主会话基础2个bug(fn scanMessage语法/DDL缺列) - design/16 §4.4(平台) + §4.5(deviceID) 已入档 - 实机: POST /chat 带 deviceId 读回 user/assistant 均带 deviceId+platform; 后端 build/vet/test 绿 + 前端 build 绿 - 红线: 身份靠 actor 元数据; 禁止 shell 读 .env(JWT冒烟由主会话跑)
This commit is contained in:
1 parent
1bf60c1ae4
commit
37e98ec584
26 files changed
+828
-116
No files matched your search
@@ -31,6 +31,7 @@ import type {
|
||||
SessionMeta,
|
||||
SessionRecord,
|
||||
} from "./types";
|
||||
import { getClientId } from "./client";
|
||||
|
||||
/**
|
||||
* REST client for the Cyrene v2 gateway. The gateway wraps every success in
|
||||
@@ -160,21 +161,24 @@ export function health(): Promise<{ status: string }> {
|
||||
return request<{ status: string }>("/health");
|
||||
}
|
||||
|
||||
/** POST /api/v1/chat — one synchronous agent turn. */
|
||||
/** POST /api/v1/chat — one synchronous agent turn. The body carries the sender's
|
||||
* persistent per-browser deviceId (design/16 §4.5) so the persisted user and
|
||||
* assistant messages are tagged with the concrete client. */
|
||||
export function chatSync(
|
||||
sessionId: string,
|
||||
text: string
|
||||
): Promise<ChatSyncResult> {
|
||||
return request<ChatSyncResult>("/chat", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ sessionId, text }),
|
||||
body: JSON.stringify({ sessionId, text, deviceId: getClientId() }),
|
||||
});
|
||||
}
|
||||
|
||||
/** POST /api/v1/chat with stream:true — triggers streaming over the WS channel.
|
||||
* `image` (a base64 data: URI or URL) is attached for the vision path; the
|
||||
* gateway's chat body currently has no image field, so an unconfigured backend
|
||||
* simply ignores it. */
|
||||
* simply ignores it. `deviceId` mirrors the client's per-browser id onto the
|
||||
* persisted message (design/16 §4.5). */
|
||||
export function chatStream(
|
||||
sessionId: string,
|
||||
text: string,
|
||||
@@ -182,7 +186,7 @@ export function chatStream(
|
||||
): Promise<{ stream: true }> {
|
||||
return request<{ stream: true }>("/chat", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ sessionId, text, image, stream: true }),
|
||||
body: JSON.stringify({ sessionId, text, image, stream: true, deviceId: getClientId() }),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -197,6 +201,10 @@ export interface MessageDto {
|
||||
mood?: string;
|
||||
image?: string;
|
||||
thinking?: string;
|
||||
/** Source-platform tag (design/16 §4.4): "web" or an IM channel platform. */
|
||||
platform?: string;
|
||||
/** Concrete client/device that produced the message (design/16 §4.5). */
|
||||
deviceId?: string;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
@@ -211,6 +219,8 @@ function toChatMessage(m: MessageDto): ChatMessage {
|
||||
mood: m.mood,
|
||||
image: m.image,
|
||||
thinking: m.thinking,
|
||||
platform: m.platform,
|
||||
deviceId: m.deviceId,
|
||||
createdAt: m.createdAt,
|
||||
};
|
||||
}
|
||||
@@ -228,18 +238,23 @@ export function createSubSession(title?: string): Promise<SessionMeta> {
|
||||
});
|
||||
}
|
||||
|
||||
/** GET /api/v1/sessions/{id}/messages?before=&limit= — hot-paginated history.
|
||||
* The wire returns newest-first, but callers render chronologically (oldest
|
||||
* first), so we reverse here: page 1 is the most recent `limit` messages in
|
||||
* display order, and `before` (the oldest message's createdAt) walks upward. */
|
||||
/** GET /api/v1/sessions/{id}/messages?before=&limit=&platform= — hot-paginated
|
||||
* history, optionally filtered to one source platform (design/16 §4.4). The wire
|
||||
* returns newest-first, but callers render chronologically (oldest first), so we
|
||||
* reverse here: page 1 is the most recent `limit` messages in display order, and
|
||||
* `before` (the oldest message's createdAt) walks upward. The Web client omits
|
||||
* `platform` for the full shared main session; an IM adapter passes its own
|
||||
* platform to see only its lane. */
|
||||
export async function fetchMessages(
|
||||
sessionId: string,
|
||||
before?: number,
|
||||
limit = 50
|
||||
limit = 50,
|
||||
platform?: string
|
||||
): Promise<ChatMessage[]> {
|
||||
const qs = new URLSearchParams();
|
||||
qs.set("limit", String(limit));
|
||||
if (before !== undefined) qs.set("before", String(before));
|
||||
if (platform) qs.set("platform", platform);
|
||||
const list = await request<MessageDto[]>(
|
||||
`/sessions/${encodeURIComponent(sessionId)}/messages?${qs.toString()}`
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user