M2: 基座能力 —— memory / toolbox / channel / message

- memory   任务记忆四性质(只追加·信封盖戳·治理先行·归侧)、活动记忆四层与条目三件套、
           任务日志两级投影(结构化零 LLM / 叙述性小结不入事实库)、度量八指标只读投影
- toolbox  注册两审(静态审 + 试运行审)、效应等级决定运行时待遇、契约演进只增不破坏、
           白名单与纯工具私有作用域、ToolInvoker(两缝之二,live/record/replay)
- channel  通道实例配置、入站归一(解析 → 系统写 → 数据事件)、出站发送与关联记录登记
- message  通知 / 跨任务征答(就地作答 · 驳回重答只增不改)/ 死信 / 无主兜底

app:message 接上 event 的 MessageFallback;派发器按目标 ID 取 advisory lock 串行消费 +
消费幂等表;/api/ingest 模拟邮件通道(mock 只在外界)

已验证:无主 RFQ 邮件入站 → 归属解析 no-match → 事件落库 → 兜底落 OP 岗位的消息要素
门禁五道全绿;150 个单元测试 + 1 个 PG 集成测试

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:16:52 +08:00
co-authored by Claude Opus 5
parent 14d4fa046a
commit f0444f1417
73 changed files with 7488 additions and 31 deletions
+42
View File
@@ -0,0 +1,42 @@
/**
* 入站路由 —— 模拟邮件通道的注入口(编码规范 §3;README MVP 完成判据)。
*
* mock 只允许出现在**外界**:这条路由代替真实 IMAP 拉件,
* 之后的归属解析、事件流、三级消费、系统写全部是真机制。
*/
import type { Hono } from 'hono';
import type { ApiError } from '@contracts';
import { ingestMailRoute } from '@contracts';
import { ChannelDisabledError, ChannelNotFoundError } from '@modules/channel';
import type { Container } from '../container.ts';
import type { AppEnv } from '../http-env.ts';
export function registerIngestRoutes(app: Hono<AppEnv>, container: Container): void {
app.post(ingestMailRoute.path, async (c) => {
const parsed = ingestMailRoute.body.safeParse(await c.req.json());
if (!parsed.success) {
const body: ApiError = { code: 'validation-failed', message: '入站邮件不合 schema', detail: parsed.error.issues };
return c.json(body, 400);
}
try {
const outcome = await container.channel.ingestMail(c.req.param('channelId'), parsed.data);
return c.json({
eventId: outcome.eventId,
resolution: outcome.resolution,
listenersHit: outcome.listenersHit,
triggersHit: outcome.triggersHit,
fellBackToMessage: outcome.fellBackToMessage,
});
} catch (error) {
if (error instanceof ChannelNotFoundError) {
const body: ApiError = { code: 'not-found', message: error.message };
return c.json(body, 404);
}
if (error instanceof ChannelDisabledError) {
const body: ApiError = { code: 'precondition-failed', message: error.message };
return c.json(body, 412);
}
throw error;
}
});
}