import { z } from "zod"; import { ApiScope } from "./common.ts"; /** * 能力调用上下文(REQ §10.3)。产品自有接口,不是 Pi SDK 原生 API。 * signal 由任务取消传播而来。 */ export interface CapabilityContext { actorId: string; taskId: string; scopeIds: string[]; signal: AbortSignal; } /** 变更计划:由业务端生成与保存,审批绑定 id + payloadHash + expectedRevision */ export const MutationPlan = z.object({ id: z.string(), operation: z.string(), targetIds: z.array(z.string()), expectedRevision: z.number().int().nonnegative(), evidenceIds: z.array(z.string()), payloadHash: z.string(), requiresApproval: z.boolean(), }); export type MutationPlan = z.infer; /** 能力 ID:`域.动作`,如 download.submit */ export const CapabilityId = z.string().regex(/^[a-z][a-z0-9]*\.[a-z][a-z0-9_]*$/); export type CapabilityId = z.infer; // ── 能力描述符与首批能力(任务书 §3.1-9;REQ §10.3) ── /** 风险级别:low 只读;medium 预览/可撤回写入;high 触发下载、文件变更或策略发布 */ export const RiskLevel = z.enum(["low", "medium", "high"]); export type RiskLevel = z.infer; /** * 接口类别(REQ §12.3),由弱到强: * - read:只读查询 * - preview:生成预览/方案,不落任何变更 * - propose:生成并保存变更计划(MutationPlan),等待授权 * - approve:对计划作出授权决定 * - execute:产生外部副作用(下载提交、Copy/Move、策略发布…) */ export const CapabilityKind = z.enum(["read", "preview", "propose", "approve", "execute"]); export type CapabilityKind = z.infer; /** 崩溃恢复时 harness 能否用持久化参数重跑:有副作用的一律 never,只读用 safe */ export const CapabilityReplay = z.enum(["safe", "never"]); export type CapabilityReplay = z.infer; /** 首批能力 ID */ export const CAPABILITY_IDS = [ "catalog.lookup", "release.search", "release.inspect", "identity.resolve", "download.plan", "download.submit", "download.recheck", "organize.plan", "organize.execute", "task.inspect", "task.retry", "task.cancel", "policy.preview", "policy.apply", "inventory.query", "agent.spawn", "agent.await", "site.list", "site.health_check", "site.refresh_login", "site.user_data", "browser.snapshot", "browser.navigate", "browser.interact", "browser.download", "agent.plan", "agent.propose_options", ] as const; export const KnownCapabilityId = z.enum(CAPABILITY_IDS); export type KnownCapabilityId = z.infer; /** schema 引用:指向 contracts 中导出的 schema 名(如 "ReleaseSearchInput"),便于序列化给 UI/MCP 列表 */ export const SchemaRef = z.string().regex(/^[A-Z][A-Za-z0-9]*$/); /** * 能力的可序列化描述(唯一定义;处理器在 server 侧挂载,模型只能看到白名单内的描述)。 * 运行时注册用 `CapabilitySpec`(同字段,inputSchema/outputSchema 换成真实 zod schema), * 首批能力的 spec 用 `capabilitySpec(id)` 从 CAPABILITY_CATALOG + CAPABILITY_IO 组装。 */ export const CapabilityDescriptor = z.object({ id: CapabilityId, version: z.number().int().min(1), title: z.string().min(1), description: z.string().min(1), kind: CapabilityKind, inputSchema: SchemaRef, outputSchema: SchemaRef, /** 经 HTTP/MCP 调用时所需的 API scope */ scope: ApiScope, riskLevel: RiskLevel, idempotent: z.boolean(), supportsPreview: z.boolean(), replay: CapabilityReplay, }); export type CapabilityDescriptor = z.infer; /** 运行时能力规格:描述符字段 + 真实的输入/输出 zod schema(注册中心、Pi 工具、HTTP 共用) */ export type CapabilitySpec = Omit< CapabilityDescriptor, "inputSchema" | "outputSchema" > & { inputSchema: I; outputSchema: O }; /** 需要走授权回调(automation_grant / approval)的类别 */ export const AUTHORIZED_KINDS: readonly CapabilityKind[] = ["propose", "approve", "execute"]; /** 首批能力的描述(inputSchema/outputSchema 名对应 capabilities-io.ts 中的导出) */ export const CAPABILITY_CATALOG: Record = { "catalog.lookup": d( "catalog.lookup", "查作品", "按标题/别名/外部 ID 查本地作品与元数据候选", "read", "CatalogLookup", "read", "low", true, false, ), "release.search": d( "release.search", "搜资源", "先查本地资源索引与站点快照,不足时对已开启站点实时补查", "read", "ReleaseSearch", "read", "low", true, false, ), "release.inspect": d( "release.inspect", "看资源详情", "读取资源详情、文件清单、促销与解析结果", "read", "ReleaseInspect", "read", "low", true, false, ), "identity.resolve": d( "identity.resolve", "识别身份", "为观察/资源提出身份映射;证据充分时自动接受,有歧义时转为待处理事项", "propose", "IdentityResolve", "organize.execute", "medium", true, true, ), "download.plan": d( "download.plan", "生成下载计划", "评估候选,生成占用版本目标(slot)与预算预留的下载计划;不提交", "preview", "DownloadPlan", "organize.execute", "medium", true, true, ), "download.submit": d( "download.submit", "提交下载", "按已保存的计划提交到下载器;校验授权与硬阈值", "execute", "DownloadSubmit", "organize.execute", "high", true, false, ), "download.recheck": d( "download.recheck", "重新校验/恢复下载", "对已纳管的下载任务强制重新校验(action=recheck),或恢复非阈值、非人工的暂停(action=resume);不换源、不删除、不改保存路径", "execute", "DownloadRecheck", "organize.execute", "medium", true, false, ), "organize.plan": d( "organize.plan", "生成整理计划", "生成并保存整理计划:冻结源/目标路径、整理方式与前置条件;不执行", "preview", "OrganizePlan", "organize.read", "low", true, true, ), "organize.execute": d( "organize.execute", "执行整理", "执行已保存的整理计划(复制/移动),生成整理记录", "execute", "OrganizeExecute", "organize.execute", "high", true, false, ), "task.inspect": d( "task.inspect", "查看任务", "读取任务状态、业务时间线与错误", "read", "TaskInspect", "read", "low", true, false, ), "task.retry": d( "task.retry", "重试任务", "对失败/死信任务重新排队;副作用经操作账本去重", "execute", "TaskRetry", "organize.execute", "medium", true, false, ), "task.cancel": d( "task.cancel", "取消任务", "停止未来步骤,不撤销已完成的外部动作", "execute", "TaskCancel", "organize.execute", "medium", true, false, ), "policy.preview": d( "policy.preview", "预览策略", "用历史数据回放策略草案,展示新增命中与误排除", "preview", "PolicyPreview", "read", "low", true, true, ), "policy.apply": d( "policy.apply", "发布策略", "发布策略版本(需授权);不直接执行 JavaScript", "execute", "PolicyApply", "admin", "high", true, true, ), "inventory.query": d( "inventory.query", "查库存", "查询本地资产、版本目标满足情况与外部库存证据", "read", "InventoryQuery", "read", "low", true, false, ), // 子代理(设置 pi.subagents 开启后才提供给模型):类别 propose——只建有界子会话,不产生外部副作用, // "先给方案"的会话也能派生(子会话同样只给方案);授权不高于父会话,由服务端校验 "agent.spawn": d( "agent.spawn", "派生子会话", "把一个有界子目标交给子会话处理:能力白名单须是本会话白名单的子集,预算从本会话剩余预算里划拨,模式不高于本会话;返回子会话 runId。await=true 时等它结束并返回汇报", "propose", "AgentSpawn", "organize.execute", "medium", true, false, ), "agent.await": d( "agent.await", "等待子会话", "等待本会话派生的子会话结束(等待期间不调用模型),返回各自状态、用量与汇报摘要;超时仍未结束的标为 pending", "read", "AgentAwait", "read", "low", true, false, ), // W10-pre2 站点能力(W9-A 站点中心的 Agent 入口);输出一律不含凭证、Cookie 与 secret 引用 "site.list": d( "site.list", "列出站点", "列出已登记站点的健康、登录状态与账号摘要(不含凭证)", "read", "SiteList", "read", "low", true, false, ), // 只读站点首页并写回健康列,重跑无害:声明 replay safe "site.health_check": d( "site.health_check", "检测站点", "检测站点首页连通与登录态,写回站点健康状态", "execute", "SiteHealthCheck", "admin", "low", true, false, "safe", ), "site.refresh_login": d( "site.refresh_login", "刷新站点登录", "对站点账号重新登录或校验 Cookie / API Key,成功后拉取用户数据;需持续授权或审批", "execute", "SiteLoginRefresh", "admin", "medium", true, false, ), "site.user_data": d( "site.user_data", "查站点用户数据", "读取站点账号的最新用户数据(上传、下载、分享率、魔力、做种)与按天历史", "read", "SiteUserData", "read", "low", true, false, ), // C1-E 浏览器能力(REQ §4.4、§4.7.3):经 BrowserProvider 访问用户已有的 camofox-browser; // 导航限站点域名,凭证由服务端注入,人工接管期间自动化动作被拒;下载产物只进后端暂存核验 "browser.snapshot": d( "browser.snapshot", "读取原站页面", "读取站点浏览器会话当前页面的结构化快照(可访问性文本 + eN 元素引用,凭证已遮蔽);控制权交还或导航后必须重新读取", "read", "BrowserSnapshot", "read", "low", true, false, ), "browser.navigate": d( "browser.navigate", "打开原站页面", "在站点浏览器会话里打开站点域名内的地址(没有会话时按站点打开);受站点限流、超时与重试预算约束", "execute", "BrowserNavigate", "admin", "low", false, false, ), "browser.interact": d( "browser.interact", "操作原站页面", "在站点浏览器会话里点击 / 输入 / 选择 / 滚动 / 按键;登录字段用 credential 由服务端注入凭证;验证码或二次认证用 request_human 转人工", "execute", "BrowserInteract", "admin", "medium", false, false, ), "browser.download": d( "browser.download", "捕获原站下载", "捕获浏览器会话里的种子下载,交后端暂存并核验 infohash、匹配本地资源;提交下载仍用 download.plan / download.submit", "execute", "BrowserDownload", "organize.execute", "medium", false, false, ), // AG-2 自动化优先的会话交互:步骤清单与方案选择。只在 Agent 会话里可用(HTTP / MCP 调用直接报错) "agent.plan": d( "agent.plan", "更新步骤清单", "开始时列出 3–7 个步骤,之后每开始、完成、跳过或失败一步就更新(只传变化的步骤,按 id 合并;replace=true 整体替换)。步骤清单实时显示在会话页顶部", "read", "AgentPlan", "read", "low", true, false, ), // 类别 propose:只登记一个待用户选择的待处理事项,不产生外部副作用;等待期间会话挂起(不占 worker、不调用模型)。 // 与 agent.spawn 一样在注册时声明 replay=safe(恢复后重放本调用取用户的选择) "agent.propose_options": d( "agent.propose_options", "给出方案", "有明显取舍时给用户 2–3 个方案(各写摘要、风险、估算代价,标出推荐项),等用户选定后返回所选方案;用户也可以都不要并说明原因。超时按设置自动选推荐项或停止", "propose", "AgentProposeOptions", "organize.execute", "low", true, false, ), }; function d( id: KnownCapabilityId, title: string, description: string, kind: CapabilityKind, schemaBase: string, scope: ApiScope, riskLevel: RiskLevel, idempotent: boolean, supportsPreview: boolean, /** 缺省按类别推导;仅对幂等且副作用可重跑的能力显式给 safe */ replay?: CapabilityReplay, ): CapabilityDescriptor { return { id, version: 1, title, description, kind, inputSchema: `${schemaBase}Input`, outputSchema: `${schemaBase}Output`, scope, riskLevel, idempotent, supportsPreview, // 有副作用的类别一律不可重放 replay: replay ?? (kind === "read" || kind === "preview" ? "safe" : "never"), }; }