Compare commits

..
Author SHA1 Message Date
weichen c8266fef42 Merge main into feature/project-inbox-r1 (sync before merge per branch protection) 2026-09-30 19:10:04 +00:00
weichen ed579bf29c Extract project channel key helpers (CF3 review fix)
Why: project channel convention (pseudo-key project:<projectId>) was
duplicated across runmgr/server/store; centralize so replay envelopes
match live stamping.
Evidence: go test green.
Impact: no behavior change; enables store replay stamping.
2026-09-30 19:07:06 +00:00
weichen f8b77c0d02 Merge pull request 'Add ProjectID to stream event envelope (CF3 project channel)' (#11) from feature/project-inbox-r1 into main 2026-09-30 18:56:52 +00:00
weichen 0b3392acea Add ProjectID to stream event envelope for project channel (CF3)
Why: D50 approved — the project inbox needs its own SSE stream reusing the
conversation event mechanism via the pseudo-key project:<projectId>; the
envelope stamps project ownership (additive field).
Evidence: conformance golden event_project_stream; go test green.
Impact: additive only (omitempty); consumers ignore unknown fields.
Refs: documents proposal 2026-09-30-card-stream-artifact-unify-project-inbox.md (D50).
2026-09-30 18:56:47 +00:00
weichen 90b57477bb Merge pull request 'Add Hidden to ToolDesc for reserved tool capabilities' (#10) from feature/file-preview-caps into main 2026-09-30 17:20:20 +00:00
3 changed files with 45 additions and 1 deletions
+6
View File
@@ -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"}},
+20 -1
View File
@@ -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
View File
@@ -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"
}
}
}