Compare commits
38
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b5c7747a3 | ||
|
|
bd632236c2 | ||
|
|
5dc190ee63 | ||
|
|
b7e5baf15c | ||
|
|
cf7df54874 | ||
|
|
88e481afcd | ||
|
|
7c66075ee9 | ||
|
|
3f7067fb2d | ||
|
|
f08a4a90c6 | ||
|
|
4d1123e45c | ||
|
|
f93f473a0e | ||
|
|
a8e81e078d | ||
|
|
9b8cf79c96 | ||
|
|
493a846f07 | ||
|
|
447ea5c5ae | ||
|
|
fdc0c1b933 | ||
|
|
b80407a9a6 | ||
|
|
d4375300cd | ||
|
|
1b04a34bbc | ||
|
|
c8266fef42 | ||
|
|
ed579bf29c | ||
|
|
f8b77c0d02 | ||
|
|
0b3392acea | ||
|
|
90b57477bb | ||
|
|
6bdc4f2ada | ||
|
|
cafd9a54e5 | ||
|
|
a64fb5a168 | ||
|
|
c9aff06ed5 | ||
|
|
e6706450e4 | ||
|
|
fd441794b1 | ||
|
|
284e8f365f | ||
|
|
10a84345b7 | ||
|
|
e5c9c540cd | ||
|
|
9c4d163333 | ||
|
|
b6d428ba35 | ||
|
|
1728c95eae | ||
|
|
0cb0a40dc8 | ||
|
|
a2050d848f |
@@ -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` 主机条目,克隆/推送直接可用。
|
||||
|
||||
+44
-3
@@ -42,11 +42,12 @@ func sampleMessage() Message {
|
||||
ID: "msg_abc123def456", ConversationID: "cnv_abc123def456", RunID: "run_abc123def456",
|
||||
Role: RoleAssistant,
|
||||
Parts: []Part{
|
||||
{Type: PartThinking, Text: "考虑先检索…", Process: true},
|
||||
{Type: PartThinking, Text: "考虑先检索…", Process: true, DurationMs: 1500},
|
||||
{Type: PartToolCall, Process: true, ToolCall: &ToolCallPart{ID: "call_1", Name: "history_search", Arguments: `{"q":"部署"}`, Duration: 12.5}},
|
||||
{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"}},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -59,11 +60,19 @@ func sampleRun() Run {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoldenPromptModule(t *testing.T) {
|
||||
golden(t, "prompt_module_subagent", PromptModule{
|
||||
ID: "agent.sub.explore", Layer: LayerAgent, Version: 1,
|
||||
Title: "探索子代理", Body: "只读检索。",
|
||||
Tools: []string{"knowledge_search", "web_search"}, Model: "lightweight", MaxTurns: 8,
|
||||
})
|
||||
}
|
||||
|
||||
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,7 +133,19 @@ 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"}},
|
||||
{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{
|
||||
@@ -170,7 +191,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"},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -186,6 +207,26 @@ func TestGoldenIngest(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGoldenWorkflowDef(t *testing.T) {
|
||||
golden(t, "workflow_def", WorkflowDef{
|
||||
ID: "demo.brief", Title: "简报", Description: "按主题写一份简报",
|
||||
IntentID: "demo.brief", MaxRetries: 6,
|
||||
Steps: []WorkflowStep{
|
||||
{ID: "length", Title: "篇幅", Kind: "hitl", Hitl: &HITLCardTemplate{
|
||||
Question: "篇幅?", Kind: HITLKindChoice, AllowFreeInput: false,
|
||||
Choices: []HITLChoice{{ID: "short", Label: "短", Description: "一页", Impact: "更快"}},
|
||||
}},
|
||||
{ID: "draft", Title: "起草", Kind: "prompt", Agent: "research",
|
||||
Prompt: "写一份关于 {{slots.topic}} 的简报。用户说:{{input}}。上一步:{{steps.length.output}}",
|
||||
Critic: &StepCritic{Criteria: "必须点明主题", MaxRetries: 1}},
|
||||
{ID: "lookup", Title: "查时间", Kind: "tool", Tool: "time_now", Args: json.RawMessage(`{"tz":"UTC"}`)},
|
||||
{ID: "save", Title: "存稿", Kind: "artifact", Artifact: "brief.md", Prompt: "{{steps.draft.output}}"},
|
||||
{ID: "note", Title: "通知", Kind: "card", Card: &ActivityCardTemplate{Kind: CardKindResult, Title: "简报已写好"}},
|
||||
{ID: "run", Title: "脚本", Kind: "script", Script: "scripts/demo.py"},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestGoldenPlugin(t *testing.T) {
|
||||
golden(t, "plugin_manifest", PluginManifest{
|
||||
ID: "example", Name: "示例插件", Version: "0.1.0", APIVersion: PluginAPIVersion,
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
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"`
|
||||
TS string `json:"ts"` // RFC3339
|
||||
ProjectID ID `json:"projectID,omitempty"` // 项目流事件的项目归属(D50;只增不删)
|
||||
TS string `json:"ts"` // RFC3339
|
||||
V int `json:"v"`
|
||||
Type StreamEventType `json:"type"`
|
||||
Payload json.RawMessage `json:"payload,omitempty"`
|
||||
@@ -16,12 +22,28 @@ 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"
|
||||
EvToolCallDelta StreamEventType = "tool.call.delta" // 参数增量:工具开始执行前即可上屏
|
||||
EvToolCall StreamEventType = "tool.call"
|
||||
EvToolOutput StreamEventType = "tool.output" // 执行中的 stdout/stderr 增量
|
||||
EvToolResult StreamEventType = "tool.result"
|
||||
EvRunPhase StreamEventType = "run.phase"
|
||||
EvRunCheck StreamEventType = "run.check" // 终轮自动检查,过程区一行
|
||||
EvHITLRequest StreamEventType = "hitl.request"
|
||||
EvHITLResolved StreamEventType = "hitl.resolved"
|
||||
EvPlanUpdate StreamEventType = "plan.update"
|
||||
@@ -63,17 +85,40 @@ type ToolCallPayload struct {
|
||||
Arguments string `json:"arguments,omitempty"`
|
||||
}
|
||||
|
||||
// ToolCallDeltaPayload 是模型正在编写的工具参数增量。Index 标识同轮第几次调用
|
||||
// (与供应商 tool_calls 下标一致);Name/CallID 往往只在首个分片出现。
|
||||
type ToolCallDeltaPayload struct {
|
||||
Index int `json:"index"`
|
||||
CallID string `json:"callId,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Delta string `json:"delta"`
|
||||
}
|
||||
|
||||
// ToolOutputPayload 是工具执行过程中的输出增量。Stream 为 stdout 或 stderr。
|
||||
type ToolOutputPayload struct {
|
||||
CallID string `json:"callId"`
|
||||
Stream string `json:"stream"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type ToolResultPayload struct {
|
||||
CallID string `json:"callId"`
|
||||
Status string `json:"status"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Truncated bool `json:"truncated,omitempty"`
|
||||
CallID string `json:"callId"`
|
||||
Status string `json:"status"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Truncated bool `json:"truncated,omitempty"`
|
||||
DurationMs float64 `json:"durationMs,omitempty"`
|
||||
}
|
||||
|
||||
type RunPhasePayload struct {
|
||||
Phase RunPhase `json:"phase"`
|
||||
}
|
||||
|
||||
// RunCheckPayload 与 CheckPart 同形,让运行中和重新加载后的检查行一致。
|
||||
type RunCheckPayload struct {
|
||||
Passed bool `json:"passed"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
type HITLResolvedPayload struct {
|
||||
CardID ID `json:"cardId"`
|
||||
Answer string `json:"answer,omitempty"`
|
||||
@@ -96,15 +141,15 @@ type AgentSpawnPayload struct {
|
||||
}
|
||||
|
||||
type AgentProgressPayload struct {
|
||||
AgentRun ID `json:"agentRun"`
|
||||
Note string `json:"note,omitempty"`
|
||||
AgentRun ID `json:"agentRun"`
|
||||
Note string `json:"note,omitempty"`
|
||||
Phase RunPhase `json:"phase,omitempty"`
|
||||
}
|
||||
|
||||
type AgentResultPayload struct {
|
||||
AgentRun ID `json:"agentRun"`
|
||||
AgentRun ID `json:"agentRun"`
|
||||
Status RunStatus `json:"status"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
// Text 为子代理完整文本产出(squad 步骤交付物用;截断摘要见 Summary)。
|
||||
Text string `json:"text,omitempty"`
|
||||
}
|
||||
@@ -119,11 +164,20 @@ type RunCompletedPayload struct {
|
||||
Context *ContextObservation `json:"context,omitempty"`
|
||||
}
|
||||
|
||||
// ContextSection 是组装结果里一段的估算。EstTokens 用 UTF-8 字节数 / 3.35,
|
||||
// 再按上一请求的真实用量校准。
|
||||
type ContextSection struct {
|
||||
Name string `json:"name"`
|
||||
EstTokens int `json:"estTokens"`
|
||||
}
|
||||
|
||||
// ContextObservation 记录一次模型输入的实际构成与预算裁剪(ctxasm 产出)。
|
||||
type ContextObservation struct {
|
||||
Items []string `json:"items"` // 如 system(画像) / history(12) / attachments(1) / knowledge(3)
|
||||
Truncations []string `json:"truncations,omitempty"` // 裁剪记录(历史超窗/工具结果超限/附件截断)
|
||||
EstChars int `json:"estChars"` // 估算输入字符量
|
||||
Items []string `json:"items"` // 如 system(画像) / history(12) / attachments(1) / knowledge(3)
|
||||
Truncations []string `json:"truncations,omitempty"` // 裁剪记录(历史超窗/工具结果超限/附件截断)
|
||||
EstChars int `json:"estChars"` // 估算输入量(token;字段名保持兼容)
|
||||
Sections []ContextSection `json:"sections,omitempty"`
|
||||
PrefixHash string `json:"prefixHash,omitempty"` // 系统前缀哈希,前缀不变时保持稳定
|
||||
}
|
||||
|
||||
type RunFailedPayload struct {
|
||||
|
||||
@@ -23,6 +23,8 @@ type HITLCard struct {
|
||||
// Blocked:阻断态(门禁不通过等)——确认入口禁用并给出出路;阻断本身进审计。
|
||||
Blocked bool `json:"blocked,omitempty"`
|
||||
BlockReason string `json:"blockReason,omitempty"`
|
||||
// Detail 是确认卡上的参数预览或其他补充说明。
|
||||
Detail string `json:"detail,omitempty"`
|
||||
}
|
||||
|
||||
type HITLKind string
|
||||
|
||||
@@ -17,22 +17,30 @@ type ID = string
|
||||
// Owner 是租户标识(多租户主线,设计 D5/D16):空串 = 单租户默认(嵌入式档/
|
||||
// 未启用认证时的隐式 owner)。API 层强制 owner 过滤,跨租户访问一律 NotFound。
|
||||
type Project struct {
|
||||
ID ID `json:"id"`
|
||||
Owner ID `json:"owner,omitempty"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ID ID `json:"id"`
|
||||
Owner ID `json:"owner,omitempty"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Settings map[string]any `json:"settings,omitempty"` // 项目层设置覆盖(4.9 覆盖链)
|
||||
}
|
||||
|
||||
type Conversation struct {
|
||||
ID ID `json:"id"`
|
||||
ProjectID ID `json:"projectId"`
|
||||
Owner ID `json:"owner,omitempty"` // 冗余自项目,查询过滤用
|
||||
Title string `json:"title"`
|
||||
ProfileID ID `json:"profileId,omitempty"` // 空为默认画像
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ID ID `json:"id"`
|
||||
ProjectID ID `json:"projectId"`
|
||||
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"` // 跨会话入站:私有会话,不被他读、不出现在他方列表
|
||||
// PermissionMode 是会话权限:空或 default 沿用画像;plan 只读加计划工具;auto_edit 放行工作区写文件。
|
||||
PermissionMode string `json:"permissionMode,omitempty"`
|
||||
// SessionNote 是本会话要求,组装时附在上下文末尾,不进入系统前缀。
|
||||
SessionNote string `json:"sessionNote,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// ---- 消息与部件 ----
|
||||
@@ -57,16 +65,22 @@ const (
|
||||
PartHITL PartType = "hitl"
|
||||
PartPlan PartType = "plan"
|
||||
PartError PartType = "error"
|
||||
PartCard PartType = "card" // 插件自定义卡片(Data 为 UI schema)
|
||||
PartCard PartType = "card" // 插件自定义卡片(Data 为 UI schema)
|
||||
PartCheck PartType = "check" // 终轮自动检查,过程区一行,不是给用户的回复
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
ID ID `json:"id"`
|
||||
ConversationID ID `json:"conversationId"`
|
||||
RunID ID `json:"runId,omitempty"`
|
||||
Role Role `json:"role"`
|
||||
Parts []Part `json:"parts"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
ID ID `json:"id"`
|
||||
ConversationID ID `json:"conversationId"`
|
||||
RunID ID `json:"runId,omitempty"`
|
||||
Role Role `json:"role"`
|
||||
Parts []Part `json:"parts"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
// Excluded 为真时界面仍显示,但组装上下文时整条(及同一 run 的成组消息)不送入模型。
|
||||
Excluded bool `json:"excluded,omitempty"`
|
||||
// VariantGroup 把同一轮的候选回复绑在一起。只把未排除的那条送入模型。
|
||||
VariantGroup string `json:"variantGroup,omitempty"`
|
||||
VariantIndex int `json:"variantIndex,omitempty"`
|
||||
}
|
||||
|
||||
// Part 是判别联合:Type 决定哪些字段有意义;未用字段为零值。
|
||||
@@ -80,25 +94,30 @@ type Part struct {
|
||||
Artifact *ArtifactRef `json:"artifact,omitempty"`
|
||||
Hitl *HITLSummary `json:"hitl,omitempty"`
|
||||
Plan *PlanSnapshot `json:"plan,omitempty"`
|
||||
Card *CardPart `json:"card,omitempty"`
|
||||
Card *CardPart `json:"card,omitempty"`
|
||||
Err *ErrorPart `json:"error,omitempty"`
|
||||
Check *CheckPart `json:"check,omitempty"`
|
||||
|
||||
// 过程标记:true 的部件在 run 终态后由前端折叠为摘要行(4.2)。
|
||||
Process bool `json:"process,omitempty"`
|
||||
|
||||
// DurationMs 是 thinking 部件的思考时长(毫秒,从首个推理增量到最后一个)。
|
||||
// 工具耗时在 ToolCallPart.Duration,不复用本字段。
|
||||
DurationMs float64 `json:"durationMs,omitempty"`
|
||||
}
|
||||
|
||||
type ToolCallPart struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Arguments string `json:"arguments,omitempty"` // 原始 JSON
|
||||
Duration float64 `json:"durationMs,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Arguments string `json:"arguments,omitempty"` // 原始 JSON
|
||||
Duration float64 `json:"durationMs,omitempty"`
|
||||
}
|
||||
|
||||
type ToolResultPart struct {
|
||||
CallID string `json:"callId"`
|
||||
Status string `json:"status"` // ok | error
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Truncated bool `json:"truncated,omitempty"` // 超长截断并转工件引用(4.10)
|
||||
CallID string `json:"callId"`
|
||||
Status string `json:"status"` // ok | error
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Truncated bool `json:"truncated,omitempty"` // 超长截断并转工件引用(4.10)
|
||||
}
|
||||
|
||||
type ArtifactRef struct {
|
||||
@@ -119,10 +138,19 @@ type ErrorPart struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// CheckPart 是终轮自动检查的结果。过程区显示一行,不进入给用户的回复。
|
||||
type CheckPart struct {
|
||||
Passed bool `json:"passed"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
}
|
||||
|
||||
// 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)----
|
||||
@@ -138,39 +166,40 @@ const (
|
||||
type RunStatus string
|
||||
|
||||
const (
|
||||
RunPending RunStatus = "pending"
|
||||
RunRunning RunStatus = "running"
|
||||
RunWaitingHITL RunStatus = "waiting_hitl"
|
||||
RunCompleted RunStatus = "completed"
|
||||
RunFailed RunStatus = "failed"
|
||||
RunCancelled RunStatus = "cancelled"
|
||||
RunInterrupted RunStatus = "interrupted"
|
||||
RunPending RunStatus = "pending"
|
||||
RunRunning RunStatus = "running"
|
||||
RunWaitingHITL RunStatus = "waiting_hitl"
|
||||
RunCompleted RunStatus = "completed"
|
||||
RunFailed RunStatus = "failed"
|
||||
RunCancelled RunStatus = "cancelled"
|
||||
RunInterrupted RunStatus = "interrupted"
|
||||
)
|
||||
|
||||
type RunPhase string
|
||||
|
||||
const (
|
||||
PhaseThinking RunPhase = "thinking"
|
||||
PhaseActing RunPhase = "acting"
|
||||
PhaseThinking RunPhase = "thinking"
|
||||
PhaseActing RunPhase = "acting"
|
||||
PhaseResponding RunPhase = "responding"
|
||||
PhaseDone RunPhase = "done"
|
||||
PhaseFinalizing RunPhase = "finalizing" // 工作循环已结束,正在整理给用户的最终回复
|
||||
PhaseDone RunPhase = "done"
|
||||
)
|
||||
|
||||
type Run struct {
|
||||
ID ID `json:"id"`
|
||||
ParentRunID ID `json:"parentRunId,omitempty"`
|
||||
ConversationID ID `json:"conversationId"`
|
||||
ProjectID ID `json:"projectId"`
|
||||
Owner ID `json:"owner,omitempty"` // 冗余自会话
|
||||
Kind RunKind `json:"kind"`
|
||||
ProfileID ID `json:"profileId,omitempty"`
|
||||
Status RunStatus `json:"status"`
|
||||
Phase RunPhase `json:"phase,omitempty"`
|
||||
AgentName string `json:"agentName,omitempty"` // subagent 显示名
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
EndedAt *time.Time `json:"endedAt,omitempty"`
|
||||
Usage *Usage `json:"usage,omitempty"`
|
||||
Error *ErrorPart `json:"error,omitempty"`
|
||||
ID ID `json:"id"`
|
||||
ParentRunID ID `json:"parentRunId,omitempty"`
|
||||
ConversationID ID `json:"conversationId"`
|
||||
ProjectID ID `json:"projectId"`
|
||||
Owner ID `json:"owner,omitempty"` // 冗余自会话
|
||||
Kind RunKind `json:"kind"`
|
||||
ProfileID ID `json:"profileId,omitempty"`
|
||||
Status RunStatus `json:"status"`
|
||||
Phase RunPhase `json:"phase,omitempty"`
|
||||
AgentName string `json:"agentName,omitempty"` // subagent 显示名
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
EndedAt *time.Time `json:"endedAt,omitempty"`
|
||||
Usage *Usage `json:"usage,omitempty"`
|
||||
Error *ErrorPart `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type Usage struct {
|
||||
@@ -179,6 +208,8 @@ type Usage struct {
|
||||
TotalTokens int `json:"totalTokens"`
|
||||
DurationMs float64 `json:"durationMs"`
|
||||
ToolCalls int `json:"toolCalls"`
|
||||
// Model 是实际给出这段用量的模型。空表示当时没有记录。
|
||||
Model string `json:"model,omitempty"`
|
||||
}
|
||||
|
||||
// ---- 工件 ----
|
||||
@@ -199,20 +230,20 @@ type Artifact struct {
|
||||
// ---- 计划与待办(右栏 Plan / Todos)----
|
||||
|
||||
type PlanSnapshot struct {
|
||||
PlanID ID `json:"planId"`
|
||||
Title string `json:"title"`
|
||||
Steps []PlanStep `json:"steps"`
|
||||
PlanID ID `json:"planId"`
|
||||
Title string `json:"title"`
|
||||
Steps []PlanStep `json:"steps"`
|
||||
}
|
||||
|
||||
type PlanStep struct {
|
||||
ID string `json:"id"`
|
||||
Text string `json:"text"`
|
||||
Status string `json:"status"` // pending | in_progress | done | skipped
|
||||
AgentRun ID `json:"agentRun,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Text string `json:"text"`
|
||||
Status string `json:"status"` // pending | in_progress | done | skipped
|
||||
AgentRun ID `json:"agentRun,omitempty"`
|
||||
}
|
||||
|
||||
type TodoList struct {
|
||||
ConversationID ID `json:"conversationId"`
|
||||
ConversationID ID `json:"conversationId"`
|
||||
Items []TodoItem `json:"items"`
|
||||
}
|
||||
|
||||
|
||||
@@ -8,59 +8,79 @@ import "encoding/json"
|
||||
const PluginAPIVersion = "1.0"
|
||||
|
||||
type PluginManifest struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
APIVersion string `json:"apiVersion"`
|
||||
Requires PluginRequires `json:"requires,omitempty"`
|
||||
Provides PluginProvides `json:"provides"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
APIVersion string `json:"apiVersion"`
|
||||
Requires PluginRequires `json:"requires,omitempty"`
|
||||
Provides PluginProvides `json:"provides"`
|
||||
Scope PluginScope `json:"scope,omitzero"`
|
||||
Backend PluginBackend `json:"backend,omitzero"`
|
||||
}
|
||||
|
||||
type PluginRequires struct {
|
||||
Network []string `json:"network,omitempty"` // 允许访问的域名(展示用,v1 自装自审)
|
||||
Network []string `json:"network,omitempty"` // 出站域名白名单;v2 空名单拒绝出站,v1 空名单保持放行
|
||||
}
|
||||
|
||||
type PluginProvides struct {
|
||||
Tools []string `json:"tools,omitempty"` // tools/ 目录下的资产名
|
||||
Workflows []string `json:"workflows,omitempty"`
|
||||
Intents []string `json:"intents,omitempty"`
|
||||
Prompts []string `json:"prompts,omitempty"`
|
||||
Panels []string `json:"panels,omitempty"`
|
||||
Cards []string `json:"cards,omitempty"`
|
||||
Settings []string `json:"settings,omitempty"`
|
||||
Commands []string `json:"commands,omitempty"` // commands/ 资产名(spec 权威在 Agent-Command 库)
|
||||
Tools []string `json:"tools,omitempty"` // tools/ 目录下的资产名
|
||||
Workflows []string `json:"workflows,omitempty"`
|
||||
Intents []string `json:"intents,omitempty"`
|
||||
Prompts []string `json:"prompts,omitempty"`
|
||||
Panels []string `json:"panels,omitempty"`
|
||||
Cards []string `json:"cards,omitempty"`
|
||||
Settings []string `json:"settings,omitempty"`
|
||||
Commands []string `json:"commands,omitempty"` // commands/ 资产名(spec 权威在 Agent-Command 库)
|
||||
Workspaces []string `json:"workspaces,omitempty"` // workspaces/ 资产名(工作区弹层)
|
||||
Skills []string `json:"skills,omitempty"`
|
||||
MCPServers []string `json:"mcpServers,omitempty"`
|
||||
Knowledge []string `json:"knowledge,omitempty"`
|
||||
Parsers []string `json:"parsers,omitempty"`
|
||||
Agents []string `json:"agents,omitempty"`
|
||||
Channels []string `json:"channels,omitempty"`
|
||||
Pages []string `json:"pages,omitempty"`
|
||||
}
|
||||
|
||||
// PluginPanel:右栏面板声明(UI schema 静态骨架 + 可选数据提供者)。
|
||||
type PluginPanel struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Body []UINode `json:"body"`
|
||||
// Tool 绑定:面板挂载时调用该工具刷新 Body 中的动态区(v1 简化为整体刷新)。
|
||||
RefreshTool string `json:"refreshTool,omitempty"`
|
||||
}
|
||||
|
||||
// WorkflowDef:工作流即数据(4.6)。Step 可产 HITL 卡片、调工具、产出工件。
|
||||
// 模板占位符:{{input}}、{{slots.<key>}}、{{steps.<id>.output}}。
|
||||
type WorkflowDef struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description,omitempty"`
|
||||
IntentID string `json:"intentId,omitempty"` // 绑定意图(命中即启动)
|
||||
Steps []WorkflowStep `json:"steps"`
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description,omitempty"`
|
||||
IntentID string `json:"intentId,omitempty"` // 绑定意图(命中即启动)
|
||||
MaxRetries int `json:"maxRetries,omitempty"` // 整条工作流的 critic 重试上限
|
||||
Steps []WorkflowStep `json:"steps"`
|
||||
}
|
||||
|
||||
// StepCritic:单步验收。无法解析的判定按不通过处理。
|
||||
type StepCritic struct {
|
||||
Criteria string `json:"criteria"`
|
||||
MaxRetries int `json:"maxRetries,omitempty"` // 本步重试上限;0 表示缺省 1 次
|
||||
}
|
||||
|
||||
type WorkflowStep struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Kind string `json:"kind"` // prompt | tool | hitl | artifact | card
|
||||
Prompt string `json:"prompt,omitempty"` // prompt: 发给模型的指令模板
|
||||
Tool string `json:"tool,omitempty"` // tool: 工具名
|
||||
Args json.RawMessage `json:"args,omitempty"` // tool: 参数模板
|
||||
Hitl *HITLCardTemplate `json:"hitl,omitempty"` // hitl: 卡片模板
|
||||
Artifact string `json:"artifact,omitempty"` // artifact: 产出工件名模板
|
||||
Card *ActivityCardTemplate `json:"card,omitempty"` // card: 产收件箱任务卡
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Kind string `json:"kind"` // prompt | tool | hitl | artifact | card | script
|
||||
Prompt string `json:"prompt,omitempty"` // prompt: 发给模型的指令模板
|
||||
Agent string `json:"agent,omitempty"` // prompt: 子代理 profile,缺省 general
|
||||
Tool string `json:"tool,omitempty"` // tool: 工具名
|
||||
Args json.RawMessage `json:"args,omitempty"` // tool: 参数模板
|
||||
Hitl *HITLCardTemplate `json:"hitl,omitempty"` // hitl: 卡片模板
|
||||
Artifact string `json:"artifact,omitempty"` // artifact: 产出工件名模板
|
||||
Card *ActivityCardTemplate `json:"card,omitempty"` // card: 产收件箱任务卡
|
||||
Script string `json:"script,omitempty"` // script: 插件目录内的相对路径
|
||||
Critic *StepCritic `json:"critic,omitempty"`
|
||||
}
|
||||
|
||||
// ActivityCardTemplate:工作流步骤产卡模板(kind=card)——步骤执行时生成
|
||||
@@ -76,20 +96,20 @@ type ActivityCardTemplate struct {
|
||||
// PluginWorkspace:工作区弹层声明(深读与治理视图;Esc/遮罩关闭回对话现场,
|
||||
// 不承载任何唯一步骤——每个操作必须另有命令路径,提案 D31)。
|
||||
type PluginWorkspace struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Shortcut string `json:"shortcut,omitempty"` // "1"–"9"(⌘N / Ctrl+N 召出)
|
||||
BadgeTool string `json:"badgeTool,omitempty"` // 角标提供者工具:返回 {count,tone}
|
||||
Body []UINode `json:"body"`
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Shortcut string `json:"shortcut,omitempty"` // "1"–"9"(⌘N / Ctrl+N 召出)
|
||||
BadgeTool string `json:"badgeTool,omitempty"` // 角标提供者工具:返回 {count,tone}
|
||||
Body []UINode `json:"body"`
|
||||
// RefreshTool:挂载时调用刷新 Body 动态区(与 Panel 同机制,v1 整体刷新)。
|
||||
RefreshTool string `json:"refreshTool,omitempty"`
|
||||
}
|
||||
|
||||
type HITLCardTemplate struct {
|
||||
Question string `json:"question"`
|
||||
Kind HITLKind `json:"kind"`
|
||||
Choices []HITLChoice `json:"choices,omitempty"`
|
||||
AllowFreeInput bool `json:"allowFreeInput"`
|
||||
FreeInputHint string `json:"freeInputHint,omitempty"`
|
||||
Question string `json:"question"`
|
||||
Kind HITLKind `json:"kind"`
|
||||
Choices []HITLChoice `json:"choices,omitempty"`
|
||||
AllowFreeInput bool `json:"allowFreeInput"`
|
||||
FreeInputHint string `json:"freeInputHint,omitempty"`
|
||||
}
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package core
|
||||
|
||||
// PluginAPIVersionExperimental 是插件契约 v2 的实验窗口。
|
||||
// 1.0 清单继续有效;v2 只增加可选字段,不收紧 1.0 已有资产。
|
||||
const PluginAPIVersionExperimental = "2.0-experimental"
|
||||
|
||||
// PluginAPIAccepted 报告宿主是否接受该 apiVersion。空值由宿主填成 PluginAPIVersion。
|
||||
func PluginAPIAccepted(v string) bool {
|
||||
switch v {
|
||||
case PluginAPIVersion, PluginAPIVersionExperimental:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// PluginScope 限制插件对哪些租户/项目可见。两者都空 = 全部可见。
|
||||
type PluginScope struct {
|
||||
Tenants []string `json:"tenants,omitempty"`
|
||||
Projects []string `json:"projects,omitempty"`
|
||||
}
|
||||
|
||||
// Allows 判断 owner/project 是否落在作用域内。
|
||||
func (s PluginScope) Allows(owner, project string) bool {
|
||||
if len(s.Tenants) > 0 && !scopeContains(s.Tenants, owner) {
|
||||
return false
|
||||
}
|
||||
if len(s.Projects) > 0 && !scopeContains(s.Projects, project) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func scopeContains(xs []string, v string) bool {
|
||||
for _, x := range xs {
|
||||
if x == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PluginBackend 声明进程内或 sidecar 代码档。SchemaVersion 是插件自有数据的迁移版本。
|
||||
type PluginBackend struct {
|
||||
Mode string `json:"mode,omitempty"` // inproc | sidecar
|
||||
Command string `json:"command,omitempty"`
|
||||
Args []string `json:"args,omitempty"`
|
||||
SchemaVersion int `json:"schemaVersion,omitempty"`
|
||||
}
|
||||
|
||||
// PluginSkill 是 skills/<name>.md 的装载结果(SKILL.md 正文)。
|
||||
type PluginSkill struct {
|
||||
Name string `json:"name"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
// PluginMCPServer 是 mcpServers/<name>.json,字段与 agent-native MCP ServerConfig 对齐。
|
||||
type PluginMCPServer struct {
|
||||
Name string `json:"name"`
|
||||
Command string `json:"command,omitempty"`
|
||||
Args []string `json:"args,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
// PluginKnowledgeSet 是知识数据集声明(数据在插件侧,检索引擎在基座)。
|
||||
type PluginKnowledgeSet struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// PluginParserDecl 是解析器声明。实现可以是内置名或 sidecar 工具名。
|
||||
type PluginParserDecl struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
MIME string `json:"mime,omitempty"`
|
||||
Tool string `json:"tool,omitempty"`
|
||||
}
|
||||
|
||||
// PluginAgentDecl 是画像/子代理声明。
|
||||
type PluginAgentDecl struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Instructions string `json:"instructions,omitempty"`
|
||||
Tools []string `json:"tools,omitempty"`
|
||||
}
|
||||
|
||||
// PluginChannelDecl 是渠道适配器声明(飞书等由下游实现,基座只登记入口)。
|
||||
type PluginChannelDecl struct {
|
||||
ID string `json:"id"`
|
||||
Kind string `json:"kind"`
|
||||
Title string `json:"title,omitempty"`
|
||||
}
|
||||
|
||||
// PluginPageDecl 是前端 ESM 页面。Module 相对插件目录或绝对 URL。
|
||||
type PluginPageDecl struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Module string `json:"module"`
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package core
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPluginAPIAccepted(t *testing.T) {
|
||||
if !PluginAPIAccepted(PluginAPIVersion) || !PluginAPIAccepted(PluginAPIVersionExperimental) {
|
||||
t.Fatal("current and experimental windows must be accepted")
|
||||
}
|
||||
if PluginAPIAccepted("0.9") || PluginAPIAccepted("") {
|
||||
t.Fatal("unknown or empty apiVersion must be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginScopeAllows(t *testing.T) {
|
||||
open := PluginScope{}
|
||||
if !open.Allows("a", "p") {
|
||||
t.Fatal("empty scope is visible to everyone")
|
||||
}
|
||||
scoped := PluginScope{Tenants: []string{"acme"}, Projects: []string{"p1"}}
|
||||
if !scoped.Allows("acme", "p1") {
|
||||
t.Fatal("matching tenant and project should pass")
|
||||
}
|
||||
if scoped.Allows("other", "p1") || scoped.Allows("acme", "p2") {
|
||||
t.Fatal("scope must reject mismatches")
|
||||
}
|
||||
}
|
||||
@@ -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"`
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
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"` // 用户停用
|
||||
// 子代理 profile。只对 agent.sub.* 有意义;空字段表示沿用内置。
|
||||
Tools []string `json:"tools,omitempty"`
|
||||
Model string `json:"model,omitempty"` // lightweight | chat
|
||||
MaxTurns int `json:"maxTurns,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"`
|
||||
}
|
||||
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
+15
-1
@@ -7,7 +7,8 @@
|
||||
{
|
||||
"type": "thinking",
|
||||
"text": "考虑先检索…",
|
||||
"process": true
|
||||
"process": true,
|
||||
"durationMs": 1500
|
||||
},
|
||||
{
|
||||
"type": "tool_call",
|
||||
@@ -39,6 +40,19 @@
|
||||
"name": "报告.md",
|
||||
"kind": "document"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"card": {
|
||||
"kind": "result",
|
||||
"data": [
|
||||
{
|
||||
"type": "markdown",
|
||||
"markdown": "全文见卡片"
|
||||
}
|
||||
],
|
||||
"cardId": "card_abc123def456"
|
||||
}
|
||||
}
|
||||
],
|
||||
"createdAt": "0001-01-01T00:00:00Z"
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "agent.sub.explore",
|
||||
"layer": "agent",
|
||||
"version": 1,
|
||||
"title": "探索子代理",
|
||||
"description": "",
|
||||
"body": "只读检索。",
|
||||
"tools": [
|
||||
"knowledge_search",
|
||||
"web_search"
|
||||
],
|
||||
"model": "lightweight",
|
||||
"maxTurns": 8
|
||||
}
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
[
|
||||
{
|
||||
"type": "avatar",
|
||||
"avatar": {
|
||||
"name": "weichen",
|
||||
"sub": "OWNER"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "avatar",
|
||||
"avatar": {
|
||||
"stack": [
|
||||
{
|
||||
"name": "甲",
|
||||
"tone": "ok"
|
||||
},
|
||||
{
|
||||
"name": "乙",
|
||||
"tone": "warn"
|
||||
},
|
||||
{
|
||||
"name": "丙"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
Vendored
+69
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"id": "demo.brief",
|
||||
"title": "简报",
|
||||
"description": "按主题写一份简报",
|
||||
"intentId": "demo.brief",
|
||||
"maxRetries": 6,
|
||||
"steps": [
|
||||
{
|
||||
"id": "length",
|
||||
"title": "篇幅",
|
||||
"kind": "hitl",
|
||||
"hitl": {
|
||||
"question": "篇幅?",
|
||||
"kind": "choice",
|
||||
"choices": [
|
||||
{
|
||||
"id": "short",
|
||||
"label": "短",
|
||||
"description": "一页",
|
||||
"impact": "更快"
|
||||
}
|
||||
],
|
||||
"allowFreeInput": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "draft",
|
||||
"title": "起草",
|
||||
"kind": "prompt",
|
||||
"prompt": "写一份关于 {{slots.topic}} 的简报。用户说:{{input}}。上一步:{{steps.length.output}}",
|
||||
"agent": "research",
|
||||
"critic": {
|
||||
"criteria": "必须点明主题",
|
||||
"maxRetries": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "lookup",
|
||||
"title": "查时间",
|
||||
"kind": "tool",
|
||||
"tool": "time_now",
|
||||
"args": {
|
||||
"tz": "UTC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "save",
|
||||
"title": "存稿",
|
||||
"kind": "artifact",
|
||||
"prompt": "{{steps.draft.output}}",
|
||||
"artifact": "brief.md"
|
||||
},
|
||||
{
|
||||
"id": "note",
|
||||
"title": "通知",
|
||||
"kind": "card",
|
||||
"card": {
|
||||
"kind": "result",
|
||||
"title": "简报已写好"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "run",
|
||||
"title": "脚本",
|
||||
"kind": "script",
|
||||
"script": "scripts/demo.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,16 +20,23 @@ 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"`
|
||||
// ReadOnly、Edits、Risk 是工具注册时的权限声明。Risk 取 low、medium、high。
|
||||
ReadOnly bool `json:"readOnly,omitempty"`
|
||||
Edits bool `json:"edits,omitempty"`
|
||||
Risk string `json:"risk,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPToolSpec:无代码自定义工具(4.8)。模板变量 {{param.x}} 从调用参数注入。
|
||||
type HTTPToolSpec struct {
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
BodyTemplate string `json:"bodyTemplate,omitempty"` // JSON 模板
|
||||
AuthRef string `json:"authRef,omitempty"` // 设置中的密钥引用(settings key)
|
||||
TimeoutMs int `json:"timeoutMs,omitempty"`
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
BodyTemplate string `json:"bodyTemplate,omitempty"` // JSON 模板
|
||||
AuthRef string `json:"authRef,omitempty"` // 设置中的密钥引用(settings key)
|
||||
TimeoutMs int `json:"timeoutMs,omitempty"`
|
||||
// ResultPath 从响应 JSON 中裁剪(jq 点路径);空取整个响应。
|
||||
ResultPath string `json:"resultPath,omitempty"`
|
||||
}
|
||||
@@ -41,9 +48,9 @@ type ToolCallRequest struct {
|
||||
}
|
||||
|
||||
type ToolCallResult struct {
|
||||
CallID string `json:"callId"`
|
||||
Status string `json:"status"` // ok | error
|
||||
Output string `json:"output,omitempty"`
|
||||
Truncated bool `json:"truncated,omitempty"`
|
||||
Artifact *Artifact `json:"artifact,omitempty"` // 超长转工件
|
||||
CallID string `json:"callId"`
|
||||
Status string `json:"status"` // ok | error
|
||||
Output string `json:"output,omitempty"`
|
||||
Truncated bool `json:"truncated,omitempty"`
|
||||
Artifact *Artifact `json:"artifact,omitempty"` // 超长转工件
|
||||
}
|
||||
|
||||
+91
-6
@@ -13,6 +13,10 @@ export interface Project {
|
||||
|
||||
export interface Conversation {
|
||||
id: ID; projectId: ID; owner?: ID; title: string; profileId?: ID;
|
||||
pinned?: boolean; archived?: boolean;
|
||||
allowCrossRead?: boolean; crossPrivate?: boolean;
|
||||
permissionMode?: 'default' | 'plan' | 'auto_edit' | '';
|
||||
sessionNote?: string;
|
||||
createdAt: string; updatedAt: string;
|
||||
}
|
||||
|
||||
@@ -20,7 +24,7 @@ export type Role = 'user' | 'assistant' | 'system';
|
||||
|
||||
export type PartType =
|
||||
| 'text' | 'thinking' | 'tool_call' | 'tool_result' | 'artifact_ref'
|
||||
| 'hitl' | 'plan' | 'error' | 'card';
|
||||
| 'hitl' | 'plan' | 'error' | 'card' | 'check';
|
||||
|
||||
export interface Part {
|
||||
type: PartType;
|
||||
@@ -30,25 +34,31 @@ 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 };
|
||||
check?: { passed: boolean; reason?: string }; // 终轮自动检查,过程区一行
|
||||
process?: boolean; // run 终态后折叠为摘要行
|
||||
durationMs?: number; // thinking 部件的思考时长(毫秒);工具耗时在 toolCall.durationMs
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
id: ID; conversationId: ID; runId?: ID; role: Role;
|
||||
parts: Part[]; createdAt: string;
|
||||
excluded?: boolean;
|
||||
variantGroup?: string;
|
||||
variantIndex?: number;
|
||||
}
|
||||
|
||||
export type RunKind = 'conversation' | 'subagent' | 'tool';
|
||||
export type RunStatus =
|
||||
| 'pending' | 'running' | 'waiting_hitl' | 'completed'
|
||||
| 'failed' | 'cancelled' | 'interrupted';
|
||||
export type RunPhase = 'thinking' | 'acting' | 'responding' | 'done';
|
||||
export type RunPhase = 'thinking' | 'acting' | 'responding' | 'finalizing' | 'done';
|
||||
|
||||
export interface Usage {
|
||||
promptTokens: number; completionTokens: number; totalTokens: number;
|
||||
durationMs: number; toolCalls: number;
|
||||
model?: string;
|
||||
}
|
||||
|
||||
export interface Run {
|
||||
@@ -76,8 +86,9 @@ export interface TodoList {
|
||||
// ---- 流式事件 ----
|
||||
|
||||
export type StreamEventType =
|
||||
| 'message.delta' | 'thinking.delta' | 'tool.call' | 'tool.result'
|
||||
| 'run.phase' | 'hitl.request' | 'hitl.resolved' | 'plan.update' | 'todo.update'
|
||||
| 'message.delta' | 'thinking.delta'
|
||||
| 'tool.call.delta' | 'tool.call' | 'tool.output' | 'tool.result'
|
||||
| 'run.phase' | 'run.check' | 'hitl.request' | 'hitl.resolved' | 'plan.update' | 'todo.update'
|
||||
| 'agent.spawn' | 'agent.progress' | 'agent.result'
|
||||
| 'artifact.created' | 'artifact.updated'
|
||||
| 'run.completed' | 'run.failed' | 'error'
|
||||
@@ -90,6 +101,9 @@ export interface StreamEvent<P = unknown> {
|
||||
|
||||
// payload 形状(按 type 判别;未知字段忽略)
|
||||
export interface MessageDeltaPayload { index: number; text: string }
|
||||
export interface ToolCallDeltaPayload { index: number; callId?: string; name?: string; delta: string }
|
||||
export interface ToolOutputPayload { callId: string; stream: 'stdout' | 'stderr'; text: string }
|
||||
export interface ToolResultPayload { callId: string; status: string; summary?: string; truncated?: boolean; durationMs?: number }
|
||||
export interface AgentSpawnPayload { agentRun: ID; name: string; task: string }
|
||||
export interface AgentProgressPayload { agentRun: ID; note?: string; phase?: RunPhase }
|
||||
export interface AgentResultPayload { agentRun: ID; status: RunStatus; summary?: string }
|
||||
@@ -99,11 +113,19 @@ export interface RunCompletedPayload {
|
||||
context?: ContextObservation
|
||||
}
|
||||
|
||||
interface ContextSection {
|
||||
name: string
|
||||
estTokens: number
|
||||
}
|
||||
|
||||
export interface ContextObservation {
|
||||
items: string[]
|
||||
truncations?: string[]
|
||||
estChars: number
|
||||
sections?: ContextSection[]
|
||||
prefixHash?: string
|
||||
}
|
||||
export interface RunCheckPayload { passed: boolean; reason?: string }
|
||||
export interface RunFailedPayload { code: string; message: string }
|
||||
|
||||
// ---- 收件箱卡片 ----
|
||||
@@ -141,6 +163,7 @@ export interface HITLCard {
|
||||
choices?: HITLChoice[]; allowFreeInput: boolean; freeInputHint?: string;
|
||||
timeoutSec?: number | null; form?: UINode[]; createdAt: string; resolved: boolean;
|
||||
anchorMessageId?: ID; blocked?: boolean; blockReason?: string;
|
||||
detail?: string;
|
||||
}
|
||||
|
||||
export interface HITLResponse {
|
||||
@@ -151,7 +174,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 +188,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 +215,34 @@ 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;
|
||||
/** 子代理 profile。只对 agent.sub.* 有意义;空字段表示沿用内置。 */
|
||||
tools?: string[]; model?: string; maxTurns?: number;
|
||||
}
|
||||
|
||||
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: {
|
||||
@@ -223,18 +270,56 @@ export interface PluginManifest {
|
||||
tools?: string[]; workflows?: string[]; intents?: string[];
|
||||
prompts?: string[]; panels?: string[]; cards?: string[]; settings?: string[];
|
||||
commands?: string[]; workspaces?: string[];
|
||||
skills?: string[]; mcpServers?: string[]; knowledge?: string[];
|
||||
parsers?: string[]; agents?: string[]; channels?: string[]; pages?: string[];
|
||||
};
|
||||
scope?: { tenants?: string[]; projects?: string[] };
|
||||
backend?: { mode?: 'inproc' | 'sidecar'; command?: string; args?: string[]; schemaVersion?: number };
|
||||
}
|
||||
|
||||
export interface PluginPanel {
|
||||
id: string; title: string; icon?: string; body: UINode[]; refreshTool?: string;
|
||||
}
|
||||
|
||||
/** 模板占位符:{{input}}、{{slots.<key>}}、{{steps.<id>.output}}。 */
|
||||
export interface WorkflowDef {
|
||||
id: string; title: string; description?: string; intentId?: string;
|
||||
/** 整条工作流的 critic 重试上限。 */
|
||||
maxRetries?: number;
|
||||
steps: WorkflowStep[];
|
||||
}
|
||||
|
||||
export interface StepCritic {
|
||||
criteria: string;
|
||||
/** 本步重试上限;缺省 1 次。 */
|
||||
maxRetries?: number;
|
||||
}
|
||||
|
||||
export interface WorkflowStep {
|
||||
id: string; title: string;
|
||||
kind: 'prompt' | 'tool' | 'hitl' | 'artifact' | 'card' | 'script';
|
||||
prompt?: string;
|
||||
/** prompt 步骤的子代理 profile,缺省 general。 */
|
||||
agent?: string;
|
||||
tool?: string;
|
||||
args?: unknown;
|
||||
hitl?: { question: string; kind: HITLKind; choices?: HITLChoice[]; allowFreeInput: boolean; freeInputHint?: string };
|
||||
artifact?: string;
|
||||
card?: { kind: CardKind; title: string; tone?: string; body?: UINode[]; actions?: { id: string; label: string; style?: 'primary' | 'default' | 'danger'; command?: string }[] };
|
||||
/** script 步骤在插件目录内的相对路径。 */
|
||||
script?: string;
|
||||
critic?: StepCritic;
|
||||
}
|
||||
|
||||
export interface PluginWorkspace {
|
||||
id: string; title: string; icon?: string; shortcut?: string;
|
||||
badgeTool?: string; body: UINode[]; refreshTool?: string;
|
||||
}
|
||||
|
||||
export interface PluginPageDecl {
|
||||
id: string; title: string; module: string;
|
||||
}
|
||||
|
||||
// ---- 摄取 / 检索 ----
|
||||
|
||||
export interface ParsedDocument {
|
||||
|
||||
@@ -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;空则降级为发消息
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user