feat(channels): 适配器日志接 WS 实时推送(落库即秒推, channel_persisted)
后端: persistIsolated 由 _ = full(不广播)改为 notifyPersisted(full); BroadcastPersisted 按
sessionId 分流——main/web:<uuid> → message_persisted(Web聊天同步), 隔离 sess-xxx → channel_persisted
(适配器日志专用, 永不写主会话 web, 红线). payload data:{message:historyMessage}(strip thinking).
前端: ws.ts 新增 channel_persisted case→parseAdminChannelMessage→pushChannelEvent; wsParse.ts 纯函数;
useAppStore 新增 channelEvents(最新在前,按id去重,cap200,不持久化); channelLogList 订阅过滤
(单adapter按adapterId/总览取adapterId非空)merge进列表(仅追加、id去重、不整页重载),保留拉历史+10s兜底.
Docs/api-client.md §3.4 补 channel_persisted 契约.
验收: 后端 build/vet/test 全绿+windows/darwin交叉编译绿; 前端 npm run build 99模块绿.
注: 偏离=web:<uuid> 归 message_persisted(保Web副会话同步), 隔离永不带web前缀红线仍守.
This commit is contained in:
1 parent
9e7b4ebaac
commit
3386679d45
10 files changed
+180
-23
No files matched your search
@@ -79,8 +79,9 @@ type SessionStore interface {
|
||||
// (design/16 §4.4): when a history store is wired, owner-private messages are
|
||||
// routed into the main web session and tagged with the channel platform, and every
|
||||
// persisted message is handed to OnPersisted so the gateway can broadcast a
|
||||
// `message_persisted` event; when both are absent the bridge runs exactly as
|
||||
// before (isolated sess-xxx, no chat_messages writes).
|
||||
// `message_persisted` (web chat) or `channel_persisted` (IM adapter log) event;
|
||||
// when both are absent the bridge runs exactly as before (isolated sess-xxx, no
|
||||
// chat_messages writes).
|
||||
type Options struct {
|
||||
Log *slog.Logger
|
||||
Loop Chatter
|
||||
@@ -572,7 +573,8 @@ func (b *Bridge) processFrom(ctx context.Context, msg adapter.UnifiedMessage, a
|
||||
// Persist the inbound user message before the turn so it is durable even if the
|
||||
// channel reply fails. Owner-private messages land in the main web session and
|
||||
// are broadcast to the Web client; restricted messages land in their isolated
|
||||
// session (the adapter log page) and are NOT broadcast to the main session.
|
||||
// session (the adapter log page) and are broadcast as a `channel_persisted` event
|
||||
// (never into the main/`web` session or the web chat).
|
||||
if isOwnerPrivate {
|
||||
b.persistToMain(ctx, history.RoleUser, text, platform, deviceIDFor(msg), adapterName(a))
|
||||
} else {
|
||||
@@ -591,8 +593,9 @@ func (b *Bridge) processFrom(ctx context.Context, msg adapter.UnifiedMessage, a
|
||||
b.log.Debug("no reply produced", "platform", msg.Platform, "session", sessID)
|
||||
return
|
||||
}
|
||||
// Persist the assistant reply (owner-private → main session + broadcast;
|
||||
// restricted → isolated log) then deliver it back to the channel.
|
||||
// Persist the assistant reply (owner-private → main session + message_persisted
|
||||
// broadcast; restricted → isolated log + channel_persisted broadcast) then
|
||||
// deliver it back to the channel.
|
||||
if isOwnerPrivate {
|
||||
b.persistToMain(ctx, history.RoleAssistant, reply, platform, deviceIDFor(msg), adapterName(a))
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user