Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8266fef42 | ||
|
|
ed579bf29c | ||
|
|
f8b77c0d02 | ||
|
|
0b3392acea | ||
|
|
90b57477bb |
@@ -125,6 +125,12 @@ func TestGoldenCardEvents(t *testing.T) {
|
||||
Payload: json.RawMessage(`{"cardId":"card_abc123def456","actionId":"escalate"}`)})
|
||||
}
|
||||
|
||||
func TestGoldenProjectStreamEvent(t *testing.T) {
|
||||
// 项目流(D50):伪会话键 "project:<projectId>" 复用会话流机制;信封带 ProjectID。
|
||||
golden(t, "event_project_stream", StreamEvent{Seq: 47, ConversationID: "project:prj_abc123def456", ProjectID: "prj_abc123def456", TS: "2026-09-30T11:00:00Z", V: 1, Type: EvCardCreated,
|
||||
Payload: json.RawMessage(`{"card":{"id":"card_def456abc789","projectId":"prj_abc123def456","kind":"result","title":"项目级卡","status":"done","createdAt":"2026-09-30T11:00:00Z","updatedAt":"2026-09-30T11:00:00Z"}}`)})
|
||||
}
|
||||
|
||||
func TestGoldenUIVocabularies(t *testing.T) {
|
||||
golden(t, "ui_avatar", []UINode{
|
||||
{Type: UIAvatar, Avatar: &UIAvatarSpec{Name: "weichen", Sub: "OWNER"}},
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
package core
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 事件信封:后端 → 前端的唯一增量通道(SSE),持久化后转发,Seq 在会话内单调。
|
||||
// 消费方对未知 Type 静默忽略(原则 6 前向兼容)。
|
||||
// 项目流(D50):项目频道事件以伪会话键 "project:<projectId>" 复用同一机制
|
||||
//(独立 seq、持久化、断线重放),信封以 ProjectID 标识归属(会话流事件为空)。
|
||||
type StreamEvent struct {
|
||||
Seq int64 `json:"seq"`
|
||||
RunID ID `json:"runId,omitempty"`
|
||||
ConversationID ID `json:"conversationId"`
|
||||
ProjectID ID `json:"projectID,omitempty"` // 项目流事件的项目归属(D50;只增不删)
|
||||
TS string `json:"ts"` // RFC3339
|
||||
V int `json:"v"`
|
||||
Type StreamEventType `json:"type"`
|
||||
@@ -16,6 +22,19 @@ type StreamEvent struct {
|
||||
|
||||
type StreamEventType string
|
||||
|
||||
// 项目流频道约定(D50):项目频道以伪会话键 "project:<projectId>" 复用会话
|
||||
// 事件的持久化/seq/重放机制;信封 ProjectID 由该键派生(发布与重放同源)。
|
||||
const projectChannelPrefix = "project:"
|
||||
|
||||
// ProjectChannelKey 返回项目流的事件频道键。
|
||||
func ProjectChannelKey(projectID ID) ID { return ID(projectChannelPrefix + string(projectID)) }
|
||||
|
||||
// ProjectFromChannel 反解项目流频道键;非项目流频道返回 false。
|
||||
func ProjectFromChannel(key ID) (ID, bool) {
|
||||
pid, ok := strings.CutPrefix(string(key), projectChannelPrefix)
|
||||
return ID(pid), ok && pid != ""
|
||||
}
|
||||
|
||||
const (
|
||||
EvMessageDelta StreamEventType = "message.delta"
|
||||
EvThinkingDelta StreamEventType = "thinking.delta"
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"seq": 47,
|
||||
"conversationId": "project:prj_abc123def456",
|
||||
"projectID": "prj_abc123def456",
|
||||
"ts": "2026-09-30T11:00:00Z",
|
||||
"v": 1,
|
||||
"type": "card.created",
|
||||
"payload": {
|
||||
"card": {
|
||||
"id": "card_def456abc789",
|
||||
"projectId": "prj_abc123def456",
|
||||
"kind": "result",
|
||||
"title": "项目级卡",
|
||||
"status": "done",
|
||||
"createdAt": "2026-09-30T11:00:00Z",
|
||||
"updatedAt": "2026-09-30T11:00:00Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user