M3(完成): interaction 与 activity-registry

interaction(L3):
- 控制权按态转交:活动在跑归活动,流转/审批/终态归系统;输入侧永远归系统(单入口)
- 视图区净化门:含 <script> / on* / 外部资源的模版一律拒推 —— 容器无脚本执行权
- 对话流单入口:自由输入同写任务记忆并抛人事件;SSE 单连接 + 按目标失效广播
- 前端零过程状态:对话流与视图都是服务端投影

activity-registry(L4):
- 安装两审:静态审(签名/SDK/工具契约齐备/模型名已绑定/schema/悬空监听/模版无脚本/
  零环境能力/动态执行/验收等级不得谎报)+ 回放审(旧成不坏是硬门,旧败能成只入报告)
- 资产与包分离版本化:包只携资产初始版,不覆盖已有更高资产版;装载 = 包版本 + 生效资产版
- 进化管线状态机:候选 → 回放 → 人放行 → 生效;回退是显式治理动作

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

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:36:05 +08:00
co-authored by Claude Opus 5
parent d96c6e6dff
commit 2850e6d4ef
29 changed files with 4339 additions and 4 deletions
@@ -0,0 +1,73 @@
/**
* 控制权按态转交 —— 纯逻辑,零 IO(编码规范 §5.3;语义出处 OS 4.1,AM 07)。
*
* 「视图区容器与对话区输出框的控制权由活动与系统**共同持有**:
* 任务激活(Agent 机制运行)时归**活动** —— 视图流控与对话输出由活动驱动;
* 流程流转(节点间、审批点、终态前后)时归**系统** —— 流转、审批与终态回执由系统呈现。
* 转交随引擎的激活 / 完成事件自动发生,活动不感知交接。
* **输入侧不参与转交**:全部人类输入永远经系统的对话流卡片协议回传。」
*/
export const CONTROL_HOLDERS = ['activity', 'system'] as const;
export type ControlHolder = (typeof CONTROL_HOLDERS)[number];
/** 任务在交互层的态 —— 决定输出侧控制权归谁 */
export const INTERACTION_PHASES = [
'activating',
'active',
'awaiting-human',
'awaiting-event',
'approving',
'transitioning',
'terminal',
] as const;
export type InteractionPhase = (typeof INTERACTION_PHASES)[number];
/** 输出侧控制权:活动在跑就归活动,其余一律归系统 */
export function outputControl(phase: InteractionPhase): ControlHolder {
switch (phase) {
case 'active':
case 'awaiting-human':
case 'awaiting-event':
return 'activity';
case 'activating':
case 'approving':
case 'transitioning':
case 'terminal':
return 'system';
}
}
/** 输入侧**永远**归系统 —— 单入口,不参与转交 */
export function inputControl(): ControlHolder {
return 'system';
}
/** 转交是否发生 —— 引擎的激活 / 完成事件驱动;活动不感知 */
export function handoverHappens(from: InteractionPhase, to: InteractionPhase): boolean {
return outputControl(from) !== outputControl(to);
}
/* ── 视图区:只读呈现容器,无脚本执行权(AM 07) ────────────────────── */
export type TemplateViolation = { readonly code: 'script' | 'event-handler' | 'external-resource'; readonly message: string };
const SCRIPT = /<\s*script\b/i;
const EVENT_HANDLER = /\son[a-z]+\s*=/i;
const EXTERNAL = /(src|href)\s*=\s*["']?\s*(https?:)?\/\//i;
/**
* 模版无脚本 —— 安装静态审与推送前都用它。
* 「模版是**内容**不是代码:web 零活动代码,容器渲染净化、无脚本执行权。」
*/
export function checkTemplate(html: string): TemplateViolation[] {
const out: TemplateViolation[] = [];
if (SCRIPT.test(html)) out.push({ code: 'script', message: '视图模版不得含 <script>' });
if (EVENT_HANDLER.test(html)) out.push({ code: 'event-handler', message: '视图模版不得含 on* 事件处理器' });
if (EXTERNAL.test(html)) out.push({ code: 'external-resource', message: '视图模版不得引用外部资源' });
return out;
}
export function templateIsSafe(html: string): boolean {
return checkTemplate(html).length === 0;
}