Files
JX-BPOS/packages/contracts/src/contract7.ts
T
JCAI Main AgentandClaude Opus 5 14d4fa046a M1: 底座 —— @contracts 首版 + event / ledger / org / llm
@contracts 首版(§8.1 九个文件):
- id 品牌 ID / terminal 终态五名与归侧四标签 / envelope 信封六字段与四类事件
- card 工单两模式 + 对话卡族(形状对齐 2026-07-26 原型定稿)
- contract7 契约七件 / ledger 三级寻址与写三分 / process-package 流程包 DSL 与触发器三类
- manifest 活动包清单六区(含 CMMN·DMN·步序声明的资产形状)/ org / api 路由契约

模块(L1):
- event   信封存储、归属解析三级短路、投递五诺、监听注册表、三级消费裁定
- ledger  schema 自生长(最大化合并/最宽类型/只增不破坏)、任务视图叠加、写三分、冲突检查
- org     岗位授权(客体三类×动作三种)、停用前置检查、会话、种子播种
- llm     四元绑定、ModelGateway 三模式(live/record/replay)—— 两缝之一

app 装配:Hono 服务 + 鉴权中间件 + 治理台/工作台路由;drizzle 迁移;AES-256-GCM 凭证密封

ADR-007 工具链与门禁 · ADR-008 投递队列 · ADR-009 凭证加密
(ADR 编号自 007 续:编码规范 §14 已用到 006,README 写「从 005 续」是笔误)

门禁五道全绿;96 个测试通过,含对真实 PG 的 ledger 集成测试

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
2026-07-27 00:59:14 +08:00

181 lines
7.9 KiB
TypeScript

/**
* 契约七件(编码规范 §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<typeof jsonSchemaSchema>;
/* ── ① 输入口:单一入口,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<typeof contractInputSchema>;
/* ── ② 输出口:必发(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<typeof outputPortSchema>;
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<typeof outputPortsSchema>;
/* ── ③ 必交付物 ─────────────────────────────────────────────────────── */
export const requiredDeliverableSchema = z.object({
key: z.string().min(1),
label: z.string(),
/** 缺一不得提交放行 —— 验收工单「成果清单」的来源 */
schema: jsonSchemaSchema.optional(),
});
export type RequiredDeliverable = z.infer<typeof requiredDeliverableSchema>;
/* ── ④ 验收判据:逐条标注真实等级 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<typeof acceptanceCriterionSchema>;
/* ── ⑤ 禁区 ─────────────────────────────────────────────────────────── */
export const prohibitionSchema = z.object({
key: z.string().min(1),
/** 应短而硬;负面清单优于正面枚举 */
statement: z.string().min(1),
});
export type Prohibition = z.infer<typeof prohibitionSchema>;
/* ── ⑥ 授权边界 ─────────────────────────────────────────────────────── */
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<typeof authorizationBoundarySchema>;
/* ── ⑦ 资源声明 ─────────────────────────────────────────────────────── */
/** 预算四类上限 —— 由基座在环外强制,不得由活动代码自查自守(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<typeof budgetCapsSchema>;
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<typeof resourceDeclarationSchema>;
/* ── 七件合体 ───────────────────────────────────────────────────────── */
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<typeof sevenPartContractSchema>;
/**
* 完成时的发射集合 = 全部必发口 + 每个选择组各一口(AM 1.2)。
* 纯函数,前后端同源:前端预演、后端裁决。
*/
export function emissionPortNames(
ports: OutputPorts,
selected: Readonly<Record<string, string>>,
): 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<Record<string, string>>,
): 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';
}