forked from ChaiHao/BPOS-MVP
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:
co-authored by
Claude Opus 5
parent
14d4fa046a
commit
f0444f1417
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* module: channel —— 对外类型(编码规范 §10.1)。
|
||||
*/
|
||||
import type { InboundMail, OwnershipResolution, TaskId } from '@contracts';
|
||||
import type { ChannelInstanceConfig, ExtractRule, LedgerMapping, NormalizedWrite } from './internal/domain/payload-mapping.ts';
|
||||
export type { ChannelInstanceConfig, ExtractRule, InboundMail, LedgerMapping, NormalizedWrite, OwnershipResolution };
|
||||
export declare class ChannelNotFoundError extends Error {
|
||||
readonly channelId: string;
|
||||
constructor(channelId: string);
|
||||
}
|
||||
export declare class ChannelDisabledError extends Error {
|
||||
readonly channelId: string;
|
||||
constructor(channelId: string);
|
||||
}
|
||||
export type InboundOutcome = {
|
||||
readonly eventId: string;
|
||||
readonly resolution: OwnershipResolution;
|
||||
readonly writes: readonly NormalizedWrite[];
|
||||
readonly listenersHit: number;
|
||||
readonly triggersHit: number;
|
||||
readonly fellBackToMessage: boolean;
|
||||
};
|
||||
export type OutboundMail = {
|
||||
readonly taskId: TaskId;
|
||||
readonly to: readonly string[];
|
||||
readonly cc?: readonly string[];
|
||||
readonly subject: string;
|
||||
readonly body: string;
|
||||
readonly threadId?: string;
|
||||
readonly attachments?: ReadonlyArray<{
|
||||
name: string;
|
||||
ref: string;
|
||||
}>;
|
||||
};
|
||||
export type ChannelService = {
|
||||
list(): Promise<ChannelInstanceConfig[]>;
|
||||
upsert(config: ChannelInstanceConfig): Promise<ChannelInstanceConfig>;
|
||||
/**
|
||||
* 入站:归属解析 → 归一为**系统写** → 数据事件。
|
||||
* 「环外回音经归属解析归一为数据写入后,以数据事件抛出 —— 活动认数据,不认通道。」
|
||||
*/
|
||||
ingestMail(channelId: string, mail: InboundMail): Promise<InboundOutcome>;
|
||||
/** 出站:触达世界的动作;发送即登记出站关联记录(归属解析①③级的对表来源) */
|
||||
sendMail(channelId: string, mail: OutboundMail): Promise<{
|
||||
messageId: string;
|
||||
threadId: string;
|
||||
}>;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* module: channel —— 唯一出口。只做 re-export,不写逻辑(编码规范 §10.1)。
|
||||
*/
|
||||
export type { ChannelInstanceConfig, ChannelService, ExtractRule, InboundMail, InboundOutcome, LedgerMapping, NormalizedWrite, OutboundMail, } from './contract.ts';
|
||||
export { ChannelDisabledError, ChannelNotFoundError } from './contract.ts';
|
||||
export type { AttachmentArchive, ChannelRepository, Clock, EventGateway, IdGenerator, LedgerSystemWriter, MailTransport, } from './port.ts';
|
||||
export type { ChannelServiceDeps } from './internal/usecase/channel-service.ts';
|
||||
export { createChannelService } from './internal/usecase/channel-service.ts';
|
||||
export { extractFields, normalize } from './internal/domain/payload-mapping.ts';
|
||||
export { createChannelRepository, createFileAttachmentArchive, createSimulatedMailTransport, } from './internal/adapter/channel-store.ts';
|
||||
export { channelTable, outboundMailTable } from './internal/adapter/channel-schema.ts';
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* module: channel —— 本模块要外界提供的能力(编码规范 §10.1 / §5.3)。
|
||||
*/
|
||||
import type { EnvelopeDraft, OwnershipResolution, PositionId, TaskId } from '@contracts';
|
||||
import type { ChannelInstanceConfig } from './internal/domain/payload-mapping.ts';
|
||||
export type Clock = {
|
||||
now(): Date;
|
||||
};
|
||||
export type IdGenerator = {
|
||||
next(): string;
|
||||
};
|
||||
export type ChannelRepository = {
|
||||
list(): Promise<ChannelInstanceConfig[]>;
|
||||
get(channelId: string): Promise<ChannelInstanceConfig | undefined>;
|
||||
upsert(config: ChannelInstanceConfig): Promise<ChannelInstanceConfig>;
|
||||
};
|
||||
/** 事件入口 —— app 注入 `event`(L1)的出口 */
|
||||
export type EventGateway = {
|
||||
ingestInbound(input: {
|
||||
facts: {
|
||||
threadId?: string;
|
||||
from: string;
|
||||
to: readonly string[];
|
||||
subject: string;
|
||||
body: string;
|
||||
receivedAt: Date;
|
||||
};
|
||||
rules: {
|
||||
patterns: ReadonlyArray<{
|
||||
key: string;
|
||||
regex: string;
|
||||
source: 'subject' | 'body' | 'both';
|
||||
}>;
|
||||
addressWindowMs: number;
|
||||
fallbackPosition?: PositionId | undefined;
|
||||
};
|
||||
draft: EnvelopeDraft;
|
||||
}): Promise<{
|
||||
eventId: string;
|
||||
resolution: OwnershipResolution;
|
||||
plan: {
|
||||
taskTargets: readonly string[];
|
||||
triggerTargets: readonly string[];
|
||||
messageFallback: boolean;
|
||||
};
|
||||
}>;
|
||||
recordOutbound(ref: {
|
||||
taskId: TaskId;
|
||||
threadId?: string;
|
||||
address: string;
|
||||
sentAt: Date;
|
||||
}): Promise<void>;
|
||||
};
|
||||
/**
|
||||
* 台账系统写 —— app 注入 `ledger`(L1)的 `systemWrite`。
|
||||
* 入站归一是「登记世界已发生的事实」,走**系统写**:不过放行门,写正式版并即时抛数据事件(OS 09)。
|
||||
*/
|
||||
export type LedgerSystemWriter = {
|
||||
systemWrite(input: {
|
||||
address: {
|
||||
domain: string;
|
||||
object: string;
|
||||
};
|
||||
key: Record<string, string | number>;
|
||||
values: Record<string, unknown>;
|
||||
cause: {
|
||||
kind: 'inbound';
|
||||
ref: string;
|
||||
};
|
||||
taskId?: TaskId;
|
||||
}): Promise<void>;
|
||||
};
|
||||
/** 邮件收发 —— 外界。MVP 允许模拟通道 / 录制回放替身(README MVP 完成判据) */
|
||||
export type MailTransport = {
|
||||
send(input: {
|
||||
readonly from: string;
|
||||
readonly to: readonly string[];
|
||||
readonly cc: readonly string[];
|
||||
readonly subject: string;
|
||||
readonly body: string;
|
||||
readonly threadId?: string;
|
||||
readonly attachments: ReadonlyArray<{
|
||||
name: string;
|
||||
ref: string;
|
||||
}>;
|
||||
}): Promise<{
|
||||
messageId: string;
|
||||
threadId: string;
|
||||
}>;
|
||||
};
|
||||
/** 原件存档 —— 附件对象的存放;返回存档地址(信封的原件指针) */
|
||||
export type AttachmentArchive = {
|
||||
put(name: string, content: string): Promise<string>;
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* module: memory —— 对外类型(编码规范 §10.1)。
|
||||
*/
|
||||
import type { AttributionSide, TaskId } from '@contracts';
|
||||
import type { ActivityMemoryEntry, CandidateIncrement, Evidence, MemoryLayer } from './internal/domain/activity-memory.ts';
|
||||
import type { EntryKind, GovernanceMark, MemoryEntry, VersionStamp } from './internal/domain/task-memory.ts';
|
||||
import type { LogLine, Metrics, MetricsInput } from './internal/domain/task-log.ts';
|
||||
export type { ActivityMemoryEntry, AttributionSide, CandidateIncrement, EntryKind, Evidence, GovernanceMark, LogLine, MemoryEntry, MemoryLayer, Metrics, MetricsInput, VersionStamp, };
|
||||
export { NoEvidenceError } from './internal/domain/activity-memory.ts';
|
||||
/** 追加一条任务记忆 —— 归侧由 kind 自动推导,调用方不得指定(零埋点) */
|
||||
export type AppendEntry = {
|
||||
readonly taskId: TaskId;
|
||||
readonly kind: EntryKind;
|
||||
readonly title: string;
|
||||
readonly detail?: Record<string, unknown>;
|
||||
readonly stamp: VersionStamp;
|
||||
readonly governance?: Partial<GovernanceMark>;
|
||||
readonly linksTo?: string;
|
||||
readonly eventRef?: string;
|
||||
};
|
||||
export type DistillInput = {
|
||||
readonly taskId: TaskId;
|
||||
readonly activityId: string;
|
||||
readonly trigger: 'terminal' | 'milestone';
|
||||
readonly milestone?: string;
|
||||
readonly prompt: string;
|
||||
};
|
||||
export type MemoryService = {
|
||||
append(entry: AppendEntry): Promise<MemoryEntry>;
|
||||
history(taskId: TaskId): Promise<MemoryEntry[]>;
|
||||
/** 活动侧唯一可配置项:窗口大小 —— 每次 LLM 调用加载多少轮 */
|
||||
window(taskId: TaskId, size: number): Promise<MemoryEntry[]>;
|
||||
seal(taskId: TaskId): Promise<void>;
|
||||
taskLog(taskId: TaskId, options?: {
|
||||
canSeeMasked?: boolean;
|
||||
}): Promise<LogLine[]>;
|
||||
/** 叙述性小结(LLM,可选)—— 叙述是视图不是事实,不入事实库 */
|
||||
narrate(taskId: TaskId): Promise<string | undefined>;
|
||||
loadActivityMemory(activityId: string, options?: {
|
||||
layers?: readonly MemoryLayer[];
|
||||
limit?: number;
|
||||
}): Promise<ActivityMemoryEntry[]>;
|
||||
/** **不设直写旁路**:唯一写入口是蒸馏钩子 */
|
||||
distill(input: DistillInput): Promise<ActivityMemoryEntry[]>;
|
||||
/** 人可修剪 —— 治理台 · 记忆管理 */
|
||||
prune(entryId: string): Promise<void>;
|
||||
setIncrementState(entryId: string, state: CandidateIncrement['state']): Promise<void>;
|
||||
metrics(input: Omit<MetricsInput, 'entries'> & {
|
||||
taskIds: readonly TaskId[];
|
||||
}): Promise<Metrics>;
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* module: memory —— 唯一出口。只做 re-export,不写逻辑(编码规范 §10.1)。
|
||||
*/
|
||||
export type { ActivityMemoryEntry, AppendEntry, AttributionSide, CandidateIncrement, DistillInput, EntryKind, Evidence, GovernanceMark, LogLine, MemoryEntry, MemoryLayer, MemoryService, Metrics, MetricsInput, VersionStamp, } from './contract.ts';
|
||||
export { NoEvidenceError } from './contract.ts';
|
||||
export type { ActivityMemoryStore, Clock, IdGenerator, SystemModel, TaskMemoryStore } from './port.ts';
|
||||
export type { MemoryServiceDeps } from './internal/usecase/memory-service.ts';
|
||||
export { createMemoryService } from './internal/usecase/memory-service.ts';
|
||||
export { assertThreePart, isBinding, MEMORY_LAYER_LABEL, MEMORY_LAYER_TARGET, MEMORY_LAYERS, selectForLoading, } from './internal/domain/activity-memory.ts';
|
||||
export { assertAppendOnly, deriveSide, distillable, ENTRY_KINDS, redactForReader, windowOf, } from './internal/domain/task-memory.ts';
|
||||
export { computeMetrics, splitBySide, structuredLog } from './internal/domain/task-log.ts';
|
||||
export { createActivityMemoryStore, createTaskMemoryStore } from './internal/adapter/memory-store.ts';
|
||||
export { activityMemoryTable, taskMemorySealTable, taskMemoryTable } from './internal/adapter/memory-schema.ts';
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* module: memory —— 本模块要外界提供的能力(编码规范 §10.1 / §5.3)。
|
||||
*/
|
||||
import type { TaskId } from '@contracts';
|
||||
import type { ActivityMemoryEntry, MemoryLayer } from './internal/domain/activity-memory.ts';
|
||||
import type { MemoryEntry } from './internal/domain/task-memory.ts';
|
||||
export type Clock = {
|
||||
now(): Date;
|
||||
};
|
||||
export type IdGenerator = {
|
||||
next(): string;
|
||||
};
|
||||
export type TaskMemoryStore = {
|
||||
nextSeq(taskId: TaskId): Promise<number>;
|
||||
append(entry: MemoryEntry): Promise<void>;
|
||||
list(taskId: TaskId): Promise<MemoryEntry[]>;
|
||||
tail(taskId: TaskId, size: number): Promise<MemoryEntry[]>;
|
||||
seal(taskId: TaskId, at: Date): Promise<void>;
|
||||
isSealed(taskId: TaskId): Promise<boolean>;
|
||||
};
|
||||
export type ActivityMemoryStore = {
|
||||
list(activityId: string, layers: readonly MemoryLayer[] | undefined, limit: number): Promise<ActivityMemoryEntry[]>;
|
||||
insertMany(entries: readonly ActivityMemoryEntry[]): Promise<void>;
|
||||
prune(entryId: string, at: Date): Promise<void>;
|
||||
setIncrementState(entryId: string, state: string): Promise<void>;
|
||||
};
|
||||
/**
|
||||
* 模型调用 —— app 注入 `llm`(L1)的 ModelGateway。
|
||||
* 蒸馏钩子与叙述性小结用**系统用途**绑定实例:计量入系统账、不占活动预算(OS 02)。
|
||||
*/
|
||||
export type SystemModel = {
|
||||
generate(input: {
|
||||
readonly model: string;
|
||||
readonly prompt: string;
|
||||
readonly outputSchema?: Record<string, unknown>;
|
||||
}): Promise<{
|
||||
text: string;
|
||||
object?: unknown;
|
||||
}>;
|
||||
/** 系统用途模型名;未配置即 undefined —— 此时叙述与蒸馏静默跳过,不报错 */
|
||||
systemModelName(): Promise<string | undefined>;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* module: message —— 对外类型(编码规范 §10.1)。
|
||||
*/
|
||||
import type { Envelope, StaffId } from '@contracts';
|
||||
import type { MessageKind, SystemMessage } from './internal/domain/message-kind.ts';
|
||||
export type { Envelope, MessageKind, SystemMessage };
|
||||
export declare class MessageNotAnswerableError extends Error {
|
||||
readonly messageId: string;
|
||||
constructor(messageId: string);
|
||||
}
|
||||
export type SendNotice = {
|
||||
readonly staffId: StaffId;
|
||||
readonly title: string;
|
||||
readonly body: string;
|
||||
readonly eventRef?: string;
|
||||
};
|
||||
export type SendElicitation = {
|
||||
readonly staffId: StaffId;
|
||||
readonly title: string;
|
||||
readonly body: string;
|
||||
readonly cardId: string;
|
||||
readonly sourceTaskId: string;
|
||||
readonly payload: Record<string, unknown>;
|
||||
};
|
||||
export type MessageService = {
|
||||
inbox(staffId: StaffId, options?: {
|
||||
unreadOnly?: boolean;
|
||||
limit?: number;
|
||||
}): Promise<SystemMessage[]>;
|
||||
markRead(messageId: string): Promise<void>;
|
||||
notify(input: SendNotice): Promise<SystemMessage>;
|
||||
/** 跨人卡以系统消息送达对方,就地作答 —— 任务要素永远只装「我负责的任务」(OS 2.1) */
|
||||
sendElicitation(input: SendElicitation): Promise<SystemMessage>;
|
||||
answer(messageId: string, staffId: StaffId, answer: Record<string, unknown>): Promise<SystemMessage>;
|
||||
/** 驳回重答 = 新发一条系统消息(新卡实例),原卡闭合 —— 只增不改 */
|
||||
rejectAndReask(messageId: string, reason: string): Promise<SystemMessage>;
|
||||
/** 死信:重试超限 → 系统通知送达相关人的消息要素与治理台 */
|
||||
deadLetter(envelope: Envelope, error: string): Promise<void>;
|
||||
/** 无主兜底:相关人 = 通道实例配置的默认认领岗位 */
|
||||
unowned(envelope: Envelope, fallbackPosition: string | undefined): Promise<void>;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* module: message —— 唯一出口。只做 re-export,不写逻辑(编码规范 §10.1)。
|
||||
*/
|
||||
export type { MessageKind, MessageService, SendElicitation, SendNotice, SystemMessage } from './contract.ts';
|
||||
export { MessageNotAnswerableError } from './contract.ts';
|
||||
export type { Clock, IdGenerator, MessageRepository, PositionResolver } from './port.ts';
|
||||
export type { MessageServiceDeps } from './internal/usecase/message-service.ts';
|
||||
export { createMessageService } from './internal/usecase/message-service.ts';
|
||||
export { canAnswer, MESSAGE_KIND_LABEL, MESSAGE_KINDS, pendingAnswerCount, unreadCount, } from './internal/domain/message-kind.ts';
|
||||
export { createMessageRepository } from './internal/adapter/message-store.ts';
|
||||
export { messageTable } from './internal/adapter/message-schema.ts';
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* module: message —— 本模块要外界提供的能力(编码规范 §10.1 / §5.3)。
|
||||
*/
|
||||
import type { StaffId } from '@contracts';
|
||||
import type { SystemMessage } from './internal/domain/message-kind.ts';
|
||||
export type Clock = {
|
||||
now(): Date;
|
||||
};
|
||||
export type IdGenerator = {
|
||||
next(): string;
|
||||
};
|
||||
export type MessageRepository = {
|
||||
list(staffId: StaffId, unreadOnly: boolean, limit: number): Promise<SystemMessage[]>;
|
||||
get(messageId: string): Promise<SystemMessage | undefined>;
|
||||
insert(message: SystemMessage): Promise<SystemMessage>;
|
||||
markRead(messageId: string, at: Date): Promise<void>;
|
||||
markAnswered(messageId: string, at: Date, answer: Record<string, unknown>): Promise<void>;
|
||||
supersede(messageId: string, byMessageId: string, reason: string): Promise<void>;
|
||||
};
|
||||
/** 岗位解析 —— app 注入 `org`(L1):无主兜底要把消息落到默认认领岗位上的人 */
|
||||
export type PositionResolver = {
|
||||
staffOfPosition(positionId: string): Promise<Array<{
|
||||
id: StaffId;
|
||||
}>>;
|
||||
/** 治理台收件人(任务管理岗)—— 死信同时送治理台 */
|
||||
governanceStaff(): Promise<Array<{
|
||||
id: StaffId;
|
||||
}>>;
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* module: toolbox —— 对外类型(编码规范 §10.1)。
|
||||
*
|
||||
* 两条缝之二(§7.3):`ToolInvoker` 支持 record | replay | live ——
|
||||
* **任何工具执行不经 ToolInvoker,回放审就对它失明**(§12.5#10)。
|
||||
*/
|
||||
import type { ActionIntent, TaskId } from '@contracts';
|
||||
import type { AuditFinding } from './internal/domain/registration-audit.ts';
|
||||
import type { CallDisposition, EffectLevel, EvolutionViolation, Preauthorization, ToolContract } from './internal/domain/tool-contract.ts';
|
||||
export type { ActionIntent, AuditFinding, CallDisposition, EffectLevel, EvolutionViolation, Preauthorization, ToolContract };
|
||||
export declare const INVOKER_MODES: readonly ["live", "record", "replay"];
|
||||
export type InvokerMode = (typeof INVOKER_MODES)[number];
|
||||
export declare class ToolNotFoundError extends Error {
|
||||
readonly tool: string;
|
||||
constructor(tool: string);
|
||||
}
|
||||
export declare class ToolNotAllowedError extends Error {
|
||||
readonly tool: string;
|
||||
readonly activityId: string;
|
||||
constructor(tool: string, activityId: string);
|
||||
}
|
||||
export declare class ToolDisabledError extends Error {
|
||||
readonly tool: string;
|
||||
constructor(tool: string);
|
||||
}
|
||||
export declare class ToolReplayMissError extends Error {
|
||||
readonly key: string;
|
||||
constructor(key: string);
|
||||
}
|
||||
export declare class RegistrationRejectedError extends Error {
|
||||
readonly findings: readonly AuditFinding[];
|
||||
constructor(findings: readonly AuditFinding[]);
|
||||
}
|
||||
export type ToolCall = {
|
||||
readonly tool: string;
|
||||
readonly operation?: string;
|
||||
readonly input: Record<string, unknown>;
|
||||
readonly activityId: string;
|
||||
readonly taskId?: TaskId;
|
||||
readonly stepNo?: number;
|
||||
};
|
||||
export type ToolResult = {
|
||||
readonly ok: boolean;
|
||||
readonly output?: Record<string, unknown>;
|
||||
readonly error?: {
|
||||
readonly code: string;
|
||||
readonly retryable: boolean;
|
||||
readonly message: string;
|
||||
};
|
||||
readonly latencyMs: number;
|
||||
readonly mode: InvokerMode;
|
||||
};
|
||||
/** 调用留痕:入参快照、出参、时延与错误自动留痕入任务记忆(OS 7.3) */
|
||||
export type InvocationTrace = {
|
||||
readonly tool: string;
|
||||
readonly input: Record<string, unknown>;
|
||||
readonly result: ToolResult;
|
||||
readonly at: string;
|
||||
};
|
||||
/**
|
||||
* 工具调用器 —— 活动基座六件里的「工具(调用契约)」。
|
||||
* **读类**过程中自由调用;**写类**登记为动作意向,放行后落地(除契约显式预授权者)。
|
||||
*/
|
||||
export type ToolInvoker = {
|
||||
readonly mode: InvokerMode;
|
||||
/** 判定运行时待遇 —— 调用方据此决定是即调还是登记意向 */
|
||||
disposition(call: ToolCall, preauthorized: readonly Preauthorization[]): Promise<CallDisposition>;
|
||||
invoke(call: ToolCall): Promise<ToolResult>;
|
||||
/** 落地预检第四项:工具契约含 validate 能力时调之(ADR-005) */
|
||||
validate(call: ToolCall): Promise<ToolResult | undefined>;
|
||||
withMode(mode: InvokerMode): ToolInvoker;
|
||||
};
|
||||
export type RegisterToolInput = {
|
||||
readonly contract: ToolContract;
|
||||
/** 契约测试集:样例入参 → 期望出参形状;试运行审的依据 */
|
||||
readonly samples: ReadonlyArray<{
|
||||
input: Record<string, unknown>;
|
||||
expectShape: Record<string, unknown>;
|
||||
}>;
|
||||
/** 连接配置的值 —— 只存治理台,加密落库(ADR-009) */
|
||||
readonly connectionValues?: Readonly<Record<string, string>>;
|
||||
};
|
||||
export type RegistrationVerdict = {
|
||||
readonly accepted: boolean;
|
||||
readonly staticFindings: readonly AuditFinding[];
|
||||
readonly evolution: readonly EvolutionViolation[];
|
||||
readonly trialRun: {
|
||||
readonly ran: number;
|
||||
readonly passed: number;
|
||||
readonly failures: readonly string[];
|
||||
};
|
||||
};
|
||||
export type ToolboxService = {
|
||||
/** 注册两审:静态审读清单,试运行审验代码 */
|
||||
register(input: RegisterToolInput): Promise<RegistrationVerdict>;
|
||||
list(): Promise<ToolContract[]>;
|
||||
get(name: string): Promise<ToolContract | undefined>;
|
||||
/** 有活动声明引用的工具不得删除;可停用 —— 停用后调用返回结构化错误 */
|
||||
setEnabled(name: string, enabled: boolean): Promise<void>;
|
||||
bindConnection(name: string, values: Readonly<Record<string, string>>): Promise<void>;
|
||||
/** 白名单:活动只经调用契约用工具,授权在白名单与契约,不在活动代码 */
|
||||
setAllowlist(activityId: string, tools: readonly string[]): Promise<void>;
|
||||
allowlistOf(activityId: string): Promise<string[]>;
|
||||
/** 安装静态审:活动声明的工具契约齐备?装不齐不上架 */
|
||||
assertContractsAvailable(names: readonly string[]): Promise<{
|
||||
ok: boolean;
|
||||
missing: string[];
|
||||
}>;
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* module: toolbox —— 唯一出口。只做 re-export,不写逻辑(编码规范 §10.1)。
|
||||
*/
|
||||
export type { ActionIntent, AuditFinding, CallDisposition, EffectLevel, EvolutionViolation, InvocationTrace, InvokerMode, Preauthorization, RegisterToolInput, RegistrationVerdict, ToolCall, ToolContract, ToolInvoker, ToolResult, ToolboxService, } from './contract.ts';
|
||||
export { INVOKER_MODES, RegistrationRejectedError, ToolDisabledError, ToolNotAllowedError, ToolNotFoundError, ToolReplayMissError, } from './contract.ts';
|
||||
export type { Clock, IdGenerator, SchemaValidator, SecretBox, ToolRegistry, ToolReplayStore, ToolRuntime, } from './port.ts';
|
||||
export type { ToolInvokerDeps } from './internal/usecase/tool-invoker.ts';
|
||||
export { createToolInvoker, toolReplayKey } from './internal/usecase/tool-invoker.ts';
|
||||
export type { ToolboxServiceDeps } from './internal/usecase/toolbox-service.ts';
|
||||
export { createToolboxService } from './internal/usecase/toolbox-service.ts';
|
||||
export { checkEvolution, dispositionOf, EFFECT_LEVEL_LABEL, EFFECT_LEVELS } from './internal/domain/tool-contract.ts';
|
||||
export { auditPassed, staticAudit } from './internal/domain/registration-audit.ts';
|
||||
export type { ToolHandler } from './internal/adapter/toolbox-store.ts';
|
||||
export { createSchemaValidator, createToolRegistry, createToolReplayStore, createToolRuntime, isAllowed, } from './internal/adapter/toolbox-store.ts';
|
||||
export { replayToolTable, toolAllowlistTable, toolTable } from './internal/adapter/toolbox-schema.ts';
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* module: toolbox —— 本模块要外界提供的能力(编码规范 §10.1 / §5.3)。
|
||||
*/
|
||||
import type { ToolContract } from './internal/domain/tool-contract.ts';
|
||||
import type { ToolCall, ToolResult } from './contract.ts';
|
||||
export type Clock = {
|
||||
now(): Date;
|
||||
};
|
||||
export type IdGenerator = {
|
||||
next(): string;
|
||||
};
|
||||
/** 凭证密封 —— 与 llm 同一实现,由 app 注入(ADR-009) */
|
||||
export type SecretBox = {
|
||||
seal(plaintext: string): string;
|
||||
open(cipher: string): string;
|
||||
};
|
||||
export type ToolRegistry = {
|
||||
list(): Promise<ToolContract[]>;
|
||||
get(name: string): Promise<ToolContract | undefined>;
|
||||
isEnabled(name: string): Promise<boolean>;
|
||||
upsert(contract: ToolContract, connectionCipher: Record<string, string> | null): Promise<void>;
|
||||
setEnabled(name: string, enabled: boolean): Promise<void>;
|
||||
connectionCipher(name: string): Promise<Record<string, string> | undefined>;
|
||||
setAllowlist(activityId: string, tools: readonly string[]): Promise<void>;
|
||||
allowlistOf(activityId: string): Promise<string[]>;
|
||||
/** 有活动声明引用即不得删除 */
|
||||
referencedBy(name: string): Promise<string[]>;
|
||||
};
|
||||
/** 工具录制回放存储 —— 与 llm 的 ReplayStore 同构,另存一表(§7.3) */
|
||||
export type ToolReplayStore = {
|
||||
put(record: {
|
||||
key: string;
|
||||
call: ToolCall;
|
||||
result: ToolResult;
|
||||
recordedAt: Date;
|
||||
}): Promise<void>;
|
||||
get(key: string): Promise<{
|
||||
call: ToolCall;
|
||||
result: ToolResult;
|
||||
} | undefined>;
|
||||
};
|
||||
/**
|
||||
* 工具执行 —— 实现对系统:server 侧执行代码,运行时受限,
|
||||
* **只可访问注册申报的连接目标,无其他环境能力**(OS 7.3)。
|
||||
*/
|
||||
export type ToolRuntime = {
|
||||
execute(input: {
|
||||
readonly contract: ToolContract;
|
||||
readonly connection: Readonly<Record<string, string>>;
|
||||
readonly call: ToolCall;
|
||||
readonly idempotencyKey?: string;
|
||||
}): Promise<{
|
||||
ok: boolean;
|
||||
output?: Record<string, unknown>;
|
||||
error?: {
|
||||
code: string;
|
||||
retryable: boolean;
|
||||
message: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
/** JSON Schema 校验 —— ajv 在 adapter,usecase 只见本端口(ADR-003) */
|
||||
export type SchemaValidator = {
|
||||
validate(schema: Record<string, unknown>, value: unknown): {
|
||||
ok: boolean;
|
||||
errors: string[];
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user