Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4eae71864c | ||
|
|
0b5c7747a3 | ||
|
|
bd632236c2 | ||
|
|
a7db6a4d0f | ||
|
|
5dc190ee63 | ||
|
|
b7e5baf15c | ||
|
|
cf7df54874 | ||
|
|
88e481afcd | ||
|
|
7c66075ee9 | ||
|
|
3f7067fb2d | ||
|
|
f08a4a90c6 | ||
|
|
4d1123e45c | ||
|
|
f93f473a0e | ||
|
|
a8e81e078d |
@@ -207,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,
|
||||
|
||||
@@ -8,13 +8,13 @@ import (
|
||||
// 事件信封:后端 → 前端的唯一增量通道(SSE),持久化后转发,Seq 在会话内单调。
|
||||
// 消费方对未知 Type 静默忽略(原则 6 前向兼容)。
|
||||
// 项目流(D50):项目频道事件以伪会话键 "project:<projectId>" 复用同一机制
|
||||
//(独立 seq、持久化、断线重放),信封以 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
|
||||
TS string `json:"ts"` // RFC3339
|
||||
V int `json:"v"`
|
||||
Type StreamEventType `json:"type"`
|
||||
Payload json.RawMessage `json:"payload,omitempty"`
|
||||
@@ -38,9 +38,12 @@ func ProjectFromChannel(key ID) (ID, bool) {
|
||||
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"
|
||||
@@ -82,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"`
|
||||
@@ -115,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"`
|
||||
}
|
||||
@@ -138,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,26 +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"` // 空为默认画像
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// ---- 消息与部件 ----
|
||||
@@ -61,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 决定哪些字段有意义;未用字段为零值。
|
||||
@@ -84,8 +94,9 @@ 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"`
|
||||
@@ -96,17 +107,17 @@ type Part struct {
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -127,6 +138,12 @@ 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)。
|
||||
@@ -149,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 {
|
||||
@@ -190,6 +208,8 @@ type Usage struct {
|
||||
TotalTokens int `json:"totalTokens"`
|
||||
DurationMs float64 `json:"durationMs"`
|
||||
ToolCalls int `json:"toolCalls"`
|
||||
// Model 是实际给出这段用量的模型。空表示当时没有记录。
|
||||
Model string `json:"model,omitempty"`
|
||||
}
|
||||
|
||||
// ---- 工件 ----
|
||||
@@ -210,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"`
|
||||
}
|
||||
|
||||
|
||||
@@ -52,24 +52,35 @@ type PluginPanel struct {
|
||||
}
|
||||
|
||||
// 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"` // 绑定意图(命中即启动)
|
||||
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
|
||||
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)——步骤执行时生成
|
||||
|
||||
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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -23,16 +23,20 @@ type ToolDesc struct {
|
||||
// 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"`
|
||||
}
|
||||
@@ -44,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"` // 超长转工件
|
||||
}
|
||||
|
||||
+54
-4
@@ -15,6 +15,8 @@ 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;
|
||||
}
|
||||
|
||||
@@ -22,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;
|
||||
@@ -34,6 +36,7 @@ export interface Part {
|
||||
plan?: PlanSnapshot;
|
||||
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
|
||||
}
|
||||
@@ -41,17 +44,21 @@ export interface Part {
|
||||
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 {
|
||||
@@ -79,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'
|
||||
@@ -93,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 }
|
||||
@@ -102,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 }
|
||||
|
||||
// ---- 收件箱卡片 ----
|
||||
@@ -144,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 {
|
||||
@@ -261,6 +281,36 @@ 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;
|
||||
|
||||
Reference in New Issue
Block a user