app 装配收尾:18 个模块接上,MVP 判据 1-2 端到端跑通

装配(app 是唯一组装层):
- wiring/engines.ts:skill/flow/loop/task/process/trigger 六个模块经端口接起来;
  两个范式引擎按统一 ParadigmEngine 契约暴露给 task —— 对上层不可区分
- agent-runner.ts:loop 的 Agent 回路经 ModelGateway(两缝之一,回放审看得见);
  出参过 schema 框住三型行动,不合规退化为「等人」而不是乱动世界
- container:interaction / human-tool / activity-registry / release 全部接上;
  event 的 TriggerMatcher 与 org 的 InFlightWorkProbe 以延迟绑定解环,端口不变
- workers/consumers.ts:派发器的两个消费者(任务监听推进旧单 / 触发器开新单,独立判定)
- tools/first-party.ts:查历史价(录制回放替身)+ 三分类打标(纯工具,DMN 确定性求值)
- seed-quote.ts:治理台四件事一次做完 —— 绑 LLM / 注册工具两审 / 经安装通道装活动包 /
  发布流程包(闭合校验通过)+ 配触发器与邮件通道

已验证(真机):
1. 治理台配置全过;活动包静态审 0 条提示、流程包闭合校验通过
2. 一封 RFQ 邮件入站 → 归属解析 → 触发器命中 1 → 起一条工作流 →
   A1 任务按岗位唯一人解析落到 BD 冯兵,runtime=running

