Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8266fef42 | ||
|
|
ed579bf29c | ||
|
|
f8b77c0d02 | ||
|
|
0b3392acea | ||
|
|
90b57477bb | ||
|
|
6bdc4f2ada | ||
|
|
cafd9a54e5 | ||
|
|
a64fb5a168 | ||
|
|
c9aff06ed5 | ||
|
|
e6706450e4 | ||
|
|
fd441794b1 | ||
|
|
284e8f365f | ||
|
|
10a84345b7 |
@@ -17,3 +17,20 @@
|
||||
- 模块边界、公开契约与隔离档位见 `policy/modules/`(如有)与 PROJECT.md。
|
||||
- 跨仓 gitlink 变更须经组合验证后在 Dev 仓推进;受保护分支经 PR 合入。
|
||||
- 验证命令见 PROJECT.md「验证」节;完成声明须附实际命令输出证据。
|
||||
|
||||
## Gitea 与 Git 访问事实(2026-09-30 实测)
|
||||
|
||||
历史文档与旧链接里的 `git-panel.u1680832.nyat.app:30319` 已失效(TLS 握手失败),
|
||||
见到一律按过期处理,不要再试。当前权威通道:
|
||||
|
||||
- **Web / API(HTTPS)**:`https://git-home.u1680832.nyat.app:30319`(Gitea 1.27.3,
|
||||
API 前缀 `/api/v1`;偶发 broken pipe / 超时,重试即可)。
|
||||
- **Git over SSH**:`ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/<Repo>.git`
|
||||
(各仓 origin 与 gitlink 均为此格式)。
|
||||
- **API token**:`~/.secrets/gitea`,两行键值 `URL_PREFIX=…` 与
|
||||
`ACCESS_TOKEN=<40 位 hex>`;取值 `grep ACCESS_TOKEN ~/.secrets/gitea | cut -d= -f2-`,
|
||||
用作 `Authorization: token <值>` 请求头。**token 值不入仓、不回显、不写进产出物**
|
||||
(baseline 硬边界 2:只记录来源、字段与可用状态)。
|
||||
- **SSH key**:`~/.ssh/home_gitea`(ED25519,公钥指纹
|
||||
`SHA256:GYXaOXIMeloQ1fKSfdJ/ndgPQzs/Ix7Hk62ekfkfQ20`,Gitea 侧密钥名 GMKtec);
|
||||
`~/.ssh/config` 已有 `git-ssh.u1680832.nyat.app` 主机条目,克隆/推送直接可用。
|
||||
|
||||
+9
-2
@@ -47,6 +47,7 @@ func sampleMessage() Message {
|
||||
{Type: PartToolResult, Process: true, ToolResult: &ToolResultPart{CallID: "call_1", Status: "ok", Summary: "3 条命中"}},
|
||||
{Type: PartText, Text: "根据历史记录……"},
|
||||
{Type: PartArtifactRef, Artifact: &ArtifactRef{ArtifactID: "art_abc123def456", Name: "报告.md", Kind: "document"}},
|
||||
{Type: PartCard, Card: &CardPart{Kind: "result", Data: []UINode{{Type: "markdown", Markdown: "全文见卡片"}}, CardID: "card_abc123def456"}},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -63,7 +64,7 @@ func TestGoldenDomain(t *testing.T) {
|
||||
golden(t, "message", sampleMessage())
|
||||
golden(t, "run", sampleRun())
|
||||
golden(t, "project", Project{ID: "prj_abc123def456", Name: "示例项目"})
|
||||
golden(t, "conversation", Conversation{ID: "cnv_abc123def456", ProjectID: "prj_abc123def456", Title: "新会话"})
|
||||
golden(t, "conversation", Conversation{ID: "cnv_abc123def456", ProjectID: "prj_abc123def456", Title: "新会话", AllowCrossRead: true, CrossPrivate: true})
|
||||
golden(t, "artifact", Artifact{ID: "art_abc123def456", ProjectID: "prj_abc123def456", Name: "报告.md", Kind: "document", MIME: "text/markdown", Size: 128})
|
||||
}
|
||||
|
||||
@@ -124,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"}},
|
||||
@@ -176,7 +183,7 @@ func TestGoldenProfileAndTools(t *testing.T) {
|
||||
golden(t, "tool_http", ToolDesc{
|
||||
Name: "http_translate", Description: "调用翻译 API", Source: SourceUser,
|
||||
Parameters: json.RawMessage(`{"type":"object","properties":{"text":{"type":"string"}},"required":["text"]}`),
|
||||
HTTP: &HTTPToolSpec{Method: "POST", URL: "https://api.example.com/translate", BodyTemplate: `{"q":"{{param.text}}"}`, TimeoutMs: 10000, ResultPath: "data.0"},
|
||||
HTTP: &HTTPToolSpec{Method: "POST", URL: "https://api.example.com/translate", BodyTemplate: `{"q":"{{param.text}}"}`, TimeoutMs: 10000, ResultPath: "data.0"},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -33,6 +33,8 @@ type Conversation struct {
|
||||
ProfileID ID `json:"profileId,omitempty"` // 空为默认画像
|
||||
Pinned bool `json:"pinned,omitempty"` // 侧栏置顶(方案 A P6/P8:本端组织偏好,服务端权威)
|
||||
Archived bool `json:"archived,omitempty"` // 归档:列表默认隐藏,数据保留
|
||||
AllowCrossRead bool `json:"allowCrossRead,omitempty"` // 跨会话出站:本会话读取同项目其他会话记录(需项目开关 crossSession.read)
|
||||
CrossPrivate bool `json:"crossPrivate,omitempty"` // 跨会话入站:私有会话,不被他读、不出现在他方列表
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
@@ -122,9 +124,12 @@ type ErrorPart struct {
|
||||
}
|
||||
|
||||
// CardPart.Data 是 UI schema 节点(4.6 有限词汇)。
|
||||
// CardID 非空 = 收件箱 ActivityCard 的引用部件(如 submit_card 产卡:卡片为产物
|
||||
// 权威载体,消息只存引用+锚点渲染);空 = 插件内嵌卡片部件(自包含 Data)。
|
||||
type CardPart struct {
|
||||
Kind string `json:"kind"`
|
||||
Data []UINode `json:"data"`
|
||||
Kind string `json:"kind"`
|
||||
Data []UINode `json:"data"`
|
||||
CardID ID `json:"cardId,omitempty"`
|
||||
}
|
||||
|
||||
// ---- Run(一切运行皆 Run)----
|
||||
|
||||
Vendored
+2
@@ -2,6 +2,8 @@
|
||||
"id": "cnv_abc123def456",
|
||||
"projectId": "prj_abc123def456",
|
||||
"title": "新会话",
|
||||
"allowCrossRead": true,
|
||||
"crossPrivate": true,
|
||||
"createdAt": "0001-01-01T00:00:00Z",
|
||||
"updatedAt": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
|
||||
+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"
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+13
@@ -39,6 +39,19 @@
|
||||
"name": "报告.md",
|
||||
"kind": "document"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"card": {
|
||||
"kind": "result",
|
||||
"data": [
|
||||
{
|
||||
"type": "markdown",
|
||||
"markdown": "全文见卡片"
|
||||
}
|
||||
],
|
||||
"cardId": "card_abc123def456"
|
||||
}
|
||||
}
|
||||
],
|
||||
"createdAt": "0001-01-01T00:00:00Z"
|
||||
|
||||
@@ -20,6 +20,9 @@ type ToolDesc struct {
|
||||
Source SourceKind `json:"source"`
|
||||
PluginID string `json:"pluginId,omitempty"`
|
||||
HTTP *HTTPToolSpec `json:"http,omitempty"` // 声明式 HTTP 工具(无代码)
|
||||
// Hidden 预留能力形态:默认不对模型与 /api/tools 列表可见,须运行时开关
|
||||
// (如 runtime.fsTools)或管理动作开启后可用(提案 2026-09-30-file-preview-and-reserved-capabilities)。
|
||||
Hidden bool `json:"hidden,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPToolSpec:无代码自定义工具(4.8)。模板变量 {{param.x}} 从调用参数注入。
|
||||
|
||||
+2
-1
@@ -14,6 +14,7 @@ export interface Project {
|
||||
export interface Conversation {
|
||||
id: ID; projectId: ID; owner?: ID; title: string; profileId?: ID;
|
||||
pinned?: boolean; archived?: boolean;
|
||||
allowCrossRead?: boolean; crossPrivate?: boolean;
|
||||
createdAt: string; updatedAt: string;
|
||||
}
|
||||
|
||||
@@ -31,7 +32,7 @@ export interface Part {
|
||||
artifact?: { artifactId: ID; name: string; kind?: string };
|
||||
hitl?: { cardId: ID; question: string; resolved: boolean; answer?: string };
|
||||
plan?: PlanSnapshot;
|
||||
card?: { kind: string; data: UINode[] };
|
||||
card?: { kind: string; data: UINode[]; cardId?: ID }; // cardId 非空 = 收件箱卡片引用部件(submit_card)
|
||||
error?: { code: string; message: string };
|
||||
process?: boolean; // run 终态后折叠为摘要行
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user