Compare commits

...
Author SHA1 Message Date
weichen 284e8f365f Add cross-session flags to the Conversation contract
Why: cross-session transcript reading needs per-conversation outbound
(allowCrossRead, default true) and inbound (crossPrivate, default false)
flags alongside the project-level gate; see the approved proposal.

Evidence: go test ./... passes with golden conversation.json regenerated
to exercise both wire fields.

Impact: additive contract fields (omitempty), wire-compatible for
existing clients that ignore unknown fields.

Refs: Agent-System-Documents proposals/2026-09-30-cross-session-reading.md
(Dev task feature/cross-session-reading).
2026-09-30 15:00:22 +00:00
weichen 10a84345b7 Merge pull request 'Add the avatar vocabulary to UINode (UI capability platform)' (#5) from feature/ui-capability-platform into main 2026-09-30 14:22:27 +00:00
weichen e5c9c540cd Add the avatar vocabulary to UINode (UI capability platform)
Why: plugins and the host need one shared avatar primitive for member/
owner displays in cards, messages, and admin surfaces (user decision
2026-09-30: avatars, cards, settings become base capabilities plugins
reuse directly).

Evidence: go test green with regenerated golden (single avatar with
sub/tone plus a stacked group); omitempty keeps existing wire samples
byte-stable.

Impact: additive optional UINode field + type; unknown-type consumers
ignore it (forward compat). Renderers gain 'avatar' via the ui repo.

Refs: Agent-System-Documents proposal 2026-09-30-ui-redesign-and-auth.md
2026-09-30 14:22:10 +00:00
weichen 9c4d163333 Merge pull request 'Add pinned and archived flags to Conversation (R1)' (#4) from feature/task-sidebar-r1 into main 2026-09-30 10:45:12 +00:00
weichen b6d428ba35 Add pinned and archived flags to Conversation
Why: the approved task-centric sidebar (proposal 2026-09-30, option A) needs
server-authoritative pin and archive state on conversations; client-side
storage would be fake state against the event-sourced model.

Evidence: go test ./... passes; omitempty keeps wire format and golden
samples byte-identical for zero values (backward compatible).

Impact: additive optional fields; consumers ignoring unknown fields are
unaffected (forward-compat principle). Backend consumes via submodule bump.

Refs: Agent-System-Documents docs/proposals/2026-09-30-ui-redesign-and-auth.md (P6/P8, R1)
2026-09-30 10:39:16 +00:00
weichen 1728c95eae Merge pull request 'feature/prompt-library' (#3) from feature/prompt-library into main 2026-09-30 10:36:34 +00:00
weichen 0cb0a40dc8 Add the prompt module contract and profile references
Why: prompts are moving from code into a data library; core needs the
PromptModule/Trigger/Block contract and profiles need an ordered
promptModules reference (empty = default base+env set).

Evidence: unit-covered via backend prompts package tests over this
contract; composite make test green.

Impact: additive contract fields only; no behavior change in core.

Refs: feature/prompt-library task card.
2026-09-30 10:28:59 +00:00
weichen a2050d848f Merge pull request 'Add activity card, card events, HITL anchoring and UI vocabulary extension' (#2) from feature/core-cards-commands-r1 into main 2026-09-30 06:22:01 +00:00
8 changed files with 170 additions and 23 deletions
+7 -1
View File
@@ -63,7 +63,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})
}
@@ -125,6 +125,12 @@ func TestGoldenCardEvents(t *testing.T) {
}
func TestGoldenUIVocabularies(t *testing.T) {
golden(t, "ui_avatar", []UINode{
{Type: UIAvatar, Avatar: &UIAvatarSpec{Name: "weichen", Sub: "OWNER"}},
{Type: UIAvatar, Avatar: &UIAvatarSpec{
Stack: []UIAvatarSpec{{Name: "甲", Tone: "ok"}, {Name: "乙", Tone: "warn"}, {Name: "丙"}},
}},
})
golden(t, "ui_vocabularies", []UINode{
{Type: UIProgress, Progress: &UIProgressSpec{Label: "市场可行性 78%", Value: 0.78, Tone: "ok"}},
{Type: UICompare, Compare: &UICompareSpec{
+4
View File
@@ -31,6 +31,10 @@ type Conversation struct {
Owner ID `json:"owner,omitempty"` // 冗余自项目,查询过滤用
Title string `json:"title"`
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"`
}
+3
View File
@@ -11,6 +11,9 @@ type AgentProfile struct {
Builtin bool `json:"builtin,omitempty"`
Source string `json:"source,omitempty"` // builtin | plugin | user
SystemPrompt string `json:"systemPrompt"`
// PromptModules 引用提示词库模块 ID(有序;空 = 组装默认基座全集)。
// SystemPrompt 兼容保留:非空时作为附加段追加在模块组装结果之后。
PromptModules []string `json:"promptModules,omitempty"`
Model ModelRoute `json:"model"`
Tools ToolPolicy `json:"tools"`
Context ContextPolicy `json:"context"`
+71
View File
@@ -0,0 +1,71 @@
package core
// 提示词模块契约(prompt-library):提示词从代码移入数据,按层组装、按触发注入。
//
// 分层(Layer):
// - base 常驻基座块(身份/行为/谨慎/工具/风格),始终组装
// - env 动态环境段(占位符填充:日期/模型/附件清单)
// - task 任务技能(触发命中才注入;同层只注入优先级最高者)
// - agent 子代理/小队角色提示词(派生 run 时取用)
// - tool 工具描述文案(ToolDecl.description 的数据来源)
// - aux 辅助模板(压缩摘要/标题生成等;入库先行,接线随功能走)
// - style 输出风格(叠加在基座之上的表达方式变体)
type PromptModule struct {
ID string `json:"id"` // "base.identity" / "task.doc-minutes"
Layer string `json:"layer"` // base|env|task|agent|tool|aux|style
Version int `json:"version"` // 内置升级判定
Title string `json:"title"`
Description string `json:"description"` // 给人看(设置界面/命令菜单)
Priority int `json:"priority,omitempty"` // task 层多命中时的取舍;缺省 50,大者优先
Triggers []PromptTrigger `json:"triggers,omitempty"` // 空 = 基座,始终组装
RequiresAttachments bool `json:"requiresAttachments,omitempty"` // 命中还需本轮有附件
Body string `json:"body"` // 模板正文,{{placeholder}} 占位
OutputContract string `json:"outputContract,omitempty"` // 期望产出形态(注入模型 + 供验收)
Source string `json:"source,omitempty"` // builtin | plugin | user
Owner string `json:"owner,omitempty"`
BasedOnVersion int `json:"basedOnVersion,omitempty"` // 用户副本基于的内置版本
Drifted bool `json:"drifted,omitempty"` // 用户副本落后于当前内置版本(提示用,不阻断)
Disabled bool `json:"disabled,omitempty"` // 用户停用
}
// PromptTrigger 触发条件;模块命中 = 任一 trigger 命中(再过 RequiresAttachments)。
type PromptTrigger struct {
Kind string `json:"kind"` // intent | command | attachment | manual
Keywords []string `json:"keywords,omitempty"`
Command string `json:"command,omitempty"` // "/minutes"
}
// 触发 Kind 常量。
const (
TriggerIntent = "intent"
TriggerCommand = "command"
TriggerAttachment = "attachment"
TriggerManual = "manual"
)
// 层常量。
const (
LayerBase = "base"
LayerEnv = "env"
LayerTask = "task"
LayerAgent = "agent"
LayerTool = "tool"
LayerAux = "aux"
LayerStyle = "style"
)
// PromptBlock 组装产物:一个已解析占位符的 system 段。
type PromptBlock struct {
ModuleID string `json:"moduleId"`
Version int `json:"version"`
Layer string `json:"layer"`
Body string `json:"body"`
}
// PromptHitRecord 触发判定记录(可观测:哪个技能因何种原因注入/让位)。
type PromptHitRecord struct {
ModuleID string `json:"moduleId"`
Layer string `json:"layer"`
Reason string `json:"reason"` // "intent:纪要" / "command:/minutes" / "always" / "superseded-by:<id>"
Injected bool `json:"injected"`
}
+2
View File
@@ -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"
}
+27
View File
@@ -0,0 +1,27 @@
[
{
"type": "avatar",
"avatar": {
"name": "weichen",
"sub": "OWNER"
}
},
{
"type": "avatar",
"avatar": {
"stack": [
{
"name": "甲",
"tone": "ok"
},
{
"name": "乙",
"tone": "warn"
},
{
"name": "丙"
}
]
}
}
]
+25 -1
View File
@@ -13,6 +13,8 @@ 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;
}
@@ -151,7 +153,7 @@ export interface HITLResponse {
export type UINodeType =
| 'section' | 'list' | 'kv' | 'form' | 'markdown' | 'doc-ref' | 'actions' | 'chart-lite'
| 'progress' | 'compare' | 'tree' | 'table' | 'steps' | 'tabs' | 'timeline';
| 'avatar' | 'progress' | 'compare' | 'tree' | 'table' | 'steps' | 'tabs' | 'timeline';
export interface UINode {
type: UINodeType;
@@ -165,6 +167,7 @@ export interface UINode {
chart?: { kind: 'bar' | 'line'; labels: string[]; series: number[] };
children?: UINode[];
avatar?: { name?: string; sub?: string; tone?: string; stack?: { name?: string; sub?: string; tone?: string }[] };
progress?: { label?: string; value: number; tone?: string };
compare?: {
left: { title?: string; tone?: string; nodes?: UINode[] };
@@ -191,11 +194,32 @@ export interface UIField {
options?: string[]; required?: boolean; placeholder?: string; help?: string; default?: string;
}
// ---- 提示词库(prompt-library)----
export interface PromptTrigger {
kind: 'intent' | 'command' | 'attachment' | 'manual';
keywords?: string[]; command?: string;
}
export interface PromptModule {
id: string; layer: 'base' | 'env' | 'task' | 'agent' | 'tool' | 'aux' | 'style';
version: number; title: string; description?: string; priority?: number;
triggers?: PromptTrigger[]; requiresAttachments?: boolean;
body: string; outputContract?: string;
source?: 'builtin' | 'plugin' | 'user'; owner?: string;
basedOnVersion?: number; drifted?: boolean; disabled?: boolean;
}
export interface PromptBlock { moduleId: string; version: number; layer: string; body: string; }
export interface PromptHitRecord { moduleId: string; layer: string; reason: string; injected: boolean; }
// ---- 画像 / 设置 ----
export interface AgentProfile {
id: string; owner?: string; name: string; description?: string; builtin?: boolean; source?: string;
systemPrompt: string;
/** 提示词库模块引用(有序;空 = 默认 base+env 全集)。 */
promptModules?: string[];
model: { chat?: string; lightweight?: string; embedding?: string; rerank?: string };
tools: { allow: string[]; deny?: string[]; confirm?: string[] };
context: {
+31 -21
View File
@@ -17,6 +17,7 @@ const (
UIChartLite UINodeType = "chart-lite"
// —— 收件箱/工作区扩展词汇(提案 D28;graph 远期,追溯视图按"对象为中心 +
// 分组泳道 + 逐层展开"约束设计,禁止力导向裸图)——
UIAvatar UINodeType = "avatar" // 头像(成员/责任人展示;UI 能力平台,插件可用)
UIProgress UINodeType = "progress" // 进度条(摄取/生成/填充度)
UICompare UINodeType = "compare" // 左右对比(基线 vs 新内容、diff 确认)
UITree UINodeType = "tree" // 树(文档树/笔记树骨架;节点带状态灯/徽标/计数)
@@ -27,24 +28,33 @@ const (
)
type UINode struct {
Type UINodeType `json:"type"`
Title string `json:"title,omitempty"`
Items []string `json:"items,omitempty"` // list
KV [][2]string `json:"kv,omitempty"` // kv
Fields []UIField `json:"fields,omitempty"` // form
Markdown string `json:"markdown,omitempty"`
DocID ID `json:"docId,omitempty"` // doc-ref
Actions []UIAction `json:"actions,omitempty"`
Chart *UIChart `json:"chart,omitempty"`
Children []UINode `json:"children,omitempty"` // section
Type UINodeType `json:"type"`
Title string `json:"title,omitempty"`
Items []string `json:"items,omitempty"` // list
KV [][2]string `json:"kv,omitempty"` // kv
Fields []UIField `json:"fields,omitempty"` // form
Markdown string `json:"markdown,omitempty"`
DocID ID `json:"docId,omitempty"` // doc-ref
Actions []UIAction `json:"actions,omitempty"`
Chart *UIChart `json:"chart,omitempty"`
Children []UINode `json:"children,omitempty"` // section
Progress *UIProgressSpec `json:"progress,omitempty"` // progress
Compare *UICompareSpec `json:"compare,omitempty"` // compare
Tree *UITreeSpec `json:"tree,omitempty"` // tree
Table *UITableSpec `json:"table,omitempty"` // table
Steps *UIStepsSpec `json:"steps,omitempty"` // steps
Tabs *UITabsSpec `json:"tabs,omitempty"` // tabs
Timeline *UITimelineSpec `json:"timeline,omitempty"` // timeline
Avatar *UIAvatarSpec `json:"avatar,omitempty"` // avatar
Progress *UIProgressSpec `json:"progress,omitempty"` // progress
Compare *UICompareSpec `json:"compare,omitempty"` // compare
Tree *UITreeSpec `json:"tree,omitempty"` // tree
Table *UITableSpec `json:"table,omitempty"` // table
Steps *UIStepsSpec `json:"steps,omitempty"` // steps
Tabs *UITabsSpec `json:"tabs,omitempty"` // tabs
Timeline *UITimelineSpec `json:"timeline,omitempty"` // timeline
}
// UIAvatar:头像(成员/责任人;Name 取首字,Tone 走语义色;Stack 为叠放组)。
type UIAvatarSpec struct {
Name string `json:"name,omitempty"`
Sub string `json:"sub,omitempty"` // 名字旁的小注(角色/状态)
Tone string `json:"tone,omitempty"` // ok/warn/danger/info/留空=品牌色
Stack []UIAvatarSpec `json:"stack,omitempty"` // 叠放头像组(多成员)
}
// UIProgress:0–1 归一进度或 n/m 计数(Value=Done/Total 任一形式,前端按 <1 判定)。
@@ -88,9 +98,9 @@ type UITableColumn struct {
}
type UITableRow struct {
ID string `json:"id,omitempty"`
Cells [][2]string `json:"cells"` // 与 Columns.Key 对应的有序单元格
Actions []UIAction `json:"actions,omitempty"` // 行级操作(走命令总线)
ID string `json:"id,omitempty"`
Cells [][2]string `json:"cells"` // 与 Columns.Key 对应的有序单元格
Actions []UIAction `json:"actions,omitempty"` // 行级操作(走命令总线)
}
type UITableSpec struct {
@@ -146,7 +156,7 @@ type UIField struct {
type UIAction struct {
ID string `json:"id"`
Label string `json:"label"`
Style string `json:"style,omitempty"` // primary | default | danger
Style string `json:"style,omitempty"` // primary | default | danger
Command string `json:"command,omitempty"` // 绑定命令总线命令 id;空则降级为发消息
}