/** * 契约七件(编码规范 §8.1;语义出处 OS 4.1,AM 01)。 * * 契约是活动对调用方暴露的全部真相,也是对 Agent 机制的全部授权; * 两种范式同一契约格式 —— 这是「统一套执行契约」的锚点。 * 契约必须在活动启动前冻结;运行中不得变更 —— 变更即新版本。 * * ADR-003:**声明层 = JSON Schema 2020-12 子集**(活动声明是运行时数据,可 diff 可版本), * 代码层 = zod。本文件用 zod 描述「声明的形状」,声明里的 schema 字段本身是 JSON Schema。 */ import { z } from 'zod'; import { acceptanceLevelSchema } from './terminal.ts'; import { activityIdSchema, skillIdSchema } from './id.ts'; /** JSON Schema 2020-12 子集的运行时形态 —— 由 ajv 校验,不在编译期展开 */ export const jsonSchemaSchema = z.record(z.string(), z.unknown()); export type JsonSchema = z.infer; /* ── ① 输入口:单一入口,data + message ──────────────────────────────── */ export const contractInputSchema = z.object({ /** data:结构化载荷,按 schema 校验,缺项不得启动 */ data: jsonSchemaSchema, /** message:委托消息(自然语言),可为空;**不得携带扩大授权的效力** */ messageAllowed: z.boolean().default(true), messageHint: z.string().optional(), }); export type ContractInput = z.infer; /* ── ② 输出口:必发(AND)与选择组(XOR) ──────────────────────────── */ export const outputPortSchema = z.object({ name: z.string().min(1), /** 交付物 schema —— 调用方仅凭口名与 schema 即可衔接下游 */ payload: jsonSchemaSchema, description: z.string().optional(), }); export type OutputPort = z.infer; export const outputPortsSchema = z.object({ /** 必发口:每次「放行完成」必发;可多口,同时发射 */ mandatory: z.array(outputPortSchema).default([]), /** 选择组:组内恰发一口;选择由环内决断,在放行(验收工单)上一并确认 */ selective: z .array( z.object({ group: z.string().min(1), ports: z.array(outputPortSchema).min(2), }), ) .default([]), }); export type OutputPorts = z.infer; /* ── ③ 必交付物 ─────────────────────────────────────────────────────── */ export const requiredDeliverableSchema = z.object({ key: z.string().min(1), label: z.string(), /** 缺一不得提交放行 —— 验收工单「成果清单」的来源 */ schema: jsonSchemaSchema.optional(), }); export type RequiredDeliverable = z.infer; /* ── ④ 验收判据:逐条标注真实等级 L1–L5 ───────────────────────────── */ export const acceptanceCriterionSchema = z.object({ key: z.string().min(1), label: z.string(), level: acceptanceLevelSchema, /** L1/L2 的机检表达式(S-FEEL);L3–L5 由环内实证 / 独立评审 / 人放行承担 */ predicate: z.string().optional(), /** loop 可引用业务模型的完成判据或 DMN 决策 */ modelRef: z.string().optional(), }); export type AcceptanceCriterion = z.infer; /* ── ⑤ 禁区 ─────────────────────────────────────────────────────────── */ export const prohibitionSchema = z.object({ key: z.string().min(1), /** 应短而硬;负面清单优于正面枚举 */ statement: z.string().min(1), }); export type Prohibition = z.infer; /* ── ⑥ 授权边界 ─────────────────────────────────────────────────────── */ export const authorizationBoundarySchema = z.object({ /** AI 可自决的范围:金额 / 折扣 / 措辞等,以 S-FEEL 谓词或叙述给出 */ discretion: z.array(z.object({ key: z.string(), statement: z.string() })).default([]), /** 预授权动作清单 —— 必须逐项列举,方可在过程中即时执行写类动作(OS 7.3) */ preauthorizedActions: z .array(z.object({ tool: z.string(), operation: z.string(), limit: z.string().optional() })) .default([]), /** 调人预算(打扰上限) */ humanCallBudget: z.number().int().min(0), }); export type AuthorizationBoundary = z.infer; /* ── ⑦ 资源声明 ─────────────────────────────────────────────────────── */ /** 预算四类上限 —— 由基座在环外强制,不得由活动代码自查自守(AM 10.1) */ export const budgetCapsSchema = z.object({ steps: z.number().int().min(1), tokens: z.number().int().min(1), /** 活跃时长(分钟);挂起、监听等待、待卡回传不计入 */ activeMinutes: z.number().int().min(1), humanCalls: z.number().int().min(0), }); export type BudgetCaps = z.infer; export const resourceDeclarationSchema = z.object({ /** 工具调用契约引用 + 版本约束 */ tools: z .array(z.object({ name: z.string().min(1), version: z.string().optional() })) .default([]), skills: z.array(skillIdSchema).default([]), /** 活动使用的 LLM,以自定义模型名(逻辑名)声明;活动不触厂商与密钥 */ models: z.array(z.string().min(1)).default([]), /** 岗位:调人时卡投递到哪个工位 */ roles: z.array(z.string().min(1)).default([]), budget: budgetCapsSchema, }); export type ResourceDeclaration = z.infer; /* ── 七件合体 ───────────────────────────────────────────────────────── */ export const sevenPartContractSchema = z.object({ activityId: activityIdSchema, version: z.string().min(1), input: contractInputSchema, output: outputPortsSchema, requiredDeliverables: z.array(requiredDeliverableSchema).default([]), acceptanceCriteria: z.array(acceptanceCriterionSchema).default([]), prohibitions: z.array(prohibitionSchema).default([]), authorization: authorizationBoundarySchema, resources: resourceDeclarationSchema, }); export type SevenPartContract = z.infer; /** * 完成时的发射集合 = 全部必发口 + 每个选择组各一口(AM 1.2)。 * 纯函数,前后端同源:前端预演、后端裁决。 */ export function emissionPortNames( ports: OutputPorts, selected: Readonly>, ): string[] { const out = ports.mandatory.map((p) => p.name); for (const group of ports.selective) { const choice = selected[group.group]; if (choice !== undefined) out.push(choice); } return out; } /** 选择组必须恰选一口 —— 未选或选了组外的口即非法 */ export function validateSelection( ports: OutputPorts, selected: Readonly>, ): string[] { const errors: string[] = []; for (const group of ports.selective) { const choice = selected[group.group]; if (choice === undefined) { errors.push(`选择组「${group.group}」未选出口`); continue; } if (!group.ports.some((p) => p.name === choice)) { errors.push(`选择组「${group.group}」的出口「${choice}」不在组内`); } } return errors; } /** 验收等级不得谎报:L4 不得标注为 L1–L3;生成者不得自评(AM 12.1) */ export function machineCheckable(criterion: AcceptanceCriterion): boolean { return criterion.level === 'L1' || criterion.level === 'L2'; }