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
@@ -235,8 +235,11 @@ func (h *hub) streamChat(ctx context.Context, payload any) {
|
||||
// The Web main session is the owner's private conversation (JWT = owner), so
|
||||
// it binds the full-access env: memory read+write, unchanged behavior.
|
||||
msgs := userChatMessages(req.Text, req.Image)
|
||||
// Thread the client's deviceID into the orchestrator via ctx so both the
|
||||
// persisted user turn and the assistant reply are tagged (design/16 §4.5).
|
||||
ctx = withDeviceID(ctx, req.DeviceID)
|
||||
if h.orchestrator != nil {
|
||||
_ = h.orchestrator.Submit(context.Background(), req.SessionID, msgs, loop.OwnerEnv())
|
||||
_ = h.orchestrator.Submit(ctx, req.SessionID, msgs, loop.OwnerEnv())
|
||||
return
|
||||
}
|
||||
_, _ = h.runTurn(ctx, req.SessionID, msgs, loop.OwnerEnv())
|
||||
@@ -305,6 +308,10 @@ type chatStreamRequest struct {
|
||||
SessionID string `json:"sessionId"`
|
||||
Text string `json:"text"`
|
||||
Image string `json:"image,omitempty"`
|
||||
// DeviceID is the client-produced identifier of the sending browser/device
|
||||
// (design/16 §4.5), e.g. "web:chrome:a1b2c3"; it is optional and persisted
|
||||
// alongside the message so the agent knows which end the owner is chatting from.
|
||||
DeviceID string `json:"deviceId,omitempty"`
|
||||
}
|
||||
|
||||
// handleWS upgrades /ws/chat after authenticating the JWT from the query token or
|
||||
@@ -432,11 +439,13 @@ func (c *wsConn) enqueueJSON(ev wsEvent) {
|
||||
}
|
||||
|
||||
// publishChatStream publishes a ws.chat streaming trigger (REST stream mode).
|
||||
func (s *Server) publishChatStream(ctx context.Context, sessionID, text, image string) {
|
||||
// deviceID is the sending browser/device id carried on the payload so the ws.chat
|
||||
// consumer can tag the persisted turn (design/16 §4.5).
|
||||
func (s *Server) publishChatStream(ctx context.Context, sessionID, text, image, deviceID string) {
|
||||
if s.bus == nil {
|
||||
return
|
||||
}
|
||||
_ = s.bus.Publish(ctx, wsChatSubject, map[string]any{"sessionId": sessionID, "text": text, "image": image})
|
||||
_ = s.bus.Publish(ctx, wsChatSubject, map[string]any{"sessionId": sessionID, "text": text, "image": image, "deviceId": deviceID})
|
||||
}
|
||||
|
||||
// wsToken extracts the JWT from the ?token= query or the Authorization header.
|
||||
|
||||
Reference in new issue
Block a user