diff --git a/events.go b/events.go index 4a688bd..1179ee9 100644 --- a/events.go +++ b/events.go @@ -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"` diff --git a/model.go b/model.go index dbf0365..132fd40 100644 --- a/model.go +++ b/model.go @@ -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"` } diff --git a/ts/types.ts b/ts/types.ts index f0b3dd6..067b6f1 100644 --- a/ts/types.ts +++ b/ts/types.ts @@ -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 } // ---- 收件箱卡片 ----