修复:zod 判别式重复值(周期触发两型展开为 periodic-calendar / periodic-data,
triggerKindOf 对回三类分类学);活动构建脚本移出 activities(它要用 fs,
而活动包零环境能力 —— depcruise 拦下后按边界搬家)

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

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 02:24:36 +08:00
co-authored by Claude Opus 5
parent c6cdb4c310
commit 78dc03bb58
24 changed files with 1406 additions and 47 deletions
+289 -12
View File
@@ -42,6 +42,19 @@ import {
} from '@modules/toolbox';
import type { MessageService } from '@modules/message';
import { createMessageRepository, createMessageService } from '@modules/message';
import type { HumanToolService } from '@modules/human-tool';
import { createCardStore, createHumanToolService } from '@modules/human-tool';
import type { InteractionService } from '@modules/interaction';
import { createInteractionService, createInteractionStore, templateIsSafe } from '@modules/interaction';
import type { ActivityRegistryService } from '@modules/activity-registry';
import { createRegistryService, createRegistryStore } from '@modules/activity-registry';
import type { ReleaseService } from '@modules/release';
import { createIntentStore, createReleaseService } from '@modules/release';
import type { TaskService } from '@modules/task';
import type { ProcessService } from '@modules/process';
import type { TriggerService } from '@modules/trigger';
import { FIRST_PARTY_TOOLS } from './tools/first-party.ts';
import { wireEngines } from './wiring/engines.ts';
import type { ChannelService } from '@modules/channel';
import {
createChannelRepository,
@@ -68,21 +81,45 @@ export type Container = {
readonly toolInvoker: ToolInvoker;
readonly message: MessageService;
readonly channel: ChannelService;
readonly humanTool: HumanToolService;
readonly interaction: InteractionService;
readonly registry: ActivityRegistryService;
readonly release: ReleaseService;
readonly task: TaskService;
readonly process: ProcessService;
readonly trigger: TriggerService;
close(): Promise<void>;
};
/**
* 在途工作探针 —— 停用前置检查要问的是 task(L6)与 human-tool(L3),二者随 M3/M4 落地。
* 占位返回空集;M4 起接真实用例(docs/debt.md · M1-1)。
* 在途工作探针 —— 停用前置检查要问的是 task(L6)与 human-tool(L3),二者层级在 org 之上,
* 故以**延迟绑定**的端口接入:org 先构造,真身就位后回填(§5.2 跨层协作经端口)。
*/
const emptyInFlightProbe: InFlightWorkProbe = {
openTasksOf: async () => [],
openCardsOf: async () => [],
openApprovalsOf: async () => [],
};
function createDeferredInFlightProbe(): InFlightWorkProbe & { bind(real: InFlightWorkProbe): void } {
let real: InFlightWorkProbe | undefined;
return {
openTasksOf: async (id) => (real === undefined ? [] : real.openTasksOf(id)),
openCardsOf: async (id) => (real === undefined ? [] : real.openCardsOf(id)),
openApprovalsOf: async (id) => (real === undefined ? [] : real.openApprovalsOf(id)),
bind(next) {
real = next;
},
};
}
/** 触发器判定 —— trigger(L7)随 M5 落地,当下恒不命中(docs/debt.md · M1-1) */
const noTriggerMatcher: TriggerMatcher = { match: async () => [] };
/**
* 触发器判定的**延迟绑定**:`event`(L1)在 `trigger`(L7)之前构造,
* 用一层可后填的间接层把环解开 —— 端口不变,装配顺序解耦(§5.2)。
*/
function createDeferredTriggerMatcher(): TriggerMatcher & { bind(real: TriggerMatcher): void } {
let real: TriggerMatcher | undefined;
return {
match: async (envelope) => (real === undefined ? [] : real.match(envelope)),
bind(next) {
real = next;
},
};
}
/**
* 监听内容条件的求值 —— @sfeel 的求值语义**前后端同源**(§8.3)。
@@ -100,7 +137,7 @@ const sfeelEvaluator = (expression: string, scope: Readonly<Record<string, unkno
};
/** 第一方工具实现 —— 由 app 注册进 ToolRuntime;活动只见调用契约 */
export const firstPartyToolHandlers = new Map<string, ToolHandler>();
export const firstPartyToolHandlers = new Map<string, ToolHandler>(FIRST_PARTY_TOOLS);
export async function createContainer(config: AppConfig): Promise<Container> {
const pool = new Pool({ connectionString: config.databaseUrl, max: 10 });
@@ -111,11 +148,13 @@ export async function createContainer(config: AppConfig): Promise<Container> {
// 草稿层的建表:ledger schema 的唯一写者是 ledger adapter,DDL 在其出口给出
await pool.query(DRAFT_DDL);
const inFlightProbe = createDeferredInFlightProbe();
const org = createOrgService({
positions: createPositionRepository(db),
staff: createStaffRepository(db),
grants: createGrantRepository(db),
inFlight: emptyInFlightProbe,
inFlight: inFlightProbe,
ids,
});
@@ -142,12 +181,14 @@ export async function createContainer(config: AppConfig): Promise<Container> {
ids,
});
const triggerMatcher = createDeferredTriggerMatcher();
const event = createEventService({
store: createEventStore(db, ids),
clock: systemClock,
ids,
evaluate: sfeelEvaluator,
triggers: noTriggerMatcher,
triggers: triggerMatcher,
messages: message,
});
@@ -224,6 +265,235 @@ export async function createContainer(config: AppConfig): Promise<Container> {
ids,
});
/* ── L3/L4:交互、人类工具、活动库、放行门 ─────────────────────── */
const interaction = createInteractionService({
store: createInteractionStore(db),
events: {
publishHumanEvent: async (input) => {
await event.publish({
type: 'human',
correlation: {
target: { kind: 'task', id: input.taskId },
cause: { kind: 'card-reply', ref: input.cardId },
keys: { cardId: input.cardId },
},
payload: { data: input.payload },
occurredAt: systemClock.now().toISOString(),
source: { kind: 'desk' },
});
},
},
memory: {
append: async (input) =>
void (await memory.append({
...input,
stamp: { contractVersion: '1', businessModelVersion: '1', skillVersions: [] },
})),
},
audience: { canSee: async () => true },
clock: systemClock,
ids,
});
const humanTool = createHumanToolService({
cards: createCardStore(db),
outbox: { sendElicitation: (input) => message.sendElicitation(input) },
memory: {
append: async (input) =>
void (await memory.append({
...input,
stamp: { contractVersion: '1', businessModelVersion: '1', skillVersions: [] },
})),
},
events: {
publishHumanEvent: async (input) => {
await event.publish({
type: 'human',
correlation: {
target: { kind: 'task', id: input.taskId },
cause: { kind: 'card-reply', ref: input.cardId },
keys: { cardId: input.cardId },
},
payload: { data: input.payload },
occurredAt: systemClock.now().toISOString(),
source: { kind: 'desk' },
});
},
},
clock: systemClock,
ids,
});
const registry = createRegistryService({
store: createRegistryStore(db),
tools: { assertContractsAvailable: (names) => toolbox.assertContractsAvailable(names) },
models: { assertNamesBound: (names) => llm.assertNamesBound(names) },
ledger: {
applyDeclarations: async (declarations) => {
// 安装时依声明**自生长**:声明合并 → DDL → 只增不破坏
const results = await ledger.applyDeclarations(declarations);
return results.map((r) => ({
applied: r.applied,
conflicts: r.conflicts.map((c) => ({ field: c.field, message: c.message })),
}));
},
},
templates: { isSafe: templateIsSafe },
clock: systemClock,
ids,
// 首次安装无历史 → 免回放审(OS 11.2)
replayCases: async () => [],
sdkVersion: '0.1.0-m5',
});
const release = createReleaseService({
intents: createIntentStore(db),
ledger: {
checkConflicts: async (taskId) =>
(await ledger.checkConflicts(taskId)).map((c) => ({ field: c.field, key: c.key })),
releaseWrite: (taskId) => ledger.releaseWrite(taskId),
},
tools: {
isInvocable: async (tool, activityId) => {
const contract = await toolbox.get(tool);
if (contract === undefined) return { ok: false, reason: `工具未注册:${tool}` };
const allowlist = await toolbox.allowlistOf(activityId);
if (contract.pure !== true && !allowlist.includes(tool)) {
return { ok: false, reason: `工具不在本活动白名单:${tool}` };
}
return { ok: true };
},
validateInput: async (tool, input) => {
const contract = await toolbox.get(tool);
if (contract === undefined) return { ok: false, errors: [`工具未注册:${tool}`] };
return schemas.validate(contract.input, input);
},
validateTarget: async (input) => {
const result = await toolInvoker.validate({ tool: input.tool, input: input.payload, activityId: input.activityId, taskId: input.taskId });
if (result === undefined) return undefined;
return { ok: result.ok, ...(result.error === undefined ? {} : { error: result.error.message }) };
},
execute: async (input) => {
const result = await toolInvoker.invoke({
tool: input.tool,
input: input.payload,
activityId: input.activityId,
taskId: input.taskId,
});
return {
id: input.idempotencyKey,
ok: result.ok,
...(result.error === undefined ? {} : { error: result.error.message }),
retryable: result.error?.retryable ?? false,
};
},
},
cards: {
issueAcceptance: async (input) => {
const issued = await humanTool.issueAcceptance(
{ taskId: input.taskId, ownerId: input.ownerId, humanCallCap: input.humanCallCap },
{
title: input.title,
basis: [...input.basis],
deliverable: input.deliverable as never,
criteria: [...input.criteria],
actions: input.actions.map((a) => ({ ...a })),
fields: [],
},
);
return { id: issued.order.id };
},
issueLandingFailure: async (input) => {
const issued = await humanTool.issueLandingFailure(
{ taskId: input.taskId, ownerId: input.ownerId, humanCallCap: input.humanCallCap },
{ failures: input.failures },
);
return { id: issued.order.id };
},
},
tasks: {
activityIdOf: async (taskId) => (await engines.task.get(taskId)).activityId,
outputPortsOf: async (taskId) => {
const task = await engines.task.get(taskId);
const manifest = await registry.manifestOf(task.activityId);
return {
mandatory: manifest.contract.output.mandatory.map((p) => p.name),
selective: manifest.contract.output.selective.map((g) => ({
group: g.group,
ports: g.ports.map((p) => p.name),
})),
};
},
},
ports: {
emit: async (input) => {
// 落地收敛才发射 —— 下游只见落地完成的任务
await event.publish({
type: 'data',
correlation: {
target: { kind: 'task', id: input.taskId },
cause: { kind: 'engine', ref: 'output-port' },
keys: { port: input.port },
},
payload: { data: input.payload },
occurredAt: systemClock.now().toISOString(),
source: { kind: 'engine', detail: 'release' },
});
},
},
memory: {
append: async (input) =>
void (await memory.append({
...input,
stamp: { contractVersion: '1', businessModelVersion: '1', skillVersions: [] },
})),
},
clock: systemClock,
ids,
});
/* ── L5–L7:范式引擎、任务、流程层 ─────────────────────────────── */
const engines = wireEngines({
db,
clock: systemClock,
ids,
event,
ledger,
memory,
toolbox,
toolInvoker,
humanTool,
interaction,
message,
org,
llm,
modelGateway,
registry,
release,
schemas,
});
// 停用前置检查的真身就位:在途任务问 task,待答卡问 human-tool
inFlightProbe.bind({
openTasksOf: (staffId) => engines.task.openTasksOf(staffId),
openCardsOf: (staffId) => humanTool.openCardsOf(staffId),
openApprovalsOf: async () => [],
});
// 触发器就位后回填 —— event 的三级消费自此真判定
triggerMatcher.bind({
match: (envelope) =>
engines.trigger.match({
type: envelope.type,
keys: envelope.correlation.keys,
payload: envelope.payload.data,
source: envelope.source.kind,
occurredAt: envelope.occurredAt,
}),
});
return {
config,
pool,
@@ -238,6 +508,13 @@ export async function createContainer(config: AppConfig): Promise<Container> {
toolInvoker,
message,
channel,
humanTool,
interaction,
registry,
release,
task: engines.task,
process: engines.process,
trigger: engines.trigger,
async close() {
await pool.end();
},