M4: 范式引擎与任务运行时

skill-engine(L4):载方法不载权力(白名单外工具即拒)、结晶片段经 ToolInvoker 留痕、
  装配入信封、技能目录渐进披露

flow-engine(L5):四种步型、确定性谓词跳步、重试与声明给定的兜底、
  异步步即监听(时限必配)、写类工具登记动作意向、步序位置持久

loop-engine(L5):自研 CMMN 子集解析器(表外构件直接报错,子集封顶可机器证明)、
  两副标准生命周期与迁移拓扑(引擎不得私改)、Enabled ≠ Active、
  语义性前提标待裁量评估(引擎不代裁量)、DMN 确定性求值 + 命中行留痕、
  DRD 缺口演绎与拓扑序、双向驱动与交集导航(断点即人环)、三型行动、
  终局唯放行与 Harness

task(L6):终态五名裁定(released 唯一成功)、Harness 预算环外强制与停滞熔断
  (监听中是健康态)、控制三语义(叫停不是终态,记最重档纠正)、改派留痕通知双方、
  断点续跑三份持久状态、封卷触发蒸馏钩子

301 个测试通过,门禁五道全绿

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
JCAI Main Agent
2026-07-27 01:51:22 +08:00
co-authored by Claude Opus 5
parent 2850e6d4ef
commit 3c974d2860
55 changed files with 7113 additions and 1 deletions
@@ -0,0 +1,91 @@
/**
* 技能装配 —— 纯逻辑,零 IO(编码规范 §5.3;语义出处 AM 08)。
*
* 「白名单回答『能用什么』(权力),技能回答『怎么用好』(方法)—— 棋子确定,棋谱是资产。」
*
* 三层软硬:
* 工具白名单 = 硬约束(权力,契约定)—— 不在名单调不动
* 技能清单 = 软资源(方法,包声明)—— AI 可用可不用,**可以越谱,不可越权**
* 临场路径 = 自由(由事决定,loop)—— 受上两层约束
*/
import type { Skill } from '@contracts';
export type AssemblyContext = {
/** 本活动的工具白名单 —— 硬约束 */
readonly allowlist: readonly string[];
/** 本活动可装配的技能集合 —— 软资源 */
readonly catalog: readonly Skill[];
};
export type SkillViolation = { readonly skillId: string; readonly tool: string; readonly message: string };
/**
* 载方法,不载权力(必须)——
* 「技能不得扩大白名单、授权边界与禁区;技能提及的工具不在本活动白名单者,**不可调**。」
*/
export function checkSkillPower(skill: Skill, allowlist: readonly string[]): SkillViolation[] {
return skill.crystallized
.filter((step) => !allowlist.includes(step.tool))
.map((step) => ({
skillId: skill.id,
tool: step.tool,
message: `技能 ${skill.label} 的结晶片段引用了白名单外的工具 ${step.tool} —— 载方法不载权力`,
}));
}
export function checkCatalog(context: AssemblyContext): SkillViolation[] {
return context.catalog.flatMap((skill) => checkSkillPower(skill, context.allowlist));
}
/** 技能目录:常驻上下文里只有它 —— 正文按事加载,引用按需下钻(渐进披露) */
export type SkillIndexEntry = { readonly id: string; readonly label: string; readonly applicability: string };
export function skillIndex(catalog: readonly Skill[]): SkillIndexEntry[] {
return catalog.map((s) => ({ id: s.id, label: s.label, applicability: s.applicability }));
}
/** 装配入信封:每步装配了哪些技能及其版本,记入任务记忆 —— 回放与归因依赖之 */
export type SkillStamp = { readonly id: string; readonly version: string };
export function stampOf(skills: readonly Skill[]): SkillStamp[] {
return skills.map((s) => ({ id: s.id, version: s.version }));
}
/**
* 结晶片段脚本 = 确定性动作链。
* 「脚本必须**注册进工具表、经运行时调用而留痕**、无环境能力;不得成为绕过留痕的暗道。」
*/
export type CrystallizedStep = { readonly tool: string; readonly input: Readonly<Record<string, string>> };
export function crystallizedChain(skill: Skill): CrystallizedStep[] {
return skill.crystallized.map((s) => ({ tool: s.tool, input: s.input }));
}
/** 入参插值:`{{path}}` 从任务数据取值 —— 纯字符串替换,零求值 */
export function interpolate(
input: Readonly<Record<string, string>>,
scope: Readonly<Record<string, unknown>>,
): Record<string, unknown> {
const out: Record<string, unknown> = {};
for (const [key, template] of Object.entries(input)) {
const exact = /^\{\{\s*([\w.]+)\s*\}\}$/.exec(template);
if (exact !== null) {
out[key] = resolvePath(scope, exact[1]!);
continue;
}
out[key] = template.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_, path: string) => {
const value = resolvePath(scope, path);
return value === null || value === undefined ? '' : String(value);
});
}
return out;
}
function resolvePath(scope: Readonly<Record<string, unknown>>, path: string): unknown {
let current: unknown = scope;
for (const segment of path.split('.')) {
if (current === null || current === undefined || typeof current !== 'object') return undefined;
current = (current as Record<string, unknown>)[segment];
}
return current;
}