forked from ChaiHao/BPOS-MVP
勘察(两路 code-explorer)坐实 loop 范式三处真缺口,本轮先修(用户拍板), 与 Mastra 化解耦、按外科手术做(活动内 MVP 期高内聚不过度抽象): ① 提交放行走不到 released:submit/判齐只返回 submitted,task 层当 running 处理,任务原地重跑到预算耗尽(exhausted 而非 released)。 修:提交 = 发**验收卡**挂起等裁决(raiseAcceptance 唯一出口:发卡、立监听、 在途验收入状态信封 sys.loop_state.pending,迁移 0015);判齐重入不发第二张卡; 裁决 pass → finished released(选口随放行带回),reject → finished 不放行。 TurnOutcome 增 finished/released/selectedPorts,engines 接线透传。 ② onEvent 双拍:自跑一整拍后 deliver 的 tick 再跑一拍 —— 每次人卡回传多耗 一次不计 steps/activeMs 预算的模型决策。修:onEvent **只记不跑** (登记 occur / 收验收裁决入信封,存快照即返),推进统一归 tick,预算一个口径。 ③ Agent 每拍失忆:memory.window 定义了但零调用,任务自身轨迹从不喂回 prompt。 修:叙事面新增【本任务最近轨迹】段,窗口大小取活动声明的 manifest.declarations.harness.memoryWindow(活动侧唯一记忆配置,不再硬编码)。 测试:turn-runner-acceptance 八例;全量绿,五闸退出码过;loop CONTRACT.md 同步。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
159 lines
5.5 KiB
TypeScript
159 lines
5.5 KiB
TypeScript
/**
|
|
* module: loop-engine —— 本模块要外界提供的能力(编码规范 §10.1 / §5.3)。
|
|
*/
|
|
import type { LoopModel, StaffId, TaskId } from '@contracts';
|
|
import type { CaseModel } from './internal/domain/cmmn-parse.ts';
|
|
import type { CaseRuntime, EnabledSet } from './internal/domain/lifecycle.ts';
|
|
import type { AgentAction } from './contract.ts';
|
|
export type Clock = {
|
|
now(): Date;
|
|
};
|
|
/** 运行时实例状态 —— loop 的模型定位与就绪集(OS 09 三份持久状态之一) */
|
|
export type LoopStateStore = {
|
|
save(taskId: TaskId, model: LoopModel, parsed: CaseModel, runtime: CaseRuntime, pending?: PendingAcceptance | null): Promise<void>;
|
|
load(taskId: TaskId): Promise<{
|
|
model: LoopModel;
|
|
parsed: CaseModel;
|
|
runtime: CaseRuntime;
|
|
pending?: PendingAcceptance;
|
|
} | undefined>;
|
|
};
|
|
/** S-FEEL —— app 注入 `@sfeel`;引擎零 LLM */
|
|
export type Sfeel = {
|
|
condition(expression: string, scope: Readonly<Record<string, unknown>>): boolean;
|
|
expression(expression: string, scope: Readonly<Record<string, unknown>>): unknown;
|
|
unaryTest(cell: string, value: unknown, scope: Readonly<Record<string, unknown>>): boolean;
|
|
};
|
|
/** 任务数据 —— app 注入 `ledger`(L1):引擎对**任务视图**求值,不依赖事件 */
|
|
export type TaskDataGateway = {
|
|
view(taskId: TaskId): Promise<Record<string, unknown>>;
|
|
draftWrite(input: {
|
|
taskId: TaskId;
|
|
domain: string;
|
|
object: string;
|
|
key: Record<string, string | number>;
|
|
patch: Record<string, unknown>;
|
|
}): Promise<void>;
|
|
};
|
|
/**
|
|
* Agent 回路 —— app 注入(Mastra Agent 在 adapter,§6.1)。
|
|
* **回合由 usecase 驱动,不放开自由 maxSteps,预算在环外由 Harness 强制。**
|
|
*/
|
|
export type AgentPort = {
|
|
decide(input: {
|
|
taskId: TaskId;
|
|
/** 叙事面:说明书 + 活动记忆选摘 + 任务记忆窗口 */
|
|
narrative: string;
|
|
enabled: EnabledSet;
|
|
scope: Record<string, unknown>;
|
|
}): Promise<AgentAction>;
|
|
};
|
|
/** 工具 / 人类工具 / 监听 —— 三型行动的落点 */
|
|
export type ToolGateway = {
|
|
invoke(input: {
|
|
tool: string;
|
|
input: Record<string, unknown>;
|
|
activityId: string;
|
|
taskId: TaskId;
|
|
}): Promise<{
|
|
ok: boolean;
|
|
output?: Record<string, unknown>;
|
|
error?: {
|
|
message: string;
|
|
};
|
|
}>;
|
|
isWriteEffect(tool: string): Promise<boolean>;
|
|
registerIntent(input: {
|
|
taskId: TaskId;
|
|
tool: string;
|
|
input: Record<string, unknown>;
|
|
label: string;
|
|
}): Promise<void>;
|
|
};
|
|
/**
|
|
* 在途验收(提交放行的持久痕):卡发出即记,裁决到达再记 verdict ——
|
|
* 判齐重入不再发第二张卡;恢复的那一拍据 verdict 定终局(released 与否)。
|
|
*/
|
|
export type PendingAcceptance = {
|
|
readonly acceptanceCardId: string;
|
|
readonly verdict?: 'pass' | 'reject';
|
|
readonly selectedPorts?: Record<string, string>;
|
|
};
|
|
export type CardGateway = {
|
|
issue(input: {
|
|
taskId: TaskId;
|
|
ownerId: StaffId;
|
|
humanCallCap: number;
|
|
workOrder: 'execution' | 'acceptance';
|
|
card: Record<string, unknown>;
|
|
}): Promise<{
|
|
id: string;
|
|
}>;
|
|
};
|
|
export type ListenerRegistrar = {
|
|
register(input: {
|
|
taskId: TaskId;
|
|
eventType: 'data' | 'time' | 'human';
|
|
keyFilter: Record<string, string>;
|
|
deadline: string;
|
|
}): Promise<{
|
|
id: string;
|
|
}>;
|
|
};
|
|
/**
|
|
* 视图流控 —— app 注入 `interaction`(L3)。
|
|
* 「loop:AI 按定位与局面临场组织视图流」(AM 07)。MVP 每回合推一次**案例全景**:
|
|
* 阶段与计划项的生命周期态、当下可为、目标缺口 —— 这些是语义引擎算出来的确定事实,
|
|
* 零 LLM 即可成屏;模型只决定「下一步做什么」,不负责画面。
|
|
*/
|
|
export type ViewGateway = {
|
|
push(input: {
|
|
taskId: TaskId;
|
|
activityId: string;
|
|
template: string;
|
|
scope: Record<string, unknown>;
|
|
}): Promise<void>;
|
|
};
|
|
/**
|
|
* 技能 —— app 注入 `skill-engine`(L4)。
|
|
* 「渐进披露:常驻上下文**仅有技能目录**,按事加载正文、按需下钻引用」(AM 08)。
|
|
*/
|
|
export type SkillGateway = {
|
|
/** 目录:id + 何时适用 —— 只给这些,正文等 AI 说要才加载 */
|
|
index(activityId: string): Promise<ReadonlyArray<{
|
|
id: string;
|
|
label: string;
|
|
applicability: string;
|
|
}>>;
|
|
/** 装配入信封:这一回合装配了哪些技能及其版本,记入任务记忆 */
|
|
stamp(activityId: string, skillIds: readonly string[]): Promise<ReadonlyArray<{
|
|
id: string;
|
|
version: string;
|
|
}>>;
|
|
};
|
|
export type MemoryRecorder = {
|
|
append(input: {
|
|
taskId: TaskId;
|
|
kind: 'engine-transition' | 'evaluation' | 'thought' | 'action' | 'result' | 'observation';
|
|
title: string;
|
|
detail?: Record<string, unknown>;
|
|
}): Promise<void>;
|
|
/** 装载:按定位选摘活动记忆入 Agent 上下文(渐进披露) */
|
|
loadActivityMemory(activityId: string, limit: number): Promise<Array<{
|
|
layer: string;
|
|
statement: string;
|
|
}>>;
|
|
/** 本任务自己的最近轨迹(窗口大小由活动声明的 memoryWindow 定)—— 喂回叙事面,Agent 每拍不失忆 */
|
|
window(taskId: TaskId): Promise<Array<{
|
|
kind: string;
|
|
title: string;
|
|
}>>;
|
|
};
|
|
export type TaskContext = {
|
|
of(taskId: TaskId): Promise<{
|
|
activityId: string;
|
|
ownerId: StaffId;
|
|
humanCallCap: number;
|
|
}>;
|
|
};
|