149 lines
6.8 KiB
Go
149 lines
6.8 KiB
Go
package extension
|
||
|
||
import (
|
||
"context"
|
||
"time"
|
||
|
||
core "agent-system-core"
|
||
)
|
||
|
||
// Request 是进程内命令处理器收到的一次分发请求(cmdbus.Request 的子集)。
|
||
type Request struct {
|
||
ID string
|
||
Params map[string]any
|
||
Entrance string // button | card | slash | chat | api(归一)
|
||
Actor string
|
||
Owner string
|
||
ConversationID string
|
||
// OperationID is generated by the trusted host bus before this handler runs.
|
||
// Persist it with an actual domain commit/outbox; never treat it as a retry key.
|
||
OperationID string
|
||
// HostProjectID 本次分发所属基座项目(EXT-18 可选上下文;空=无项目
|
||
// 上下文——API 入口无对话或对话未关联项目时为零值)。下游按显式映射
|
||
// 解析(如 RDM rdm_host_project_map),不得据此替代自身授权判定。
|
||
HostProjectID string
|
||
}
|
||
|
||
// AdminProvider 是 EXT-18 可选能力接口:调用方平台管理角色查询。
|
||
// 通过对分发 Host 句柄做类型断言获取(断言失败=能力缺失,明确报错不降级)。
|
||
// 语义:IsAdmin() 返回本次分发主体(Actor)是否持基座平台管理角色。
|
||
// 下游不得将此作为唯一授权依据——须与自身权限体系组合(如 RDM KTD4 双层)。
|
||
type AdminProvider interface {
|
||
// IsAdmin 报告本次分发主体是否为基座平台管理员(accounts.is_admin)。
|
||
// 非 dispatch 上下文(EXT-11 scoped host)无 Actor 时恒返回 false。
|
||
IsAdmin() bool
|
||
}
|
||
|
||
// Handler 是下游注册的进程内命令处理器:每次分发由基座构造 owner 作用域的
|
||
// Host 句柄传入,处理器经句柄访问基座能力,不得自行越权持有 owner 凭证。
|
||
type Handler func(ctx context.Context, h Host, req Request) (any, error)
|
||
|
||
// Host 是基座在分发时构造的 owner 作用域宿主句柄(EXT-1 最小集 + EXT-5 解析)。
|
||
// 所有能力的可见范围都以本次分发的 Owner 为准;artifacts 只读
|
||
// (写入属完整集,另行评估)。实现由基座 internal 提供。
|
||
//
|
||
// 可选能力接口(type assertion 获取,断言失败=明确报错):
|
||
// - AdminProvider — EXT-18:调用方平台管理角色查询
|
||
// - SurfaceHost — EXT-17:页面保存/读取
|
||
// - WorkCompletionHost — X1:受信项目工作生成
|
||
// - WorkTaskHost — X1:分发期建立可恢复的受信项目任务绑定
|
||
// - HumanDecisionVerifier — 本次分发是否为认证人对确切待确认卡的响应
|
||
// - RolePolicyProvider — Backend 权威职位与动作默认/上限
|
||
// - CapabilityExplainProvider — 当前分发主体的创建者/成员/文档职责能力解释
|
||
type Host interface {
|
||
// PluginID 返回注册处理器的插件标识(形如 rdm)。
|
||
PluginID() string
|
||
// Owner 返回本次分发的作用域 owner。
|
||
Owner() string
|
||
|
||
// KVPut/KVGet 插件 KV(按 pluginID+owner 隔离)。
|
||
KVPut(key, value string) error
|
||
KVGet(key string) (value string, ok bool, err error)
|
||
|
||
// Audit 写一条 owner 名下的审计事件。
|
||
Audit(action, target, evidence string) error
|
||
|
||
// Complete 经基座 LLM 路由补全(owner 的模型配置生效)。
|
||
Complete(ctx context.Context, prompt string) (string, error)
|
||
|
||
// KnowledgePublish 幂等发布(EXT-9):按 docs 的 ref 整批替换——先清该 owner
|
||
// 下相同 ref 的旧行与旧向量,再走 KnowledgeWrite 语义(Owner/Source 强制同彼)。
|
||
// 重试产生相同结果;embed=true 时重建向量。
|
||
KnowledgePublish(ctx context.Context, docs []core.KnowledgeDoc, embed bool) error
|
||
// KnowledgeDelete 撤销本插件来源的知识索引(EXT-9):sourcePrefix 必须是
|
||
// "plugin:<本插件ID>"——manual 或他人来源拒绝;删除行与向量,幂等(无匹配不报错)。
|
||
KnowledgeDelete(ctx context.Context, sourcePrefix string) error
|
||
|
||
// KnowledgeSearch 混合检索(owner 可见范围)。
|
||
KnowledgeSearch(ctx context.Context, projectID core.ID, text string, topK int) ([]core.RetrievalHit, error)
|
||
// KnowledgeWrite 写知识分块;embed=true 时同步建向量(失败降级纯全文)。
|
||
// docs 各项的 Owner 由基座强制改写为本次 owner;Source 为空补 "plugin:<PluginID>"。
|
||
KnowledgeWrite(ctx context.Context, docs []core.KnowledgeDoc, embed bool) error
|
||
|
||
// TaskCreate/TaskUpdate 后台任务台账(进度/状态记账,执行体由下游自跑)。
|
||
TaskCreate(kind, title, conversationID string) (taskID string, err error)
|
||
TaskUpdate(taskID, status string, progress int, errText string) error
|
||
|
||
// ArtifactGet/ArtifactContent 工件只读(R1-3 ingest 读原件)。
|
||
ArtifactGet(artifactID string) (core.Artifact, error)
|
||
ArtifactContent(artifactID string) ([]byte, error)
|
||
|
||
// ParseDocument 用基座内置解析器把文档字节解析为 ParsedDocument(EXT-5)。
|
||
// 纯计算,不读写 owner 数据——要解析工件原件先经 ArtifactContent 取字节,
|
||
// owner 作用域由它保证。按扩展名分派;不可解析返回带 Warnings 的文档
|
||
//(显式失败),只有 ctx 取消与输入超过 100MiB 两种情况返回 error。
|
||
ParseDocument(ctx context.Context, filename, mime string, content []byte) (core.ParsedDocument, error)
|
||
}
|
||
|
||
// —— AdminProvider 定义移至本文件上方(Request 之后)——
|
||
|
||
// HumanDecisionAudienceStructure is the only audience this proof can authorize.
|
||
// Another plugin cannot reuse the same human response as its own confirmation.
|
||
const HumanDecisionAudienceStructure = "rdm.structure.decide"
|
||
|
||
// HumanDecisionBinding is the exact object a human must have confirmed.
|
||
// Callers pass it to the host; they do not carry a copyable secret.
|
||
type HumanDecisionBinding struct {
|
||
Audience string
|
||
Actor string
|
||
ProjectID string
|
||
ProposalID string
|
||
ProposalRevision int
|
||
PayloadHash string
|
||
Decision string
|
||
}
|
||
|
||
// HumanDecisionCard is the pending object a person confirms.
|
||
type HumanDecisionCard struct {
|
||
CardID string
|
||
Question string
|
||
ExpiresAt time.Time
|
||
}
|
||
|
||
// HumanDecisionProof is a read-only statement that a person already responded.
|
||
// ProofID is a consumption key for the caller. The host does not mark it used.
|
||
type HumanDecisionProof struct {
|
||
ProofID string
|
||
Issuer string
|
||
HumanActor string
|
||
Audience string
|
||
ProjectID string
|
||
ProposalID string
|
||
Revision int
|
||
PayloadHash string
|
||
Decision string
|
||
IssuedAt time.Time
|
||
ExpiresAt time.Time
|
||
ConversationID string
|
||
RunID string
|
||
HITLResponseRef string
|
||
}
|
||
|
||
// HumanDecisionVerifier is optional. A missing assertion means the capability
|
||
// is absent and the caller must refuse the write.
|
||
type HumanDecisionVerifier interface {
|
||
IssueHumanDecision(binding HumanDecisionBinding, question string) (HumanDecisionCard, error)
|
||
VerifyHumanDecision(expected HumanDecisionBinding) (HumanDecisionProof, error)
|
||
RevokeHumanDecision(cardID string) error
|
||
}
|