Merge pull request 'Add a finalizing phase and a check part' (#18) from feature/final-reply-compose into main

This commit was merged in pull request #18.
This commit is contained in:
2026-10-02 14:15:05 +00:00
3 changed files with 85 additions and 67 deletions
+7
View File
@@ -43,6 +43,7 @@ const (
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"
@@ -112,6 +113,12 @@ 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"`
+73 -64
View File
@@ -17,26 +17,26 @@ 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"` // 跨会话入站:私有会话,不被他读、不出现在他方列表
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// ---- 消息与部件 ----
@@ -61,16 +61,17 @@ 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"`
}
// Part 是判别联合:Type 决定哪些字段有意义;未用字段为零值。
@@ -84,8 +85,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 +98,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 +129,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 +157,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 {
@@ -210,20 +219,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"`
}
+5 -3
View File
@@ -22,7 +22,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 +34,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
}
@@ -47,7 +48,7 @@ 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;
@@ -81,7 +82,7 @@ export interface TodoList {
export type StreamEventType =
| 'message.delta' | 'thinking.delta'
| 'tool.call.delta' | 'tool.call' | 'tool.output' | 'tool.result'
| 'run.phase' | 'hitl.request' | 'hitl.resolved' | 'plan.update' | 'todo.update'
| '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'
@@ -111,6 +112,7 @@ export interface ContextObservation {
truncations?: string[]
estChars: number
}
export interface RunCheckPayload { passed: boolean; reason?: string }
export interface RunFailedPayload { code: string; message: string }
// ---- 收件箱卡片 ----