forked from ChaiHao/BPOS-MVP
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:
co-authored by
Claude Opus 5
parent
d96c6e6dff
commit
2850e6d4ef
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* module: activity-registry —— 对外类型(编码规范 §10.1;语义出处 OS 11,AM 11)。
|
||||
*
|
||||
* 活动库:**独立的安装与卸载入口** —— 不在编排中发生。
|
||||
* 安装恒为新增、版本并存;在途任务按其启动时版本跑完,切换零丢单。
|
||||
*/
|
||||
import type { ActivityManifest, InstallVerdict, Paradigm, StaticAuditFinding } from '@contracts';
|
||||
import type { AssetKind, AssetVersion, IncrementState, LoadEnvelope, PackageVersion, ReplayCase, ReplayVerdict } from './internal/domain/versioning.ts';
|
||||
import type { ExternalChecks } from './internal/domain/static-audit.ts';
|
||||
export type { ActivityManifest, AssetKind, AssetVersion, ExternalChecks, IncrementState, InstallVerdict, LoadEnvelope, PackageVersion, Paradigm, ReplayCase, ReplayVerdict, StaticAuditFinding, };
|
||||
export declare class InstallRejectedError extends Error {
|
||||
readonly verdict: InstallVerdict;
|
||||
constructor(verdict: InstallVerdict);
|
||||
}
|
||||
export declare class ActivityNotInstalledError extends Error {
|
||||
readonly activityId: string;
|
||||
constructor(activityId: string);
|
||||
}
|
||||
export declare class ToolNotDeclaredError extends Error {
|
||||
readonly missing: readonly string[];
|
||||
constructor(missing: readonly string[]);
|
||||
}
|
||||
/** 库内两维管理:业务分类(一级,组织自定)× 活动类型(标准 / 案例)—— 两维平级、相互独立 */
|
||||
export type ActivityCard = {
|
||||
readonly activityId: string;
|
||||
readonly name: string;
|
||||
readonly category: string;
|
||||
readonly paradigm: Paradigm;
|
||||
readonly packageVersions: readonly PackageVersion[];
|
||||
readonly assetVersions: readonly AssetVersion[];
|
||||
readonly enabled: boolean;
|
||||
};
|
||||
/** 编排导出的依赖文件 —— 清单骨架:契约区 + 声明区(OS 11.1 第 2 步) */
|
||||
export type DependencyFile = {
|
||||
readonly activityId: string;
|
||||
readonly contract: ActivityManifest['contract'];
|
||||
readonly declarations: ActivityManifest['declarations'];
|
||||
readonly compileOptions: {
|
||||
readonly sdkVersion: string;
|
||||
readonly format: 'esm';
|
||||
readonly single: true;
|
||||
};
|
||||
};
|
||||
export type ActivityRegistryService = {
|
||||
/** 安装两审:静态审读清单,回放审重演历史 */
|
||||
install(manifest: ActivityManifest, artifact: string): Promise<InstallVerdict>;
|
||||
list(): Promise<ActivityCard[]>;
|
||||
get(activityId: string): Promise<ActivityCard | undefined>;
|
||||
setEnabled(activityId: string, enabled: boolean): Promise<void>;
|
||||
uninstall(activityId: string): Promise<void>;
|
||||
/** 任务装载 = 包版本 + 各资产当前生效版,全部入信封 */
|
||||
loadEnvelope(activityId: string): Promise<LoadEnvelope>;
|
||||
manifestOf(activityId: string, packageVersion?: string): Promise<ActivityManifest>;
|
||||
/** 进化管线:候选 → 回放 → 人放行 → 生效;可回退 */
|
||||
proposeIncrement(input: {
|
||||
activityId: string;
|
||||
kind: AssetKind;
|
||||
diff: string;
|
||||
evidenceEntryIds: readonly string[];
|
||||
}): Promise<{
|
||||
incrementId: string;
|
||||
state: IncrementState;
|
||||
}>;
|
||||
replayIncrement(incrementId: string, cases: readonly ReplayCase[]): Promise<ReplayVerdict>;
|
||||
releaseIncrement(incrementId: string, by: string): Promise<AssetVersion>;
|
||||
rejectIncrement(incrementId: string, reason: string): Promise<void>;
|
||||
/** 行为回退 = 版本回退(显式治理动作) */
|
||||
rollbackAsset(activityId: string, kind: AssetKind, toVersion: number): Promise<AssetVersion>;
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* module: activity-registry —— 唯一出口。只做 re-export,不写逻辑(编码规范 §10.1)。
|
||||
*/
|
||||
export type { ActivityCard, ActivityManifest, ActivityRegistryService, AssetKind, AssetVersion, DependencyFile, ExternalChecks, IncrementState, InstallVerdict, LoadEnvelope, PackageVersion, ReplayCase, ReplayVerdict, StaticAuditFinding, } from './contract.ts';
|
||||
export { ActivityNotInstalledError, InstallRejectedError, ToolNotDeclaredError } from './contract.ts';
|
||||
export type { Clock, IdGenerator, LedgerDeclarationApplier, ModelBindingProbe, RegistryStore, TemplateGuard, ToolContractProbe, } from './port.ts';
|
||||
export type { RegistryServiceDeps } from './internal/usecase/registry-service.ts';
|
||||
export { createRegistryService } from './internal/usecase/registry-service.ts';
|
||||
export { acceptanceLevelFindings, auditPassed, danglingListeners, flowFindings, loopFindings, scanArtifact, staticAudit, } from './internal/domain/static-audit.ts';
|
||||
export { ASSET_KINDS, assertTransition, buildLoadEnvelope, canTransition, IllegalIncrementTransitionError, INCREMENT_STATES, judgeReplay, mayTakeEffect, shouldSeedAsset, } from './internal/domain/versioning.ts';
|
||||
export { createRegistryStore } from './internal/adapter/registry-store.ts';
|
||||
export { activityAssetTable, activityPackageTable, assetIncrementTable } from './internal/adapter/registry-schema.ts';
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* module: activity-registry —— 本模块要外界提供的能力(编码规范 §10.1 / §5.3)。
|
||||
*/
|
||||
import type { ActivityManifest } from '@contracts';
|
||||
import type { AssetKind, AssetVersion, IncrementState, PackageVersion, ReplayVerdict } from './internal/domain/versioning.ts';
|
||||
export type Clock = {
|
||||
now(): Date;
|
||||
};
|
||||
export type IdGenerator = {
|
||||
next(): string;
|
||||
};
|
||||
export type RegistryStore = {
|
||||
savePackage(manifest: ActivityManifest, artifact: string): Promise<void>;
|
||||
listPackages(activityId?: string): Promise<PackageVersion[]>;
|
||||
manifest(activityId: string, packageVersion?: string): Promise<ActivityManifest | undefined>;
|
||||
artifact(activityId: string, packageVersion: string): Promise<string | undefined>;
|
||||
setEnabled(activityId: string, enabled: boolean): Promise<void>;
|
||||
setState(activityId: string, version: string, state: PackageVersion['state']): Promise<void>;
|
||||
enabledOf(activityId: string): Promise<boolean>;
|
||||
listAssets(activityId: string): Promise<AssetVersion[]>;
|
||||
saveAsset(asset: AssetVersion, body: string): Promise<void>;
|
||||
setAssetEffective(activityId: string, kind: AssetKind, version: number): Promise<void>;
|
||||
saveIncrement(input: {
|
||||
id: string;
|
||||
activityId: string;
|
||||
kind: AssetKind;
|
||||
diff: string;
|
||||
evidenceEntryIds: readonly string[];
|
||||
state: IncrementState;
|
||||
createdAt: Date;
|
||||
}): Promise<void>;
|
||||
getIncrement(id: string): Promise<{
|
||||
id: string;
|
||||
activityId: string;
|
||||
kind: AssetKind;
|
||||
diff: string;
|
||||
state: IncrementState;
|
||||
} | undefined>;
|
||||
setIncrementState(id: string, state: IncrementState, report?: ReplayVerdict, by?: string): Promise<void>;
|
||||
};
|
||||
/** 工具契约核对 —— app 注入 `toolbox`(L2):装不齐不上架 */
|
||||
export type ToolContractProbe = {
|
||||
assertContractsAvailable(names: readonly string[]): Promise<{
|
||||
ok: boolean;
|
||||
missing: string[];
|
||||
}>;
|
||||
};
|
||||
/** 模型名核对 —— app 注入 `llm`(L1) */
|
||||
export type ModelBindingProbe = {
|
||||
assertNamesBound(names: readonly string[]): Promise<{
|
||||
ok: boolean;
|
||||
missing: string[];
|
||||
}>;
|
||||
};
|
||||
/** 台账声明合并 —— app 注入 `ledger`(L1):安装时依声明自生长 */
|
||||
export type LedgerDeclarationApplier = {
|
||||
applyDeclarations(declarations: ActivityManifest['declarations']['ledger']): Promise<Array<{
|
||||
applied: boolean;
|
||||
conflicts: ReadonlyArray<{
|
||||
field: string;
|
||||
message: string;
|
||||
}>;
|
||||
}>>;
|
||||
};
|
||||
/** 模版净化门 —— app 注入 `interaction`(L3) */
|
||||
export type TemplateGuard = {
|
||||
isSafe(html: string): boolean;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* module: interaction —— 唯一出口。只做 re-export,不写逻辑(编码规范 §10.1)。
|
||||
*/
|
||||
export type { ChatMessage, ControlHolder, InteractionPhase, InteractionService, PushEnvelope, Subscription, TemplateViolation, ViewBlock, } from './contract.ts';
|
||||
export { TemplateRejectedError } from './contract.ts';
|
||||
export type { Audience, Clock, HumanEventPublisher, IdGenerator, InteractionStore, MemoryRecorder } from './port.ts';
|
||||
export type { InteractionServiceDeps } from './internal/usecase/interaction-service.ts';
|
||||
export { createInteractionService, visibleTo } from './internal/usecase/interaction-service.ts';
|
||||
export { CONTROL_HOLDERS, checkTemplate, handoverHappens, inputControl, INTERACTION_PHASES, outputControl, templateIsSafe, } from './internal/domain/control-handover.ts';
|
||||
export { createInteractionStore } from './internal/adapter/interaction-store.ts';
|
||||
export { chatMessageTable, taskViewTable } from './internal/adapter/interaction-schema.ts';
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* module: interaction —— 本模块要外界提供的能力(编码规范 §10.1 / §5.3)。
|
||||
*/
|
||||
import type { ChatMessage, StaffId, TaskId, ViewBlock } from '@contracts';
|
||||
import type { InteractionPhase } from './internal/domain/control-handover.ts';
|
||||
export type Clock = {
|
||||
now(): Date;
|
||||
};
|
||||
export type IdGenerator = {
|
||||
next(): string;
|
||||
};
|
||||
export type InteractionStore = {
|
||||
appendMessage(taskId: TaskId, message: ChatMessage): Promise<void>;
|
||||
listMessages(taskId: TaskId): Promise<ChatMessage[]>;
|
||||
putView(taskId: TaskId, view: {
|
||||
blocks?: readonly ViewBlock[];
|
||||
html?: string;
|
||||
}): Promise<void>;
|
||||
setPhase(taskId: TaskId, phase: InteractionPhase): Promise<void>;
|
||||
phaseOf(taskId: TaskId): Promise<InteractionPhase | undefined>;
|
||||
};
|
||||
/** 自由输入即人事件 —— app 注入 `event`(L1) */
|
||||
export type HumanEventPublisher = {
|
||||
publishHumanEvent(input: {
|
||||
taskId: TaskId;
|
||||
cardId: string;
|
||||
payload: Record<string, unknown>;
|
||||
}): Promise<void>;
|
||||
};
|
||||
/** 留痕 —— app 注入 `memory`(L2):时间线即任务记忆的可读投影 */
|
||||
export type MemoryRecorder = {
|
||||
append(input: {
|
||||
taskId: TaskId;
|
||||
kind: 'human-input';
|
||||
title: string;
|
||||
detail?: Record<string, unknown>;
|
||||
}): Promise<void>;
|
||||
};
|
||||
/** 收件人解析 —— app 注入 `org`(L1):SSE 只推给有权看的人 */
|
||||
export type Audience = {
|
||||
canSee(staffId: StaffId, taskId: TaskId): Promise<boolean>;
|
||||
};
|
||||
Reference in New Issue
Block a user