@router-agent/core 0.0.0-stage → 0.3.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ /**
2
+ * 跨包共享的配置视图。
3
+ *
4
+ * 应用侧 `src/config.ts` 里的 AppConfig 属于装配层,适配器包不应该反向依赖它;
5
+ * 这里只放适配器真正需要的、稳定的字段形状,AppConfig 结构上天然兼容。
6
+ */
7
+ /** 群级准入配置:适配器只关心「哪些群、什么接收方式、名单是谁」。 */
8
+ export interface ChatAdmissionSettings {
9
+ chat: string;
10
+ /** all:全部消息,且不 @ 也会回复;mention:只收 @机器人。 */
11
+ mode: 'all' | 'mention';
12
+ /** 本群只允许这些 open_id 操作;空表示沿用全局名单。 */
13
+ allowed: string[];
14
+ /** 需求分析助手的终审管理员;邮箱或 open_id。 */
15
+ intake?: {
16
+ approvers?: string[];
17
+ };
18
+ }
19
+ /** worker 运行时选项:`runtime.options.<runtimeId>.<key>`。 */
20
+ export interface RuntimeSettingsView {
21
+ default: string;
22
+ options?: Record<string, unknown>;
23
+ /** 运行时插件入口:id → npm 包名 / 本地路径 / 目录。 */
24
+ plugins?: Record<string, unknown>;
25
+ }
26
+ /** 运行时装配用到的配置视图;AppConfig 结构上兼容。 */
27
+ export interface RuntimeConfigView {
28
+ runtime: RuntimeSettingsView;
29
+ }
30
+ /** 通道装配用到的配置视图;AppConfig 结构上兼容。 */
31
+ export interface ChannelConfigView {
32
+ /** 通道插件入口:id → npm 包名 / 本地路径 / 目录。 */
33
+ channels?: Record<string, unknown>;
34
+ feishu: {
35
+ appId: string;
36
+ allowed: string[];
37
+ chats: ChatAdmissionSettings[];
38
+ directMessages: boolean;
39
+ botOpenIds: string[];
40
+ botNames: string[];
41
+ };
42
+ }
package/dist/config.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 跨包共享的配置视图。
3
+ *
4
+ * 应用侧 `src/config.ts` 里的 AppConfig 属于装配层,适配器包不应该反向依赖它;
5
+ * 这里只放适配器真正需要的、稳定的字段形状,AppConfig 结构上天然兼容。
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../packages/core/src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,9 @@
1
+ /** 结构化日志事件:由内核/队列/API 发出,由 CLI 统一落盘。 */
2
+ export interface LogEvent {
3
+ event: string;
4
+ [key: string]: unknown;
5
+ }
6
+ export type EventSink = (event: LogEvent) => void;
7
+ /** 截断长文本,日志里保留可读的片段而不刷屏。 */
8
+ export declare function preview(value: unknown, max?: number): string;
9
+ export declare function noopSink(_event: LogEvent): void;
package/dist/events.js ADDED
@@ -0,0 +1,10 @@
1
+ /** 截断长文本,日志里保留可读的片段而不刷屏。 */
2
+ export function preview(value, max = 160) {
3
+ const text = typeof value === 'string' ? value : String(value ?? '');
4
+ const single = text.replace(/\s+/g, ' ').trim();
5
+ return single.length > max ? `${single.slice(0, max)}…` : single;
6
+ }
7
+ export function noopSink(_event) {
8
+ // 未注入日志实现时什么都不做
9
+ }
10
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../../../packages/core/src/events.ts"],"names":[],"mappings":"AAQA,4BAA4B;AAC5B,MAAM,UAAU,OAAO,CAAC,KAAc,EAAE,GAAG,GAAG,GAAG;IAC/C,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;IACpE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;IAC/C,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAA;AAClE,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAgB;IACvC,gBAAgB;AAClB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from './model.js';
2
+ export * from './policy.js';
3
+ export * from './stage.js';
4
+ export * from './events.js';
5
+ export * from './config.js';
6
+ export * from './pr.js';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from './model.js';
2
+ export * from './policy.js';
3
+ export * from './stage.js';
4
+ export * from './events.js';
5
+ export * from './config.js';
6
+ export * from './pr.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/core/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,SAAS,CAAA"}
@@ -0,0 +1,261 @@
1
+ /** 领域内核的实体与取值定义。所有模块共用这里的词汇。 */
2
+ /** Router 对一个 Trigger 的四种决定。 */
3
+ export type Decision = 'accept' | 'stop' | 'ignore' | 'unavailable';
4
+ /** 决定的依据强度:显式(@bot / 直接指派)或推断(LLM 研判)。 */
5
+ export type Strength = 'explicit' | 'inferred';
6
+ /** 工单阶段(五种):待办 / 进行中 / 审核中 / 已阻塞 / 已完成。 */
7
+ export type Stage = 'todo' | 'running' | 'review' | 'blocked' | 'done';
8
+ /** Worker 上报的六种状态。 */
9
+ export type ReportState = 'progress' | 'waiting' | 'ready' | 'blocked' | 'failed' | 'dropped';
10
+ /** Source 种类:内置飞书与 GitHub PR 跟踪;自定义通道可以写自己的来源名。 */
11
+ export type SourceKind = string;
12
+ /** 一条消息的寻址方式。 */
13
+ export type AddressMode = 'mention' | 'direct' | 'none';
14
+ export type TriggerDisposition = 'pending' | 'evaluating' | 'filtered' | 'evaluated' | 'fanned_out' | 'owned';
15
+ /**
16
+ * Router 落盘到本机的原始附件。
17
+ * 目前只有飞书图片;派工/追问时把本地路径透传给 Worker,让它直接看原图。
18
+ */
19
+ export interface Attachment {
20
+ /** Router 本机绝对路径。 */
21
+ path: string;
22
+ /** image/png、image/jpeg 等,供 Worker 判断文件类型。 */
23
+ mimeType: string;
24
+ /** 是否来自被引用的消息。 */
25
+ quoted?: boolean;
26
+ }
27
+ export interface Trigger {
28
+ id: string;
29
+ source: SourceKind;
30
+ /**
31
+ * 通道实例 ID(一期固定 feishu)。
32
+ * 历史数据没有该字段时按 DEFAULT_CHANNEL_ID 兜底,见 channels/port.ts。
33
+ */
34
+ channelId?: string;
35
+ /** Source 自己的事件 ID,用于幂等。 */
36
+ externalId: string;
37
+ threadId: string;
38
+ /**
39
+ * 这条消息携带的全部外部线程标识:话题 ID(omt_)、根消息 ID(om_)、消息自身 ID。
40
+ * 飞书在不同客户端入口给不全字段(话题面板直发只有 thread_id),
41
+ * Store 靠这组别名把同一话题的消息解析到同一个 Thread。见 Store.resolveThread。
42
+ */
43
+ threadKeys?: string[];
44
+ /** 飞书 chat_id 或 GitHub owner/repo#number。 */
45
+ chatId: string;
46
+ chatName: string;
47
+ actorId: string;
48
+ actorName: string;
49
+ isBot: boolean;
50
+ text: string;
51
+ addressed: AddressMode;
52
+ /**
53
+ * 这条消息没有 @ 机器人,但因「话题已绑定在办工单 + 发送者在允许名单」而被放行(tk-59)。
54
+ *
55
+ * 放行不等于会被回复:准入 Gate 仍然可以把它拦掉。但一旦真的处理了,
56
+ * Router 就要加确认表情并给一句回执,否则用户只看到消息被吞掉。
57
+ */
58
+ unaddressedAdmitted?: boolean;
59
+ /** 纯附件消息(图片 / 文件 / 语音等):正文只是占位文本,不能据此开工。 */
60
+ mediaOnly?: boolean;
61
+ receivedAt: string;
62
+ disposition: TriggerDisposition;
63
+ decisionId?: string;
64
+ /** Source 原始载荷,便于审计与回放。 */
65
+ raw?: unknown;
66
+ }
67
+ export interface Thread {
68
+ id: string;
69
+ source: SourceKind;
70
+ /** 通道实例 ID;历史线程没有该字段时按飞书兜底。 */
71
+ channelId?: string;
72
+ rootId: string;
73
+ chatId: string;
74
+ createdAt: string;
75
+ }
76
+ export interface Mandate {
77
+ id: string;
78
+ ticketId: string;
79
+ policyVersion: string;
80
+ strength: Strength;
81
+ /** 授权范围:一句话说明这份工作允许做什么。 */
82
+ scope: string;
83
+ /** 研判命中的策略规则名。 */
84
+ rules: string[];
85
+ actorId: string;
86
+ actorName: string;
87
+ source: SourceKind;
88
+ chatId: string;
89
+ threadId: string;
90
+ createdAt: string;
91
+ }
92
+ export interface Ticket {
93
+ id: string;
94
+ title: string;
95
+ threadId: string;
96
+ source: SourceKind;
97
+ /** 通道实例 ID(channels.<id>);历史工单没有该字段时按飞书兜底。 */
98
+ channelId?: string;
99
+ chatId: string;
100
+ mandateId: string;
101
+ stage: Stage;
102
+ /** 派工目标仓库(Orca repo 选择器或项目名);开单时必须明确。 */
103
+ repo: string;
104
+ /** 派工起始分支(worktree 的 base branch);开单时必须明确。 */
105
+ branch: string;
106
+ /** 开单消息里随附的原始附件(Router 已落盘),派工时透传给 Worker。 */
107
+ attachments?: Attachment[];
108
+ prNumber: number;
109
+ prUrl: string;
110
+ prState: 'none' | 'open' | 'merged' | 'closed';
111
+ createdAt: string;
112
+ updatedAt: string;
113
+ closedAt?: string;
114
+ }
115
+ /**
116
+ * 一次尚未凑齐派工信息的对话草稿。
117
+ * Router 判断该开工但缺 repo / branch 时先落这里,等用户补齐后再开单。
118
+ */
119
+ export interface DispatchDraft {
120
+ chatId: string;
121
+ threadId: string;
122
+ actorId: string;
123
+ /** 原始工作请求(多轮补齐时保持不变)。 */
124
+ task: string;
125
+ repo: string;
126
+ branch: string;
127
+ updatedAt: string;
128
+ }
129
+ export interface Assignment {
130
+ id: string;
131
+ ticketId: string;
132
+ runtimeId: string;
133
+ handle: string;
134
+ workdir: string;
135
+ status: 'pending' | 'running' | 'ended';
136
+ startedAt: string;
137
+ endedAt?: string;
138
+ }
139
+ export type InputKind = 'followup' | 'ci' | 'policy' | 'retry';
140
+ export interface Input {
141
+ id: string;
142
+ ticketId: string;
143
+ assignmentId: string;
144
+ seq: number;
145
+ kind: InputKind;
146
+ text: string;
147
+ /**
148
+ * 来源标识:同一条外部消息只入库一次。
149
+ * 空表示没有可去重的来源(例如内部补的 retry 输入)。
150
+ */
151
+ sourceKey?: string;
152
+ /** expired:worker 在 ack 前就没了;不再重投,也不代表用户请求被完成。 */
153
+ status: 'pending' | 'delivered' | 'acked' | 'expired';
154
+ createdAt: string;
155
+ deliveredAt?: string;
156
+ ackedAt?: string;
157
+ }
158
+ export interface Report {
159
+ id: string;
160
+ ticketId: string;
161
+ assignmentId: string;
162
+ turn: number;
163
+ state: ReportState;
164
+ summary: string;
165
+ humanAction?: string;
166
+ prNumber?: number;
167
+ prUrl?: string;
168
+ /** Worker 要发给发起人的完整回复(Markdown)。里面可以有文档链接、PR 链接,Router 原样发送。 */
169
+ reply?: string;
170
+ ackedInputs: string[];
171
+ at: string;
172
+ }
173
+ export interface Subscription {
174
+ threadId: string;
175
+ ticketId: string;
176
+ /** 只提供上下文、不授予权限的订阅。 */
177
+ contextOnly: boolean;
178
+ createdAt: string;
179
+ }
180
+ export interface DecisionRecord {
181
+ id: string;
182
+ triggerId: string;
183
+ threadId: string;
184
+ decision: Decision;
185
+ strength: Strength;
186
+ reason: string;
187
+ policyVersion: string;
188
+ /** observe 模式下「本该接单」的标记。 */
189
+ wouldAccept: boolean;
190
+ createdAt: string;
191
+ }
192
+ export interface Channel {
193
+ kind: 'feishu' | 'github';
194
+ chatId: string;
195
+ /** 飞书 thread / 根消息 ID,用于话题回复。 */
196
+ threadId?: string;
197
+ }
198
+ /**
199
+ * 需求识别(intake)的状态。
200
+ *
201
+ * Router 只负责调度与流程收尾:初筛、追问、结构化整理全部交给 intake 分析引擎,
202
+ * 结构化结果必须经管理员终审(approved / rejected)才可能进入原有派单流程。
203
+ */
204
+ export type IntakeStatus = 'clarifying' | 'ready' | 'approved' | 'rejected' | 'dismissed' | 'answered';
205
+ /** 初筛结论:闲聊 / 普通咨询 / 疑似故障或需求。 */
206
+ export type IntakeKind = 'chat' | 'question' | 'issue';
207
+ /** 一条 intake 会话里的消息;Router 回复也记进来,供下一轮分析看到上下文。 */
208
+ export interface IntakeTurn {
209
+ role: 'user' | 'router';
210
+ text: string;
211
+ at: string;
212
+ actorId?: string;
213
+ }
214
+ /** 分析引擎整理出的结构化需求单。 */
215
+ export interface IntakeReport {
216
+ title: string;
217
+ /** 问题描述:用户实际遇到了什么。 */
218
+ problem: string;
219
+ /** 背景:什么场景下发生、影响谁。 */
220
+ background: string;
221
+ /** 复现步骤;收集不到时留空字符串,由 Router 标注「未收集到」。 */
222
+ steps: string;
223
+ /** 设备 / 账号 / 环境等信息。 */
224
+ environment: string;
225
+ /** 影响范围与严重程度。 */
226
+ impact: string;
227
+ /** 用户期望的结果。 */
228
+ expected: string;
229
+ /** 分析引擎判断的类型:操作问题 / 已知问题 / 疑似 BUG / 新需求。 */
230
+ assessment: string;
231
+ }
232
+ /**
233
+ * 一次需求识别会话。
234
+ *
235
+ * 与 Ticket 没有直接绑定:只有管理员终审通过后才通过 Engine.decide() 生成正式工单,
236
+ * 原有权限与派单体系保持不变。
237
+ */
238
+ export interface Intake {
239
+ id: string;
240
+ threadId: string;
241
+ chatId: string;
242
+ /** 发起人:只有本人和管理员能推进这条 intake。 */
243
+ actorId: string;
244
+ actorName: string;
245
+ /** 群绑定项目名与仓库选择器;intake 仅读取这个项目的代码。 */
246
+ project: string;
247
+ repo: string;
248
+ branch: string;
249
+ status: IntakeStatus;
250
+ kind: IntakeKind;
251
+ /** 最初触发识别的原文。 */
252
+ rawText: string;
253
+ turns: IntakeTurn[];
254
+ report?: IntakeReport;
255
+ /** 已经追问过的轮数,用于防止无限对话。 */
256
+ questions: number;
257
+ /** 终审通过后生成的工单号。 */
258
+ ticketId?: string;
259
+ createdAt: string;
260
+ updatedAt: string;
261
+ }
package/dist/model.js ADDED
@@ -0,0 +1,3 @@
1
+ /** 领域内核的实体与取值定义。所有模块共用这里的词汇。 */
2
+ export {};
3
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../../packages/core/src/model.ts"],"names":[],"mappings":"AAAA,gCAAgC"}
@@ -0,0 +1,96 @@
1
+ import type { Decision, Mandate, Strength, Trigger } from './model.js';
2
+ export type AcceptMode = 'off' | 'observe' | 'auto';
3
+ /**
4
+ * worker 被服务重启打断后的恢复策略。
5
+ *
6
+ * auto:自动补一条输入让 worker 从中断处接着做。
7
+ * notify:只发飞书提醒,等用户回复才恢复。
8
+ * off:完全不处理。
9
+ */
10
+ export type InterruptedRecoveryMode = 'auto' | 'notify' | 'off';
11
+ /** 准入表:三列分别对应「名单内未寻址 / 名单外已寻址 / 名单外未寻址」。 */
12
+ export interface AdmissionTable {
13
+ listedNotAddressed: 'inferred' | 'ignore';
14
+ unlistedAddressed: 'explicit' | 'inferred' | 'ignore';
15
+ unlistedNotAddressed: 'inferred' | 'ignore';
16
+ }
17
+ export interface Policy {
18
+ version: string;
19
+ acceptMode: AcceptMode;
20
+ maxWorkers: number;
21
+ /** 允许发起工作的原始条目(邮箱或 open_id)。 */
22
+ allowed: string[];
23
+ /** 名单条目解析出的 open_id;缺省时回退到 allowed(旧库兼容)。 */
24
+ allowedOpenIds?: string[];
25
+ /** 群级白名单:非空时该群只允许这些人操作,覆盖全局 allowed。 */
26
+ allowedByChat?: Record<string, string[]>;
27
+ /** 群级白名单解析出的 open_id;缺省时回退到 allowedByChat。 */
28
+ allowedByChatOpenIds?: Record<string, string[]>;
29
+ /** open_id → 通讯录显示名;只用于展示(喂给模型时写成「名字(ou_xxx)」),不参与匹配。 */
30
+ names?: Record<string, string>;
31
+ /** 允许产生推断候选的机器人身份。 */
32
+ bots: string[];
33
+ admission: AdmissionTable;
34
+ /** 该范围是否接收全部消息;false 表示只接收 @bot 与私聊。 */
35
+ receiveAll: boolean;
36
+ }
37
+ export type GateResult = {
38
+ action: 'explicit';
39
+ strength: Extract<Strength, 'explicit'>;
40
+ } | {
41
+ action: 'infer';
42
+ strength: Extract<Strength, 'inferred'>;
43
+ } | {
44
+ action: 'reply-only';
45
+ strength: Extract<Strength, 'inferred'>;
46
+ reason: string;
47
+ } | {
48
+ action: 'ignore';
49
+ reason: string;
50
+ };
51
+ export declare const DEFAULT_ADMISSION: AdmissionTable;
52
+ /**
53
+ * 生效的全局名单:优先用解析出的 open_id,没有解析结果时回退到原始条目
54
+ * (旧 `state.db` 里的 policy 行没有新字段,回退即旧行为)。
55
+ */
56
+ export declare function effectiveAllowed(policy: Policy): string[];
57
+ /**
58
+ * 生效的群级名单。
59
+ *
60
+ * 返回值有三种形态,调用方必须分清:
61
+ * - `undefined`:该群没配名单 → 沿用全局名单;
62
+ * - 非空数组:该群生效的 open_id 列表;
63
+ * - 空数组:配了名单但一个都没解析出来 → **该群拒绝所有人**,
64
+ * 绝不能退回全局名单(那比用户原意更宽松,是安全倒退)。
65
+ */
66
+ export declare function effectiveAllowedByChat(policy: Policy, chatId: string): string[] | undefined;
67
+ /**
68
+ * 三层路由的第一层:确定性 Gate。
69
+ * 不调用任何模型,只看名单、寻址方式、机器人名单与 accept mode。
70
+ */
71
+ export declare function gate(trigger: Trigger, policy: Policy): GateResult;
72
+ /**
73
+ * 是否给收到的消息加「收到」表情。
74
+ *
75
+ * 两个条件同时成立才加:
76
+ * 1. 确实在跟机器人说话:@ / 私聊,所在群配置为全量接收,
77
+ * 或未 @ 但因话题已绑定在办工单而被放行(tk-59);
78
+ * 2. 准入 Gate 不会静默忽略:被群白名单拦截、`admission: ignore` 的消息不加。
79
+ *
80
+ * 否则用户会先看到「Typing」,然后永远等不到回复,表情也不会被撤销。
81
+ */
82
+ export declare function shouldAcknowledge(trigger: Trigger, policy: Policy, replyAlwaysChats: ReadonlySet<string>): boolean;
83
+ export interface Acceptance {
84
+ decision: Decision;
85
+ wouldAccept: boolean;
86
+ }
87
+ /** observe 模式下,推断候选的 accept 被降级为「记录但不开工」。 */
88
+ export declare function negotiateAcceptance(decision: Decision, strength: Strength, mode: AcceptMode): Acceptance;
89
+ export type Revalidation = {
90
+ valid: true;
91
+ } | {
92
+ valid: false;
93
+ reason: string;
94
+ };
95
+ /** 工单恢复前对当前策略做重新校验;不主动撤销在途 Mandate。 */
96
+ export declare function revalidateMandate(mandate: Mandate, policy: Policy): Revalidation;
package/dist/policy.js ADDED
@@ -0,0 +1,117 @@
1
+ export const DEFAULT_ADMISSION = {
2
+ listedNotAddressed: 'inferred',
3
+ unlistedAddressed: 'inferred',
4
+ unlistedNotAddressed: 'ignore',
5
+ };
6
+ /**
7
+ * 生效的全局名单:优先用解析出的 open_id,没有解析结果时回退到原始条目
8
+ * (旧 `state.db` 里的 policy 行没有新字段,回退即旧行为)。
9
+ */
10
+ export function effectiveAllowed(policy) {
11
+ return policy.allowedOpenIds ?? policy.allowed;
12
+ }
13
+ /**
14
+ * 生效的群级名单。
15
+ *
16
+ * 返回值有三种形态,调用方必须分清:
17
+ * - `undefined`:该群没配名单 → 沿用全局名单;
18
+ * - 非空数组:该群生效的 open_id 列表;
19
+ * - 空数组:配了名单但一个都没解析出来 → **该群拒绝所有人**,
20
+ * 绝不能退回全局名单(那比用户原意更宽松,是安全倒退)。
21
+ */
22
+ export function effectiveAllowedByChat(policy, chatId) {
23
+ const scoped = policy.allowedByChat?.[chatId];
24
+ if (!scoped || scoped.length === 0)
25
+ return undefined;
26
+ return policy.allowedByChatOpenIds?.[chatId] ?? scoped;
27
+ }
28
+ /**
29
+ * 三层路由的第一层:确定性 Gate。
30
+ * 不调用任何模型,只看名单、寻址方式、机器人名单与 accept mode。
31
+ */
32
+ export function gate(trigger, policy) {
33
+ const addressed = trigger.addressed !== 'none';
34
+ // 群级白名单:配置了就只认这一份,名单外的人在该群完全无效(不接单、不回复)。
35
+ const scoped = effectiveAllowedByChat(policy, trigger.chatId);
36
+ // 配了群名单但一个都没解析出来:这个群谁都不许进,也不能退回全局名单。
37
+ if (scoped && scoped.length === 0) {
38
+ return { action: 'ignore', reason: 'admission:chat-allowlist-empty' };
39
+ }
40
+ const listed = (scoped ?? effectiveAllowed(policy)).includes(trigger.actorId);
41
+ if (trigger.isBot) {
42
+ // 机器人永远不能构成显式请求:要么被列入 bots 成为推断候选,要么直接忽略。
43
+ if (!policy.bots.includes(trigger.actorId))
44
+ return { action: 'ignore', reason: 'bot:not-listed' };
45
+ if (!addressed && !policy.receiveAll)
46
+ return { action: 'ignore', reason: 'bot:not-receive-all' };
47
+ return applyAcceptMode({ action: 'infer', strength: 'inferred' }, policy);
48
+ }
49
+ if (scoped && !listed) {
50
+ return { action: 'ignore', reason: 'admission:not-in-chat-allowlist' };
51
+ }
52
+ // 设计文档 2.3:私聊 bot 进入研判,不构成显式授权;只有 @机器人 才是显式。
53
+ if (trigger.addressed === 'direct') {
54
+ return applyAcceptMode({ action: 'infer', strength: 'inferred' }, policy);
55
+ }
56
+ let cell;
57
+ if (addressed) {
58
+ cell = listed ? 'explicit' : policy.admission.unlistedAddressed;
59
+ }
60
+ else {
61
+ cell = listed ? policy.admission.listedNotAddressed : policy.admission.unlistedNotAddressed;
62
+ }
63
+ if (cell === 'ignore') {
64
+ return { action: 'ignore', reason: addressed ? 'admission:unlisted-addressed' : listed ? 'admission:listed-not-addressed' : 'admission:unlisted-not-addressed' };
65
+ }
66
+ if (cell === 'explicit')
67
+ return { action: 'explicit', strength: 'explicit' };
68
+ return applyAcceptMode({ action: 'infer', strength: 'inferred' }, policy);
69
+ }
70
+ /**
71
+ * 是否给收到的消息加「收到」表情。
72
+ *
73
+ * 两个条件同时成立才加:
74
+ * 1. 确实在跟机器人说话:@ / 私聊,所在群配置为全量接收,
75
+ * 或未 @ 但因话题已绑定在办工单而被放行(tk-59);
76
+ * 2. 准入 Gate 不会静默忽略:被群白名单拦截、`admission: ignore` 的消息不加。
77
+ *
78
+ * 否则用户会先看到「Typing」,然后永远等不到回复,表情也不会被撤销。
79
+ */
80
+ export function shouldAcknowledge(trigger, policy, replyAlwaysChats) {
81
+ const verdict = gate(trigger, policy);
82
+ // 未 @ 但被放行的消息(tk-59)Router 一定会处理(投给工单的 worker),表情必须跟着走,
83
+ // 否则用户只看到消息被吞掉:既没有确认表情,也等不到回执。
84
+ // 只放行「名单内未寻址」这一条 ignore——机器人、名单外成员、群名单为空等仍然拦。
85
+ if (trigger.unaddressedAdmitted && verdict.action === 'ignore' && verdict.reason === 'admission:listed-not-addressed')
86
+ return true;
87
+ if (trigger.addressed === 'none' && !trigger.unaddressedAdmitted && !replyAlwaysChats.has(trigger.chatId))
88
+ return false;
89
+ return verdict.action !== 'ignore';
90
+ }
91
+ function applyAcceptMode(result, policy) {
92
+ if (policy.acceptMode === 'off') {
93
+ return { action: 'reply-only', strength: 'inferred', reason: 'accept-mode:off' };
94
+ }
95
+ return result;
96
+ }
97
+ /** observe 模式下,推断候选的 accept 被降级为「记录但不开工」。 */
98
+ export function negotiateAcceptance(decision, strength, mode) {
99
+ if (decision === 'accept' && strength === 'inferred' && mode === 'observe') {
100
+ return { decision: 'ignore', wouldAccept: true };
101
+ }
102
+ return { decision, wouldAccept: false };
103
+ }
104
+ /** 工单恢复前对当前策略做重新校验;不主动撤销在途 Mandate。 */
105
+ export function revalidateMandate(mandate, policy) {
106
+ if (mandate.strength === 'explicit')
107
+ return { valid: true };
108
+ if (policy.acceptMode !== 'auto')
109
+ return { valid: false, reason: 'accept-mode:not-auto' };
110
+ // 名单改成邮箱后,这里必须按解析结果判定,否则在途工单会被判成 sender:not-allowed 直接失效。
111
+ const scoped = effectiveAllowedByChat(policy, mandate.chatId);
112
+ const allowed = scoped ?? effectiveAllowed(policy);
113
+ if (!allowed.includes(mandate.actorId))
114
+ return { valid: false, reason: 'sender:not-allowed' };
115
+ return { valid: true };
116
+ }
117
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../../packages/core/src/policy.ts"],"names":[],"mappings":"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"}
package/dist/pr.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /** PR / MR 的中立视图;Router 跟踪与 UI 证据门禁共用。 */
2
+ export interface PrInfo {
3
+ number: number;
4
+ url: string;
5
+ state: 'open' | 'merged' | 'closed';
6
+ title: string;
7
+ branch: string;
8
+ /** PR 描述正文;UI 证据门禁用它检查 Before / After。列表查询不返回时为 undefined。 */
9
+ body?: string;
10
+ }
package/dist/pr.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pr.js.map
package/dist/pr.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr.js","sourceRoot":"","sources":["../../../../packages/core/src/pr.ts"],"names":[],"mappings":""}
@@ -0,0 +1,36 @@
1
+ import type { Assignment, Report, Stage, Ticket } from './model.js';
2
+ export interface StageInput {
3
+ ticket: Ticket;
4
+ assignment?: Assignment | null;
5
+ latestReport?: Report | null;
6
+ /** 是否存在等待人工回答的问题。 */
7
+ hasAttention?: boolean;
8
+ }
9
+ /**
10
+ * 五种工单阶段的展示顺序与中文名。
11
+ * 顺序即看板列顺序,也用于 CLI / 提示文案。
12
+ */
13
+ export declare const STAGE_VALUES: readonly Stage[];
14
+ export declare const STAGE_LABELS: Record<Stage, string>;
15
+ /**
16
+ * 旧八阶段 → 新五阶段的映射。
17
+ * 同时用于两处:历史库读时兼容(不写库),以及 scripts/migrate-ticket-stages.mjs 的迁移。
18
+ * 注意:旧 `draft` / `accepted` 都并回「待办」,旧 `idle` 因为需要人工推动也并入「已阻塞」。
19
+ */
20
+ export declare const LEGACY_STAGE_MAP: Readonly<Record<string, Stage>>;
21
+ export declare function isStage(value: unknown): value is Stage;
22
+ /** 把库里的阶段值归一化到新五阶段;未知值返回 null,交给调用方决定兜底。 */
23
+ export declare function normalizeStage(value: unknown): Stage | null;
24
+ /** 按新阶段查询时,需要一并匹配的旧阶段值(迁移执行前也能查全)。 */
25
+ export declare function stageAliases(stage: Stage): string[];
26
+ /**
27
+ * 由「工单 + 派工 + 最新报告」推导阶段。
28
+ * 自上而下判断,第一条命中的规则生效。
29
+ */
30
+ export declare function computeStage(input: StageInput): Stage;
31
+ /**
32
+ * 阶段迁移是否合法。
33
+ * 收敛到五种后不再表达细分流程:除「已完成」外,四个活跃阶段之间都可以互转;
34
+ * 「已完成」是终态,符合 PR 合并/关单后不可切回的既有约束。
35
+ */
36
+ export declare function canTransition(from: Stage, to: Stage): boolean;
package/dist/stage.js ADDED
@@ -0,0 +1,89 @@
1
+ /**
2
+ * 五种工单阶段的展示顺序与中文名。
3
+ * 顺序即看板列顺序,也用于 CLI / 提示文案。
4
+ */
5
+ export const STAGE_VALUES = ['todo', 'running', 'review', 'blocked', 'done'];
6
+ export const STAGE_LABELS = {
7
+ todo: '待办',
8
+ running: '进行中',
9
+ review: '审核中',
10
+ blocked: '已阻塞',
11
+ done: '已完成',
12
+ };
13
+ /**
14
+ * 旧八阶段 → 新五阶段的映射。
15
+ * 同时用于两处:历史库读时兼容(不写库),以及 scripts/migrate-ticket-stages.mjs 的迁移。
16
+ * 注意:旧 `draft` / `accepted` 都并回「待办」,旧 `idle` 因为需要人工推动也并入「已阻塞」。
17
+ */
18
+ export const LEGACY_STAGE_MAP = {
19
+ draft: 'todo',
20
+ accepted: 'todo',
21
+ assigned: 'running',
22
+ running: 'running',
23
+ review: 'review',
24
+ person: 'blocked',
25
+ idle: 'blocked',
26
+ closed: 'done',
27
+ };
28
+ export function isStage(value) {
29
+ return typeof value === 'string' && STAGE_VALUES.includes(value);
30
+ }
31
+ /** 把库里的阶段值归一化到新五阶段;未知值返回 null,交给调用方决定兜底。 */
32
+ export function normalizeStage(value) {
33
+ if (isStage(value))
34
+ return value;
35
+ if (typeof value === 'string')
36
+ return LEGACY_STAGE_MAP[value] ?? null;
37
+ return null;
38
+ }
39
+ /** 按新阶段查询时,需要一并匹配的旧阶段值(迁移执行前也能查全)。 */
40
+ export function stageAliases(stage) {
41
+ const aliases = Object.entries(LEGACY_STAGE_MAP)
42
+ .filter(([, mapped]) => mapped === stage)
43
+ .map(([legacy]) => legacy);
44
+ return [...new Set([stage, ...aliases])];
45
+ }
46
+ /**
47
+ * 由「工单 + 派工 + 最新报告」推导阶段。
48
+ * 自上而下判断,第一条命中的规则生效。
49
+ */
50
+ export function computeStage(input) {
51
+ const { ticket, assignment, latestReport, hasAttention } = input;
52
+ if (ticket.closedAt)
53
+ return 'done';
54
+ if (hasAttention)
55
+ return 'blocked';
56
+ if (latestReport?.state === 'ready')
57
+ return 'review';
58
+ if (latestReport?.state === 'blocked' || latestReport?.state === 'failed')
59
+ return 'blocked';
60
+ if (!assignment)
61
+ return 'todo';
62
+ // 已派工(pending 或 running)都算「进行中」:对发起人来说工作已经开始了。
63
+ if (assignment.status === 'pending')
64
+ return 'running';
65
+ // 派工已结束但工单没闭环、worker 明确 waiting/dropped:都需要人来推动。
66
+ if (assignment.status === 'ended')
67
+ return 'blocked';
68
+ if (latestReport?.state === 'waiting' || latestReport?.state === 'dropped')
69
+ return 'blocked';
70
+ return 'running';
71
+ }
72
+ const TRANSITIONS = {
73
+ todo: ['running', 'review', 'blocked', 'done'],
74
+ running: ['todo', 'review', 'blocked', 'done'],
75
+ review: ['todo', 'running', 'blocked', 'done'],
76
+ blocked: ['todo', 'running', 'review', 'done'],
77
+ done: [],
78
+ };
79
+ /**
80
+ * 阶段迁移是否合法。
81
+ * 收敛到五种后不再表达细分流程:除「已完成」外,四个活跃阶段之间都可以互转;
82
+ * 「已完成」是终态,符合 PR 合并/关单后不可切回的既有约束。
83
+ */
84
+ export function canTransition(from, to) {
85
+ if (from === to)
86
+ return true;
87
+ return TRANSITIONS[from].includes(to);
88
+ }
89
+ //# sourceMappingURL=stage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage.js","sourceRoot":"","sources":["../../../../packages/core/src/stage.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,33 @@
1
1
  {
2
2
  "name": "@router-agent/core",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.31",
4
+ "type": "module",
5
+ "description": "领域内核:工单、授权、阶段、策略与共享类型",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/blankPen/router-agent.git",
10
+ "directory": "packages/core"
11
+ },
12
+ "homepage": "https://github.com/blankPen/router-agent#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/blankPen/router-agent/issues"
15
+ },
16
+ "main": "./dist/index.js",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ },
22
+ "./*.js": "./dist/*.js",
23
+ "./*": "./dist/*"
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "types": "./dist/index.d.ts",
29
+ "publishConfig": {
30
+ "access": "public",
31
+ "registry": "https://registry.npmjs.org/"
32
+ }
33
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.