// 原 contract 包并入 exec。与本文件同名的执行门面仍是 Request、Result、Backend; // 设计契约里冲突的四个名字加 Contract 前缀。 // // 本包是助手(Core)与小队(Squad)共用的唯一执行接口层:Core 注入具体 // ContractBackend 实现,Squad 只表达需求并使用已注入的后端。契约不依赖任何具体 // 内核、产品类型、数据库或配置包。 // // 语义不变量(实现方与消费方都必须遵守): // // 1. ExecutionID 在一次逻辑执行内唯一;步骤重试与受控 fallback 必须复用 // 同一个 ExecutionID,并共享预算与截止时间。 // 2. Outcome 取值 Succeeded/Failed/Canceled/Unknown 四态之一;Unknown 表示 // 结果不确定(可能已有副作用),绝不静默降级为 Succeeded 或 Failed。 // 3. 重试只有一个责任层:步骤级重试归调用方(Squad),传输级有界重试归 // adapter,循环 turn 上限归 runtime;不得多层重试产生重复写入。 // 4. 取消必须传播到模型请求、工具调用与子进程;不支持可靠取消的实现必须 // 报告 cancel_requested 并保持 Unknown,不得伪报 Canceled。 // 5. 原生执行失败且已有文件或外部动作、或结果 Unknown 时,禁止无条件换 // 后端重放;是否可重放由调用方依据 Error.SideEffects 判定。 // 6. 执行 transcript 与产品聊天历史不是同一账本;SessionScope 仅用于隔离 // 上下文,不授予跨会话可见性。 // 7. Workspace 的路径约束不是 OS 沙箱;执行不可信第三方工具需要调用方 // 提供的进程/容器隔离,不能由本契约推导。 package exec import ( "context" "time" ) // Capability 声明后端可验证的能力。解析器按任务需求选择实现;必须能力 // 缺失时拒绝执行,而不是静默降级到 Stub。 type Capability string const ( // CapText 表示能返回非空文本结果(对话、摘要等纯文本任务)。 CapText Capability = "text" // CapFiles 表示能按 Targets 交付文件候选产物(reply.md/output 流程)。 CapFiles Capability = "files" // CapStream 表示支持过程事件流(OnEvent 回调有实际内容)。 CapStream Capability = "stream" // CapTools 表示支持受控工具调用(受 ToolPolicy 约束)。 CapTools Capability = "tools" // CapCancel 表示支持可靠取消(取消传播到模型/工具/子进程)。 CapCancel Capability = "cancel" // CapResume 表示支持跨进程恢复执行(配合 ExecutionJournal 端口)。 CapResume Capability = "resume" // CapUsage 表示能报告 token 用量。 CapUsage Capability = "usage" // CapStructuredOutput 表示支持结构化(非纯文本)输出。 CapStructuredOutput Capability = "structured-output" ) // ToolPolicy 描述执行允许的工具范围。实现不得自行扩展;未知工具一律拒绝。 type ToolPolicy struct { // Profile 为策略名(workspace/reasoning/或产品注册的自定义名)。 Profile string `json:"profile"` // Allowlist 显式列出额外允许的工具名;与 Profile 取交集语义由实现声明, // 未声明时视为仅 Allowlist。 Allowlist []string `json:"allowlist,omitempty"` } // WorkspaceHandle 是调用方已授权的工作区句柄。Root 之外的路径访问必须失败。 // 它表达路径授权,不构成操作系统级隔离。 type WorkspaceHandle struct { // Root 为授权根目录的绝对路径(已归一化)。 Root string `json:"root"` // Writable 声明允许写入的相对路径集合;空表示由产品规则决定(兼容现状)。 Writable []string `json:"writable,omitempty"` } // SessionScope 隔离执行上下文:不同 tenant/project/session 的会话、transcript、 // 授权与预算互不可见。字段为产品侧标识,契约层只要求非空时保持隔离。 type SessionScope struct { Tenant string `json:"tenant,omitempty"` Project string `json:"project,omitempty"` Session string `json:"session,omitempty"` } // ContractBudget 约束一次执行(含共享 ExecutionID 的重试)。 type ContractBudget struct { // MaxPromptTokens/MaxCompletionTokens 为 0 时不限制。 MaxPromptTokens int `json:"max_prompt_tokens,omitempty"` MaxCompletionTokens int `json:"max_completion_tokens,omitempty"` // Deadline 为截止时间;零值表示无外部截止(仍受 ctx 控制)。 Deadline time.Time `json:"deadline,omitempty"` } // Message 是可选的历史上下文(多轮对话/延续任务)。实现可以忽略,但不得 // 跨 SessionScope 混入。 type Message struct { Role string `json:"role"` // user | assistant | tool | system Content string `json:"content"` } // ContractRequest 是一次授权执行的统一输入。字段为设计契约;现有 file-only 流程 // (Prompt+Instructions+Targets→reply.md)是其子集 facade。 type ContractRequest struct { // ExecutionID 由调用方生成,重试与受控 fallback 复用同值。 ExecutionID string `json:"execution_id"` // SessionScope 为隔离身份;可为零值(一次性任务)。 SessionScope SessionScope `json:"session_scope"` Prompt string `json:"prompt"` Instructions string `json:"instructions,omitempty"` // Context 为可选历史消息;实现不得将其与 Prompt 之外的数据混合。 Context []Message `json:"context,omitempty"` // Targets 为期望交付的文件相对路径(文件任务);纯文本任务可空。 Targets []string `json:"targets,omitempty"` // Workspace 为授权工作区;文件任务的读写都必须限制在 Root 内。 Workspace WorkspaceHandle `json:"workspace"` // ToolPolicy 为工具范围;空 Profile 等同 reasoning。 ToolPolicy ToolPolicy `json:"tool_policy"` // ContractBudget 为本次执行预算。 ContractBudget ContractBudget `json:"budget"` // Required 为必须满足的能力;解析器据此选择实现,缺失即拒绝。 Required []Capability `json:"required,omitempty"` // OnEvent 为可选过程事件回调(nil = 调用方不需要过程流)。 // 回调必须在 Execute 返回前完成;回调内不得阻塞。 OnEvent func(Event) `json:"-"` } // Outcome 是执行终态。Unknown 优先于乐观判定:凡无法确认结果(含进程崩溃、 // 传输中断后未收到终态)一律 Unknown,由调用方恢复。 type Outcome string const ( OutcomeSucceeded Outcome = "succeeded" OutcomeFailed Outcome = "failed" OutcomeCanceled Outcome = "canceled" OutcomeUnknown Outcome = "unknown" ) // EventKind 过程事件类型(与现有 ProcessEvent 投影兼容)。 type EventKind string const ( EventTextDelta EventKind = "text_delta" EventReasoningDelta EventKind = "reasoning_delta" EventToolCallDelta EventKind = "tool_call_delta" EventToolExecStart EventKind = "tool_execution_start" EventToolExecEnd EventKind = "tool_execution_end" EventMessageEnd EventKind = "message_end" ) // Event 是过程事件帧;消费方(Core)负责映射为 SSE/持久化事件。 type Event struct { Kind EventKind `json:"kind"` Text string `json:"text,omitempty"` // *_delta 文本 Tool string `json:"tool,omitempty"` // tool_* 工具名 Detail string `json:"detail,omitempty"` // 工具参数/结果摘要 } // SideEffects 描述失败时的副作用状态,决定是否允许重试/fallback 重放。 type SideEffects string const ( // SideEffectNone:确认未产生任何外部副作用(未派发或纯读取后失败)。 SideEffectNone SideEffects = "none" // SideEffectPossible:可能已有副作用(文件已写、外部工具已调用、 // 终态未知)。禁止无条件重放;恢复判断归调用方。 SideEffectPossible SideEffects = "possible" ) // ErrorCode 稳定错误分类。未知代码按 Unknown 处理。 type ErrorCode string const ( ErrCanceled ErrorCode = "agent.canceled" ErrDeadlineExceeded ErrorCode = "agent.deadline_exceeded" ErrBudgetExceeded ErrorCode = "agent.budget_exceeded" ErrProviderUnavailable ErrorCode = "agent.provider_unavailable" ErrProviderRejected ErrorCode = "agent.provider_rejected" ErrToolRejected ErrorCode = "agent.tool_rejected" ErrPathOutsideWorkspace ErrorCode = "agent.path_outside_workspace" ErrMissingCapability ErrorCode = "agent.missing_capability" ErrMissingDeliverable ErrorCode = "agent.missing_deliverable" ErrEmptyOutput ErrorCode = "agent.empty_output" ErrUnknownOutcome ErrorCode = "agent.unknown_outcome" ErrInternal ErrorCode = "agent.internal" ) // Error 是标准化执行错误。Retryable 仅表示传输级瞬时故障可由 adapter 有界 // 重试或调用方作为新步骤重试;与 SideEffects 独立判定。 type Error struct { Code ErrorCode `json:"code"` Reason string `json:"reason,omitempty"` Retryable bool `json:"retryable,omitempty"` SideEffects SideEffects `json:"side_effects"` } func (e *Error) Error() string { if e == nil { return "" } return string(e.Code) + ": " + e.Reason } // ErrorCode 返回规范码字符串。日志库用这个方法识别错误,因此 contract 不导入错误码模块。 func (e *Error) ErrorCode() string { if e == nil { return "" } return string(e.Code) } // ErrorFields 返回可写入日志的字段。执行标识由调用方随日志字段另传,不放进本结构。 func (e *Error) ErrorFields() map[string]any { if e == nil { return nil } fields := map[string]any{ "retryable": e.Retryable, } if e.Reason != "" { fields["reason"] = e.Reason } if e.SideEffects != "" { fields["side_effects"] = string(e.SideEffects) } return fields } // Usage 报告 token 用量;实现报告不了时保持零值,不猜测。 type Usage struct { PromptTokens int `json:"prompt_tokens,omitempty"` CompletionTokens int `json:"completion_tokens,omitempty"` } // ContractResult 是执行终态。Text 与 Files 依据任务可为空;Outcome 决定解释。 type ContractResult struct { Outcome Outcome `json:"outcome"` // Text 为文本结果(CapText 任务必填)。 Text string `json:"text,omitempty"` // Files 为文件候选产物(内容字节),由调用方决定正式落盘与提交。 Files map[string][]byte `json:"files,omitempty"` // Logs 为执行日志/过程摘要(兼容现有 ContractResult.Logs)。 Logs string `json:"logs,omitempty"` // Err 为 Failed/Canceled/Unknown 时的标准化错误;Succeeded 时为 nil。 Err *Error `json:"err,omitempty"` // Usage 为 token 用量。 Usage Usage `json:"usage"` } // BackendDescriptor 声明实现身份与可验证能力;前端只展示经授权的描述。 type BackendDescriptor struct { // Name 为规范后端名(如 agent-native、pi-agent)。规范名 agent-native; // pi-native 为兼容别名,注册表负责别名解析与弃用周期。 Name string `json:"name"` // Capabilities 为该实现声明且经验证的能力集合。 Capabilities []Capability `json:"capabilities"` // AdapterVersion 标识实现版本(内核或适配器),用于收据与回退审计。 AdapterVersion string `json:"adapter_version,omitempty"` } // Has 报告描述是否声明指定能力。 func (d BackendDescriptor) Has(c Capability) bool { for _, x := range d.Capabilities { if x == c { return true } } return false } // ContractBackend 是统一执行接口。实现必须: // - 在 ctx 取消时尽快返回(Canceled 或 Unknown); // - 不持有跨执行的可变会话状态(连接池等无状态资源可复用); // - 不自行扩大工具范围或读取 Workspace 之外的路径。 type ContractBackend interface { Descriptor() BackendDescriptor // Execute 执行一次授权调用。返回的 ContractResult 永不为 nil;ctx 错误映射为 // Outcome=Canceled/Unknown,不返回 Go error 表达业务终态。 Execute(ctx context.Context, req ContractRequest) (*ContractResult, error) } // Resolver 按名称与必需能力解析后端。未知名必须返回错误,生产路径不得 // 回落到 Stub。 type Resolver interface { Resolve(name string, required []Capability) (ContractBackend, error) }