forked from ChaiHao/BPOS-MVP
M3(2/5): human-tool 与 release —— 卡协议与放行门
human-tool(L3): - 三规矩机检:调用必附上下文(裸调用拒发)/ 回传即留痕 / 完成只出自放行通过 - 工单两模式 + 对话卡族的形态校验与回传校验;驳回必附意见;补全卡「没法填」旁路; 决策卡自定义收尾项必附理由;已答不得再答(只增不改) - 到达面两路(负责人 → 任务对话流;他人 → 系统消息);驳回重答 = 新卡实例、原卡闭合 - 调人预算由基座在环外强制;落地失败卡 = 执行工单的 preset release(L4): - 落地预检五项(入参 schema / 工具可用与授权 / 连接可达 / 目标预校验 / 数据冲突) - 预检全过才发验收工单,卡面即已验证的动作清单;不过即打回环内,不发卡 - 放行写合并 + 幂等落地(任务 × 意向);落地收敛才发射输出口; 受阻走落地失败卡且 emittedPorts 恒空;已成动作留存不回滚 215 个测试通过,门禁五道全绿 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
146e9bbfad
commit
561a2474ae
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* module: human-tool —— 对外类型(编码规范 §10.1)。
|
||||
*
|
||||
* **双层边界**(§9):范式只在引擎层;本模块是前端呈现协议的后端面 ——
|
||||
* 工单两模式(执行 / 验收)+ 对话卡族(决策 / 补全 / 评价)。
|
||||
* 落地失败卡是**执行工单的系统预置形态**,不构成第五种卡(OS 7.3)。
|
||||
*/
|
||||
import type {
|
||||
AcceptanceWorkOrder,
|
||||
ChatCard,
|
||||
ChatCardReply,
|
||||
ExecutionWorkOrder,
|
||||
StaffId,
|
||||
TaskId,
|
||||
WorkOrder,
|
||||
WorkOrderReply,
|
||||
} from '@contracts';
|
||||
import type { ArrivalSurface, CardViolation } from './internal/domain/card-rules.ts';
|
||||
|
||||
export type {
|
||||
AcceptanceWorkOrder,
|
||||
ArrivalSurface,
|
||||
CardViolation,
|
||||
ChatCard,
|
||||
ChatCardReply,
|
||||
ExecutionWorkOrder,
|
||||
WorkOrder,
|
||||
WorkOrderReply,
|
||||
};
|
||||
|
||||
export class BareCallError extends Error {
|
||||
constructor(readonly violations: readonly CardViolation[]) {
|
||||
super(`基座拒发:${violations.map((v) => v.message).join('; ')}`);
|
||||
this.name = 'BareCallError';
|
||||
}
|
||||
}
|
||||
|
||||
export class ReplyRejectedError extends Error {
|
||||
constructor(readonly violations: readonly CardViolation[]) {
|
||||
super(`回传校验未过:${violations.map((v) => v.message).join('; ')}`);
|
||||
this.name = 'ReplyRejectedError';
|
||||
}
|
||||
}
|
||||
|
||||
export class HumanCallBudgetExceededError extends Error {
|
||||
constructor(
|
||||
readonly taskId: TaskId,
|
||||
readonly used: number,
|
||||
readonly cap: number,
|
||||
) {
|
||||
super(`调人预算触顶:${used}/${cap}(打扰上限在契约授权边界,由 Harness 强制)`);
|
||||
this.name = 'HumanCallBudgetExceededError';
|
||||
}
|
||||
}
|
||||
|
||||
/** 工单草稿:id / at 由基座补齐 —— ID 生成不外泄给调用方(domain 零随机) */
|
||||
export type ExecutionWorkOrderDraft = Omit<ExecutionWorkOrder, 'mode' | 'done' | 'id' | 'at'> & {
|
||||
readonly id?: ExecutionWorkOrder['id'] | undefined;
|
||||
readonly at?: string | undefined;
|
||||
};
|
||||
export type AcceptanceWorkOrderDraft = Omit<AcceptanceWorkOrder, 'mode' | 'done' | 'id' | 'at'> & {
|
||||
readonly id?: AcceptanceWorkOrder['id'] | undefined;
|
||||
readonly at?: string | undefined;
|
||||
};
|
||||
|
||||
/** 发卡的公共入参 */
|
||||
export type IssueContext = {
|
||||
readonly taskId: TaskId;
|
||||
/** 任务负责人 —— 决定到达面两路 */
|
||||
readonly ownerId: StaffId;
|
||||
/** 收件人;缺省即负责人 */
|
||||
readonly recipientId?: StaffId;
|
||||
/** 契约的调人预算(打扰上限) */
|
||||
readonly humanCallCap: number;
|
||||
};
|
||||
|
||||
export type IssuedWorkOrder = {
|
||||
readonly order: WorkOrder;
|
||||
readonly arrival: ArrivalSurface;
|
||||
/** 到达面 = system-message 时的消息 ID(跨人卡) */
|
||||
readonly messageId?: string;
|
||||
};
|
||||
|
||||
export type IssuedChatCard = {
|
||||
readonly card: ChatCard;
|
||||
readonly arrival: ArrivalSurface;
|
||||
readonly messageId?: string;
|
||||
};
|
||||
|
||||
export type ReplyOutcome = {
|
||||
readonly order: WorkOrder;
|
||||
/** 完成只出自放行通过 —— 这里为真,任务才可收敛终态 */
|
||||
readonly producesCompletion: boolean;
|
||||
/** 驳回:环内回转,任务继续 */
|
||||
readonly rejection?: { readonly kind: 'redo' | 'revise'; readonly comment: string };
|
||||
/** 选择组的出口选择 —— 随放行一并确认(AM 1.2) */
|
||||
readonly selectedPorts: Readonly<Record<string, string>>;
|
||||
};
|
||||
|
||||
export type HumanToolService = {
|
||||
/** 发执行工单;裸调用拒发,预算触顶熔断。id / at 留空即由基座生成 */
|
||||
issueExecution(context: IssueContext, order: ExecutionWorkOrderDraft): Promise<IssuedWorkOrder>;
|
||||
/** 发验收工单 —— 发卡**前**必须已过落地预检(由 release 保证,见其 CONTRACT) */
|
||||
issueAcceptance(context: IssueContext, order: AcceptanceWorkOrderDraft): Promise<IssuedWorkOrder>;
|
||||
/** 落地失败卡:执行工单的系统预置形态,不是第五种卡 */
|
||||
issueLandingFailure(
|
||||
context: IssueContext,
|
||||
input: { readonly failures: ReadonlyArray<{ label: string; reason: string }> },
|
||||
): Promise<IssuedWorkOrder>;
|
||||
issueChatCard(context: IssueContext, card: ChatCard): Promise<IssuedChatCard>;
|
||||
|
||||
replyWorkOrder(reply: WorkOrderReply, by: StaffId): Promise<ReplyOutcome>;
|
||||
replyChatCard(reply: ChatCardReply, by: StaffId): Promise<ChatCard>;
|
||||
/** 驳回重答:新卡实例,原卡闭合 —— 只增不改 */
|
||||
rejectAndReask(cardId: string, reason: string): Promise<IssuedChatCard>;
|
||||
|
||||
openWorkOrders(taskId: TaskId): Promise<WorkOrder[]>;
|
||||
chatCards(taskId: TaskId): Promise<ChatCard[]>;
|
||||
/** org 停用前置检查要问的「待答卡」 */
|
||||
openCardsOf(staffId: StaffId): Promise<Array<{ cardId: string; title: string }>>;
|
||||
};
|
||||
Reference in New Issue
Block a user