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,61 @@
/**
* module: interaction —— 对外类型(编码规范 §10.1;语义出处 OS 02 / 09,AM 07)。
*
* 交互的**结构**归基座,**内容**归活动:
* 对话流 = 任务的人机时间线、**一切回环输入的唯一入口**;
* 视图区 = 只读呈现容器,内容由后端流控推送。
*/
import type { ChatMessage, StaffId, TaskId, ViewBlock } from '@contracts';
import type { ControlHolder, InteractionPhase, TemplateViolation } from './internal/domain/control-handover.ts';
export type { ChatMessage, ControlHolder, InteractionPhase, TemplateViolation, ViewBlock };
export declare class TemplateRejectedError extends Error {
readonly violations: readonly TemplateViolation[];
constructor(violations: readonly TemplateViolation[]);
}
/** SSE 推送的信封 —— 前端按目标失效对应缓存(§9.2) */
export type PushEnvelope = {
readonly kind: 'chat';
readonly taskId: TaskId;
readonly message: ChatMessage;
} | {
readonly kind: 'view';
readonly taskId: TaskId;
readonly blocks: readonly ViewBlock[];
} | {
readonly kind: 'view-html';
readonly taskId: TaskId;
readonly html: string;
} | {
readonly kind: 'control';
readonly taskId: TaskId;
readonly phase: InteractionPhase;
readonly output: ControlHolder;
} | {
readonly kind: 'invalidate';
readonly target: 'task' | 'workflow' | 'desk';
readonly id: string;
};
export type Subscription = {
close(): void;
};
export type InteractionService = {
/** 对话流:任务的人机时间线 —— 与任务记忆同源、不同筛选 */
timeline(taskId: TaskId): Promise<ChatMessage[]>;
/**
* 负责人的自由输入 —— 人事件的第二形态(卡回传之外):
* 写入任务记忆,loop 作为观察进入后续窗口;**不驱动 flow 步**。
* 叫停等指令走控制事件,不经自由输入。
*/
say(taskId: TaskId, by: StaffId, text: string): Promise<ChatMessage>;
/** 视图流控:活动按局面推结构化块 */
pushView(taskId: TaskId, blocks: readonly ViewBlock[]): Promise<void>;
/** 视图流控:活动推 HTML 模版 —— 过净化门,不过即抛 */
pushViewHtml(taskId: TaskId, html: string): Promise<void>;
/** 控制权转交 —— 随引擎的激活 / 完成事件自动发生 */
setPhase(taskId: TaskId, phase: InteractionPhase): Promise<ControlHolder>;
phaseOf(taskId: TaskId): Promise<InteractionPhase>;
/** SSE:单连接、按信封目标失效缓存 */
subscribe(staffId: StaffId, onPush: (envelope: PushEnvelope) => void): Subscription;
/** 供上层广播失效信号 */
invalidate(target: 'task' | 'workflow' | 'desk', id: string): void;
};