@geoly-ai/social-hub-core 0.0.4 → 0.0.6
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.
- package/dist/activity.d.ts +138 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +577 -0
- package/dist/activity.js.map +1 -0
- package/dist/activity.test.d.ts +2 -0
- package/dist/activity.test.d.ts.map +1 -0
- package/dist/activity.test.js +430 -0
- package/dist/activity.test.js.map +1 -0
- package/dist/client-delivery-config.d.ts +26 -0
- package/dist/client-delivery-config.d.ts.map +1 -0
- package/dist/client-delivery-config.js +40 -0
- package/dist/client-delivery-config.js.map +1 -0
- package/dist/client-delivery-config.test.d.ts +2 -0
- package/dist/client-delivery-config.test.d.ts.map +1 -0
- package/dist/client-delivery-config.test.js +36 -0
- package/dist/client-delivery-config.test.js.map +1 -0
- package/dist/content-review-scoring.d.ts +67 -0
- package/dist/content-review-scoring.d.ts.map +1 -0
- package/dist/content-review-scoring.js +128 -0
- package/dist/content-review-scoring.js.map +1 -0
- package/dist/content-review-scoring.test.d.ts +2 -0
- package/dist/content-review-scoring.test.d.ts.map +1 -0
- package/dist/content-review-scoring.test.js +109 -0
- package/dist/content-review-scoring.test.js.map +1 -0
- package/dist/errors.d.ts +3 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/notification-sources/field-paths.d.ts +93 -0
- package/dist/notification-sources/field-paths.d.ts.map +1 -0
- package/dist/notification-sources/field-paths.js +86 -0
- package/dist/notification-sources/field-paths.js.map +1 -0
- package/dist/notification-sources/field-set.d.ts +46 -0
- package/dist/notification-sources/field-set.d.ts.map +1 -0
- package/dist/notification-sources/field-set.js +178 -0
- package/dist/notification-sources/field-set.js.map +1 -0
- package/dist/notification-sources/index.d.ts +13 -0
- package/dist/notification-sources/index.d.ts.map +1 -0
- package/dist/notification-sources/index.js +13 -0
- package/dist/notification-sources/index.js.map +1 -0
- package/dist/notification-sources/notification-sources.test.d.ts +2 -0
- package/dist/notification-sources/notification-sources.test.d.ts.map +1 -0
- package/dist/notification-sources/notification-sources.test.js +211 -0
- package/dist/notification-sources/notification-sources.test.js.map +1 -0
- package/dist/notification-sources/post-performance-node-runtime.d.ts +155 -0
- package/dist/notification-sources/post-performance-node-runtime.d.ts.map +1 -0
- package/dist/notification-sources/post-performance-node-runtime.js +158 -0
- package/dist/notification-sources/post-performance-node-runtime.js.map +1 -0
- package/dist/notification-sources/post-performance-node-runtime.test.d.ts +2 -0
- package/dist/notification-sources/post-performance-node-runtime.test.d.ts.map +1 -0
- package/dist/notification-sources/post-performance-node-runtime.test.js +182 -0
- package/dist/notification-sources/post-performance-node-runtime.test.js.map +1 -0
- package/dist/notification-sources/post-performance-node.d.ts +34 -0
- package/dist/notification-sources/post-performance-node.d.ts.map +1 -0
- package/dist/notification-sources/post-performance-node.js +437 -0
- package/dist/notification-sources/post-performance-node.js.map +1 -0
- package/dist/notification-sources/registry.d.ts +27 -0
- package/dist/notification-sources/registry.d.ts.map +1 -0
- package/dist/notification-sources/registry.js +222 -0
- package/dist/notification-sources/registry.js.map +1 -0
- package/dist/notification-sources/source-definition.d.ts +97 -0
- package/dist/notification-sources/source-definition.d.ts.map +1 -0
- package/dist/notification-sources/source-definition.js +30 -0
- package/dist/notification-sources/source-definition.js.map +1 -0
- package/dist/openclaw-config.d.ts +9 -0
- package/dist/openclaw-config.d.ts.map +1 -1
- package/dist/openclaw-config.js +17 -0
- package/dist/openclaw-config.js.map +1 -1
- package/dist/openclaw-config.test.d.ts +2 -0
- package/dist/openclaw-config.test.d.ts.map +1 -0
- package/dist/openclaw-config.test.js +49 -0
- package/dist/openclaw-config.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { activityDateOf, computeEffectiveTimezone, computeRecentActivitySummary, validateCanonicalEventPayload, validateInteractionActionOutcomePayload, INTERACTION_ACTION_OUTCOME_TYPE, } from "./activity.js";
|
|
3
|
+
/**
|
|
4
|
+
* §11.4 验收 fixture:跨午夜、02:00 边界、未结束当日、重试/重复、缺 runId、
|
|
5
|
+
* planned/skipped 不计、DST 春秋切换、tz 缺失、tz 变更、28 天全空、连续沉默。
|
|
6
|
+
*/
|
|
7
|
+
const TZ = "America/New_York";
|
|
8
|
+
const NOW = new Date("2026-07-14T20:00:00-04:00"); // 当地 20:00,activityDate=2026-07-14
|
|
9
|
+
let seq = 0;
|
|
10
|
+
function ev(occurredAt, over = {}) {
|
|
11
|
+
seq += 1;
|
|
12
|
+
const payload = {
|
|
13
|
+
occurredAt,
|
|
14
|
+
actionKind: over.actionKind ?? "comment",
|
|
15
|
+
eventSchemaVersion: 1,
|
|
16
|
+
jobId: over.jobId ?? "job-1",
|
|
17
|
+
};
|
|
18
|
+
if (over.eventTimezone !== null)
|
|
19
|
+
payload["eventTimezone"] = over.eventTimezone ?? TZ;
|
|
20
|
+
if (over.runId)
|
|
21
|
+
payload["runId"] = over.runId;
|
|
22
|
+
if (over.sessionId)
|
|
23
|
+
payload["sessionId"] = over.sessionId;
|
|
24
|
+
return {
|
|
25
|
+
id: `ev-${seq}`,
|
|
26
|
+
externalRef: over.externalRef === undefined ? `ref-${seq}` : over.externalRef,
|
|
27
|
+
result: over.result ?? "succeeded",
|
|
28
|
+
payload,
|
|
29
|
+
createdAt: new Date(occurredAt),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function summarize(events, over = {}) {
|
|
33
|
+
return computeRecentActivitySummary(events, {
|
|
34
|
+
now: NOW,
|
|
35
|
+
effectiveTimezone: TZ,
|
|
36
|
+
timezoneKnown: true,
|
|
37
|
+
...over,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function day(s, date) {
|
|
41
|
+
return s.localDays.find((d) => d.date === date);
|
|
42
|
+
}
|
|
43
|
+
describe("activityDateOf (§4.3③ 固定 cutoff)", () => {
|
|
44
|
+
it("本地 00:00–02:00 归前一日,02:00 及以后归当日", () => {
|
|
45
|
+
// 01:30 EDT → 前一日
|
|
46
|
+
expect(activityDateOf(new Date("2026-07-14T01:30:00-04:00"), TZ)).toBe("2026-07-13");
|
|
47
|
+
// 02:00 边界 → 当日
|
|
48
|
+
expect(activityDateOf(new Date("2026-07-14T02:00:00-04:00"), TZ)).toBe("2026-07-14");
|
|
49
|
+
// 23:00 → 当日
|
|
50
|
+
expect(activityDateOf(new Date("2026-07-14T23:00:00-04:00"), TZ)).toBe("2026-07-14");
|
|
51
|
+
});
|
|
52
|
+
it("DST 春季切换日(2026-03-08,02:00→03:00 缺失):03:00 归当日,01:59 归前一日", () => {
|
|
53
|
+
// 07:00Z = EST 02:00 不存在,该 UTC 时刻本地是 03:00 EDT → 当日
|
|
54
|
+
expect(activityDateOf(new Date("2026-03-08T07:00:00Z"), TZ)).toBe("2026-03-08");
|
|
55
|
+
// 06:59Z = 01:59 EST → 前一日
|
|
56
|
+
expect(activityDateOf(new Date("2026-03-08T06:59:00Z"), TZ)).toBe("2026-03-07");
|
|
57
|
+
});
|
|
58
|
+
it("DST 秋季切换日(2026-11-01,01:00 重复):两个真实时刻均归前一日(<02:00)", () => {
|
|
59
|
+
// 05:30Z = 01:30 EDT(第一次);06:30Z = 01:30 EST(第二次)——都在 cutoff 前
|
|
60
|
+
expect(activityDateOf(new Date("2026-11-01T05:30:00Z"), TZ)).toBe("2026-10-31");
|
|
61
|
+
expect(activityDateOf(new Date("2026-11-01T06:30:00Z"), TZ)).toBe("2026-10-31");
|
|
62
|
+
// 07:00Z = 02:00 EST → 当日
|
|
63
|
+
expect(activityDateOf(new Date("2026-11-01T07:00:00Z"), TZ)).toBe("2026-11-01");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe("computeRecentActivitySummary (§4.2/§4.3④)", () => {
|
|
67
|
+
it("localDays 恰 windowDays 条,以当前 activityDate 结尾;28 天全空", () => {
|
|
68
|
+
const s = summarize([]);
|
|
69
|
+
expect(s.localDays).toHaveLength(28);
|
|
70
|
+
expect(s.localDays.at(-1).date).toBe("2026-07-14");
|
|
71
|
+
expect(s.localDays.every((d) => d.sessions === 0)).toBe(true);
|
|
72
|
+
expect(s.activeDaysLast28).toBe(0);
|
|
73
|
+
expect(s.consecutiveSilentDays).toBe(27); // 27 个已完结日全静默
|
|
74
|
+
expect(s.lastSessionAt).toBeNull();
|
|
75
|
+
});
|
|
76
|
+
it("同 sessionId 多动作归一个 session,action 各自照数;comments/replies 互斥统计", () => {
|
|
77
|
+
const s = summarize([
|
|
78
|
+
ev("2026-07-13T10:00:00-04:00", {
|
|
79
|
+
sessionId: "s1",
|
|
80
|
+
actionKind: "browse",
|
|
81
|
+
}),
|
|
82
|
+
ev("2026-07-13T10:05:00-04:00", {
|
|
83
|
+
sessionId: "s1",
|
|
84
|
+
actionKind: "comment",
|
|
85
|
+
}),
|
|
86
|
+
ev("2026-07-13T10:07:00-04:00", { sessionId: "s1", actionKind: "reply" }),
|
|
87
|
+
ev("2026-07-13T10:09:00-04:00", { sessionId: "s1", actionKind: "like" }),
|
|
88
|
+
]);
|
|
89
|
+
const d = day(s, "2026-07-13");
|
|
90
|
+
expect(d).toMatchObject({ sessions: 1, comments: 1, replies: 1, likes: 1 });
|
|
91
|
+
});
|
|
92
|
+
it("缺 sessionId 用 runId;同 runId 跨 job 合并一个 session", () => {
|
|
93
|
+
const s = summarize([
|
|
94
|
+
ev("2026-07-13T10:00:00-04:00", { runId: "r1", jobId: "job-a" }),
|
|
95
|
+
ev("2026-07-13T11:30:00-04:00", { runId: "r1", jobId: "job-b" }),
|
|
96
|
+
]);
|
|
97
|
+
expect(day(s, "2026-07-13").sessions).toBe(1);
|
|
98
|
+
});
|
|
99
|
+
it("缺 runId:同 jobId 相邻 ≤20min 归同 session,>20min 另起", () => {
|
|
100
|
+
const s = summarize([
|
|
101
|
+
ev("2026-07-13T10:00:00-04:00", {}),
|
|
102
|
+
ev("2026-07-13T10:15:00-04:00", {}), // ≤20min → 同 session
|
|
103
|
+
ev("2026-07-13T10:40:00-04:00", {}), // >20min → 新 session
|
|
104
|
+
]);
|
|
105
|
+
expect(day(s, "2026-07-13").sessions).toBe(2);
|
|
106
|
+
expect(day(s, "2026-07-13").comments).toBe(3);
|
|
107
|
+
});
|
|
108
|
+
it("重试/重复:无 externalRef 的相同三元组整条去重(action 也只计 1 次)", () => {
|
|
109
|
+
const dup = {
|
|
110
|
+
sessionId: "s1",
|
|
111
|
+
externalRef: null,
|
|
112
|
+
actionKind: "comment",
|
|
113
|
+
};
|
|
114
|
+
const s = summarize([
|
|
115
|
+
ev("2026-07-13T10:00:00-04:00", dup),
|
|
116
|
+
ev("2026-07-13T10:00:00-04:00", dup), // 完全相同 → 去重
|
|
117
|
+
ev("2026-07-13T10:05:00-04:00", dup), // occurredAt 不同 → 真实第二条
|
|
118
|
+
]);
|
|
119
|
+
const d = day(s, "2026-07-13");
|
|
120
|
+
expect(d.comments).toBe(2);
|
|
121
|
+
expect(d.sessions).toBe(1);
|
|
122
|
+
});
|
|
123
|
+
it("planned/skipped/session_start 与 failed/skipped result 均不计", () => {
|
|
124
|
+
const s = summarize([
|
|
125
|
+
ev("2026-07-13T10:00:00-04:00", { actionKind: "planned" }),
|
|
126
|
+
ev("2026-07-13T10:01:00-04:00", { actionKind: "skipped" }),
|
|
127
|
+
ev("2026-07-13T10:02:00-04:00", { actionKind: "session_start" }),
|
|
128
|
+
ev("2026-07-13T10:03:00-04:00", { result: "failed" }),
|
|
129
|
+
ev("2026-07-13T10:04:00-04:00", { result: "skipped" }),
|
|
130
|
+
]);
|
|
131
|
+
expect(day(s, "2026-07-13")).toMatchObject({
|
|
132
|
+
sessions: 0,
|
|
133
|
+
comments: 0,
|
|
134
|
+
likes: 0,
|
|
135
|
+
replies: 0,
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
it("需求A 安全闸:interaction_action_outcome 事件不计入活动(即使 actionKind=comment)", () => {
|
|
139
|
+
const real = ev("2026-07-13T10:00:00-04:00", {
|
|
140
|
+
sessionId: "s1",
|
|
141
|
+
actionKind: "comment",
|
|
142
|
+
});
|
|
143
|
+
const outcome = ev("2026-07-13T10:05:00-04:00", {
|
|
144
|
+
sessionId: "s1",
|
|
145
|
+
actionKind: "comment",
|
|
146
|
+
});
|
|
147
|
+
outcome.type = INTERACTION_ACTION_OUTCOME_TYPE; // 复用真实 actionKind,但按 type 排除
|
|
148
|
+
const s = summarize([real, outcome]);
|
|
149
|
+
const d = day(s, "2026-07-13");
|
|
150
|
+
expect(d.comments).toBe(1); // 只算真实 comment,outcome 不计
|
|
151
|
+
expect(d.sessions).toBe(1);
|
|
152
|
+
});
|
|
153
|
+
it("跨午夜:本地 01:30 的 session 归前一日", () => {
|
|
154
|
+
const s = summarize([
|
|
155
|
+
ev("2026-07-14T01:30:00-04:00", { sessionId: "s-night" }),
|
|
156
|
+
]);
|
|
157
|
+
expect(day(s, "2026-07-13").sessions).toBe(1);
|
|
158
|
+
expect(day(s, "2026-07-14").sessions).toBe(0);
|
|
159
|
+
});
|
|
160
|
+
it("consecutiveSilentDays 不含未结束当日;当日有活动不清零昨日静默", () => {
|
|
161
|
+
const s = summarize([
|
|
162
|
+
ev("2026-07-11T10:00:00-04:00", { sessionId: "a" }), // 3 天前活跃
|
|
163
|
+
ev("2026-07-14T10:00:00-04:00", { sessionId: "b" }), // 当日(未结束)
|
|
164
|
+
]);
|
|
165
|
+
// 已完结的 07-12、07-13 静默 → 2
|
|
166
|
+
expect(s.consecutiveSilentDays).toBe(2);
|
|
167
|
+
});
|
|
168
|
+
it("timezone 变更:事件按自身 eventTimezone 归日,summary 顶层是当前时区", () => {
|
|
169
|
+
// 事件发生时账号在东京(UTC+9):当地 7/13 01:00 → 归 7/12
|
|
170
|
+
const s = summarize([
|
|
171
|
+
ev("2026-07-13T01:00:00+09:00", {
|
|
172
|
+
eventTimezone: "Asia/Tokyo",
|
|
173
|
+
sessionId: "s1",
|
|
174
|
+
}),
|
|
175
|
+
]);
|
|
176
|
+
expect(day(s, "2026-07-12").sessions).toBe(1);
|
|
177
|
+
expect(s.historicalTzApprox).toBe(false);
|
|
178
|
+
});
|
|
179
|
+
it("历史事件缺 eventTimezone:按当前时区近似归日并标 historicalTzApprox", () => {
|
|
180
|
+
const s = summarize([
|
|
181
|
+
ev("2026-07-13T10:00:00-04:00", { eventTimezone: null, sessionId: "s1" }),
|
|
182
|
+
]);
|
|
183
|
+
expect(day(s, "2026-07-13").sessions).toBe(1);
|
|
184
|
+
expect(s.historicalTzApprox).toBe(true);
|
|
185
|
+
});
|
|
186
|
+
it("tz 完全未知(裁决④):不做逐日归属,保留绝对时间字段;带 eventTimezone 的事件仍可计 lastSessionAt", () => {
|
|
187
|
+
const s = summarize([
|
|
188
|
+
ev("2026-07-13T10:00:00-04:00", {
|
|
189
|
+
eventTimezone: null,
|
|
190
|
+
sessionId: "a",
|
|
191
|
+
}),
|
|
192
|
+
ev("2026-07-13T12:00:00-04:00", {
|
|
193
|
+
eventTimezone: "Asia/Tokyo",
|
|
194
|
+
sessionId: "b",
|
|
195
|
+
}),
|
|
196
|
+
], { effectiveTimezone: "unknown", timezoneKnown: false });
|
|
197
|
+
expect(s.timezoneKnown).toBe(false);
|
|
198
|
+
expect(s.localDays).toEqual([]);
|
|
199
|
+
expect(s.consecutiveSilentDays).toBeNull();
|
|
200
|
+
expect(s.activeDaysLast7).toBeNull();
|
|
201
|
+
expect(s.unattributedEvents).toBe(1); // 缺 eventTimezone 那条
|
|
202
|
+
expect(s.lastSessionAt).toBe("2026-07-13T16:00:00.000Z"); // Tokyo 那条
|
|
203
|
+
});
|
|
204
|
+
it("activeDaysLast7/28 按已完结 activityDate 计", () => {
|
|
205
|
+
const s = summarize([
|
|
206
|
+
ev("2026-07-10T10:00:00-04:00", { sessionId: "a" }),
|
|
207
|
+
ev("2026-07-12T10:00:00-04:00", { sessionId: "b" }),
|
|
208
|
+
ev("2026-07-14T10:00:00-04:00", { sessionId: "c" }), // 当日不计
|
|
209
|
+
]);
|
|
210
|
+
expect(s.activeDaysLast7).toBe(2);
|
|
211
|
+
expect(s.activeDaysLast28).toBe(2);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
describe("validateCanonicalEventPayload (§2.5.2① 全组校验)", () => {
|
|
215
|
+
const now = new Date("2026-07-14T12:00:00Z");
|
|
216
|
+
it("legacy(全缺)放行;半 canonical 拒绝并列出缺项", () => {
|
|
217
|
+
expect(validateCanonicalEventPayload({ foo: 1 })).toEqual({
|
|
218
|
+
ok: true,
|
|
219
|
+
canonical: false,
|
|
220
|
+
});
|
|
221
|
+
const r = validateCanonicalEventPayload({ actionKind: "comment" }, { now });
|
|
222
|
+
expect(r.ok).toBe(false);
|
|
223
|
+
if (!r.ok) {
|
|
224
|
+
expect(r.errors.join(";")).toMatch(/occurredAt/);
|
|
225
|
+
expect(r.errors.join(";")).toMatch(/jobId/);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
it("完整 canonical 通过;occurredAt 超 48h 补录窗或无 offset 拒绝", () => {
|
|
229
|
+
const good = {
|
|
230
|
+
occurredAt: "2026-07-14T11:00:00Z",
|
|
231
|
+
eventTimezone: "America/New_York",
|
|
232
|
+
actionKind: "comment",
|
|
233
|
+
eventSchemaVersion: 1,
|
|
234
|
+
jobId: "job-1",
|
|
235
|
+
};
|
|
236
|
+
expect(validateCanonicalEventPayload(good, { now }).ok).toBe(true);
|
|
237
|
+
expect(validateCanonicalEventPayload({ ...good, occurredAt: "2026-07-10T11:00:00Z" }, { now }).ok).toBe(false);
|
|
238
|
+
expect(validateCanonicalEventPayload({ ...good, occurredAt: "2026-07-14T11:00:00" }, { now }).ok).toBe(false);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
describe("canonical v2 时区(需求A:允许 null + timezoneKnown)", () => {
|
|
242
|
+
const now = new Date("2026-07-14T12:00:00Z");
|
|
243
|
+
const base = {
|
|
244
|
+
occurredAt: "2026-07-14T11:00:00Z",
|
|
245
|
+
actionKind: "comment",
|
|
246
|
+
eventSchemaVersion: 2,
|
|
247
|
+
jobId: "job-1",
|
|
248
|
+
};
|
|
249
|
+
it("v1 仍要求有效 IANA(回归:不受 v2 影响)", () => {
|
|
250
|
+
const v1 = {
|
|
251
|
+
occurredAt: "2026-07-14T11:00:00Z",
|
|
252
|
+
eventTimezone: null,
|
|
253
|
+
actionKind: "comment",
|
|
254
|
+
eventSchemaVersion: 1,
|
|
255
|
+
jobId: "job-1",
|
|
256
|
+
};
|
|
257
|
+
expect(validateCanonicalEventPayload(v1, { now }).ok).toBe(false);
|
|
258
|
+
});
|
|
259
|
+
it("v2 timezoneKnown=false + eventTimezone=null → 通过", () => {
|
|
260
|
+
expect(validateCanonicalEventPayload({ ...base, eventTimezone: null, timezoneKnown: false }, { now }).ok).toBe(true);
|
|
261
|
+
});
|
|
262
|
+
it("v2 timezoneKnown=true + 有效 IANA → 通过", () => {
|
|
263
|
+
expect(validateCanonicalEventPayload({ ...base, eventTimezone: "America/New_York", timezoneKnown: true }, { now }).ok).toBe(true);
|
|
264
|
+
});
|
|
265
|
+
it("v2 timezoneKnown=false 但 eventTimezone 非 null → 拒", () => {
|
|
266
|
+
expect(validateCanonicalEventPayload({ ...base, eventTimezone: "UTC", timezoneKnown: false }, { now }).ok).toBe(false);
|
|
267
|
+
});
|
|
268
|
+
it("v2 缺 timezoneKnown → 拒", () => {
|
|
269
|
+
expect(validateCanonicalEventPayload({ ...base, eventTimezone: null }, { now })
|
|
270
|
+
.ok).toBe(false);
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
describe("validateInteractionActionOutcomePayload (需求A §5.1 交叉校验)", () => {
|
|
274
|
+
it("成功:ok=true/succeeded/NONE/stop=false/retryable=false", () => {
|
|
275
|
+
const r = validateInteractionActionOutcomePayload({
|
|
276
|
+
outcome: "succeeded",
|
|
277
|
+
ok: true,
|
|
278
|
+
stop: false,
|
|
279
|
+
retryable: false,
|
|
280
|
+
code: "NONE",
|
|
281
|
+
phase: "verify",
|
|
282
|
+
method: "coordinate-input",
|
|
283
|
+
});
|
|
284
|
+
expect(r.ok).toBe(true);
|
|
285
|
+
});
|
|
286
|
+
it("终止:STOP_CHALLENGE ⇒ stopped/stop=true/retryable=false/ok=false", () => {
|
|
287
|
+
expect(validateInteractionActionOutcomePayload({
|
|
288
|
+
outcome: "stopped",
|
|
289
|
+
ok: false,
|
|
290
|
+
stop: true,
|
|
291
|
+
retryable: false,
|
|
292
|
+
code: "STOP_CHALLENGE",
|
|
293
|
+
phase: "precondition",
|
|
294
|
+
method: null,
|
|
295
|
+
}).ok).toBe(true);
|
|
296
|
+
});
|
|
297
|
+
it("STOP_WRITE_UNCERTAIN ⇒ outcome=uncertain(不是 stopped)", () => {
|
|
298
|
+
const uncertain = {
|
|
299
|
+
ok: false,
|
|
300
|
+
stop: true,
|
|
301
|
+
retryable: false,
|
|
302
|
+
code: "STOP_WRITE_UNCERTAIN",
|
|
303
|
+
phase: "verify",
|
|
304
|
+
method: "coordinate-input",
|
|
305
|
+
};
|
|
306
|
+
expect(validateInteractionActionOutcomePayload({
|
|
307
|
+
...uncertain,
|
|
308
|
+
outcome: "uncertain",
|
|
309
|
+
}).ok).toBe(true);
|
|
310
|
+
expect(validateInteractionActionOutcomePayload({
|
|
311
|
+
...uncertain,
|
|
312
|
+
outcome: "stopped",
|
|
313
|
+
}).ok).toBe(false);
|
|
314
|
+
});
|
|
315
|
+
it("可重试:TARGET_STALE ⇒ failed/stop=false/retryable=true", () => {
|
|
316
|
+
expect(validateInteractionActionOutcomePayload({
|
|
317
|
+
outcome: "failed",
|
|
318
|
+
ok: false,
|
|
319
|
+
stop: false,
|
|
320
|
+
retryable: true,
|
|
321
|
+
code: "TARGET_STALE",
|
|
322
|
+
phase: "target",
|
|
323
|
+
method: null,
|
|
324
|
+
}).ok).toBe(true);
|
|
325
|
+
});
|
|
326
|
+
it("非法组合拒:ok=true 但 outcome=stopped", () => {
|
|
327
|
+
expect(validateInteractionActionOutcomePayload({
|
|
328
|
+
outcome: "stopped",
|
|
329
|
+
ok: true,
|
|
330
|
+
stop: false,
|
|
331
|
+
retryable: false,
|
|
332
|
+
code: "NONE",
|
|
333
|
+
phase: "verify",
|
|
334
|
+
method: null,
|
|
335
|
+
}).ok).toBe(false);
|
|
336
|
+
});
|
|
337
|
+
it("未知 code 拒绝(不静默降级)", () => {
|
|
338
|
+
expect(validateInteractionActionOutcomePayload({
|
|
339
|
+
outcome: "stopped",
|
|
340
|
+
ok: false,
|
|
341
|
+
stop: true,
|
|
342
|
+
retryable: false,
|
|
343
|
+
code: "STOP_SOMETHING_NEW",
|
|
344
|
+
phase: "verify",
|
|
345
|
+
method: null,
|
|
346
|
+
}).ok).toBe(false);
|
|
347
|
+
});
|
|
348
|
+
it("method=null 在 input/submit/verify 阶段拒(已执行输入)", () => {
|
|
349
|
+
expect(validateInteractionActionOutcomePayload({
|
|
350
|
+
outcome: "stopped",
|
|
351
|
+
ok: false,
|
|
352
|
+
stop: true,
|
|
353
|
+
retryable: false,
|
|
354
|
+
code: "STOP_POST_CLICK_NO_CHANGE",
|
|
355
|
+
phase: "input",
|
|
356
|
+
method: null,
|
|
357
|
+
}).ok).toBe(false);
|
|
358
|
+
});
|
|
359
|
+
it("method 字符串 'null' 拒(必须 JSON null)", () => {
|
|
360
|
+
expect(validateInteractionActionOutcomePayload({
|
|
361
|
+
outcome: "stopped",
|
|
362
|
+
ok: false,
|
|
363
|
+
stop: true,
|
|
364
|
+
retryable: false,
|
|
365
|
+
code: "STOP_CHALLENGE",
|
|
366
|
+
phase: "precondition",
|
|
367
|
+
method: "null",
|
|
368
|
+
}).ok).toBe(false);
|
|
369
|
+
});
|
|
370
|
+
it("经 validateCanonicalEventPayload(eventType) 集成:outcome 字段非法 → 整体拒", () => {
|
|
371
|
+
const now = new Date("2026-07-14T12:00:00Z");
|
|
372
|
+
const r = validateCanonicalEventPayload({
|
|
373
|
+
occurredAt: "2026-07-14T11:00:00Z",
|
|
374
|
+
eventTimezone: null,
|
|
375
|
+
timezoneKnown: false,
|
|
376
|
+
actionKind: "comment",
|
|
377
|
+
eventSchemaVersion: 2,
|
|
378
|
+
jobId: "job-1",
|
|
379
|
+
outcome: "succeeded",
|
|
380
|
+
ok: false, // 与 succeeded 矛盾
|
|
381
|
+
stop: false,
|
|
382
|
+
retryable: false,
|
|
383
|
+
code: "NONE",
|
|
384
|
+
phase: "verify",
|
|
385
|
+
method: null,
|
|
386
|
+
}, { now, eventType: INTERACTION_ACTION_OUTCOME_TYPE });
|
|
387
|
+
expect(r.ok).toBe(false);
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
describe("computeEffectiveTimezone (§4.3① UTC 毒化修复)", () => {
|
|
391
|
+
it("默认填充的 UTC 不算已知;真配置 risk 优先;env 按 lastHealthAt 最新", () => {
|
|
392
|
+
expect(computeEffectiveTimezone({
|
|
393
|
+
risk: { timezone: "UTC", timezoneConfigured: false },
|
|
394
|
+
browserEnvs: [],
|
|
395
|
+
})).toEqual({ effectiveTimezone: "unknown", timezoneKnown: false });
|
|
396
|
+
expect(computeEffectiveTimezone({
|
|
397
|
+
risk: { timezone: "UTC", timezoneConfigured: true }, // 显式配置 UTC=已知
|
|
398
|
+
browserEnvs: [],
|
|
399
|
+
})).toEqual({ effectiveTimezone: "UTC", timezoneKnown: true });
|
|
400
|
+
expect(computeEffectiveTimezone({
|
|
401
|
+
risk: { timezone: "America/Chicago", timezoneConfigured: true },
|
|
402
|
+
browserEnvs: [
|
|
403
|
+
{
|
|
404
|
+
timezone: "Asia/Tokyo",
|
|
405
|
+
timezoneConfigured: true,
|
|
406
|
+
lastHealthAt: new Date(),
|
|
407
|
+
createdAt: new Date(),
|
|
408
|
+
},
|
|
409
|
+
],
|
|
410
|
+
}).effectiveTimezone).toBe("America/Chicago"); // 冲突裁决:risk 权威
|
|
411
|
+
expect(computeEffectiveTimezone({
|
|
412
|
+
risk: null,
|
|
413
|
+
browserEnvs: [
|
|
414
|
+
{
|
|
415
|
+
timezone: "Asia/Tokyo",
|
|
416
|
+
timezoneConfigured: true,
|
|
417
|
+
lastHealthAt: new Date("2026-07-01"),
|
|
418
|
+
createdAt: new Date("2026-06-01"),
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
timezone: "Europe/London",
|
|
422
|
+
timezoneConfigured: true,
|
|
423
|
+
lastHealthAt: new Date("2026-07-10"),
|
|
424
|
+
createdAt: new Date("2026-05-01"),
|
|
425
|
+
},
|
|
426
|
+
],
|
|
427
|
+
}).effectiveTimezone).toBe("Europe/London");
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
//# sourceMappingURL=activity.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.test.js","sourceRoot":"","sources":["../src/activity.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 客户面内容审核(Client Content Delivery)Phase 1 —— feature flag(设计 §10 Deploy A/B)。
|
|
3
|
+
*
|
|
4
|
+
* `CLIENT_DELIVERY_PORTAL_BRAND_IDS`:逗号分隔的 brandId 集合,**默认空 = 对所有 brand 关闭**。
|
|
5
|
+
* - 未在集合内的 brand:portal 端点对 client 返 404、发布链行为**完全不变**(expand-only)。
|
|
6
|
+
* - 在集合内的 brand:开放客户 portal + 发布硬门参与评估。
|
|
7
|
+
*
|
|
8
|
+
* **API 与 worker 必须用同一 env 值**(否则发布门在两条路径判定不一致)。上线 enforce 前须做
|
|
9
|
+
* 多进程 allowlist 指纹一致性核对(Codex:单进程漏配 env = fail-open 绕过)。
|
|
10
|
+
*
|
|
11
|
+
* `CLIENT_DELIVERY_PUBLISH_GATE_ENFORCED`:发布硬门 off→enforce 切换,**默认 off**。
|
|
12
|
+
* - off(默认):发布链**根本不评估** client gate(零 SQL、零行为变化)——Deploy A expand-only。
|
|
13
|
+
* (尚无 shadow 观测模式;未来若要“评估但不拦”的真 shadow,须在此加第三档并让调用方评估后不阻断。)
|
|
14
|
+
* - enforce:客户未当前批准 → 拒排期。**上线 enforce 前须先补齐 claim 时二次校验 / 评论链 gate /
|
|
15
|
+
* 直建 scheduled_job 旁路收口**(Codex 复评:plan-time 门是 preflight,非唯一 chokepoint)。
|
|
16
|
+
*/
|
|
17
|
+
/** 解析 allowlist(逗号分隔,trim 去空;解析绝不抛异常)。空/未设 → 空集合。 */
|
|
18
|
+
export declare function clientDeliveryEnabledBrandIds(): Set<string>;
|
|
19
|
+
/**
|
|
20
|
+
* 某 brand 是否开启客户交付。**零 SQL**:空集合或未命中直接 false,供发布门在第一条 SQL 前短路
|
|
21
|
+
* (flag 关时对 evaluateReviewGateInTx 零行为影响)。brandId 为空/未知一律 false(fail-closed)。
|
|
22
|
+
*/
|
|
23
|
+
export declare function isClientDeliveryEnabledForBrand(brandId: string | null | undefined): boolean;
|
|
24
|
+
/** 发布硬门是否 enforce(默认 shadow:只有显式 =enforce 才阻断)。 */
|
|
25
|
+
export declare function isClientDeliveryPublishGateEnforced(): boolean;
|
|
26
|
+
//# sourceMappingURL=client-delivery-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-delivery-config.d.ts","sourceRoot":"","sources":["../src/client-delivery-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,qDAAqD;AACrD,wBAAgB,6BAA6B,IAAI,GAAG,CAAC,MAAM,CAAC,CAS3D;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACjC,OAAO,CAGT;AAED,mDAAmD;AACnD,wBAAgB,mCAAmC,IAAI,OAAO,CAE7D"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 客户面内容审核(Client Content Delivery)Phase 1 —— feature flag(设计 §10 Deploy A/B)。
|
|
3
|
+
*
|
|
4
|
+
* `CLIENT_DELIVERY_PORTAL_BRAND_IDS`:逗号分隔的 brandId 集合,**默认空 = 对所有 brand 关闭**。
|
|
5
|
+
* - 未在集合内的 brand:portal 端点对 client 返 404、发布链行为**完全不变**(expand-only)。
|
|
6
|
+
* - 在集合内的 brand:开放客户 portal + 发布硬门参与评估。
|
|
7
|
+
*
|
|
8
|
+
* **API 与 worker 必须用同一 env 值**(否则发布门在两条路径判定不一致)。上线 enforce 前须做
|
|
9
|
+
* 多进程 allowlist 指纹一致性核对(Codex:单进程漏配 env = fail-open 绕过)。
|
|
10
|
+
*
|
|
11
|
+
* `CLIENT_DELIVERY_PUBLISH_GATE_ENFORCED`:发布硬门 off→enforce 切换,**默认 off**。
|
|
12
|
+
* - off(默认):发布链**根本不评估** client gate(零 SQL、零行为变化)——Deploy A expand-only。
|
|
13
|
+
* (尚无 shadow 观测模式;未来若要“评估但不拦”的真 shadow,须在此加第三档并让调用方评估后不阻断。)
|
|
14
|
+
* - enforce:客户未当前批准 → 拒排期。**上线 enforce 前须先补齐 claim 时二次校验 / 评论链 gate /
|
|
15
|
+
* 直建 scheduled_job 旁路收口**(Codex 复评:plan-time 门是 preflight,非唯一 chokepoint)。
|
|
16
|
+
*/
|
|
17
|
+
/** 解析 allowlist(逗号分隔,trim 去空;解析绝不抛异常)。空/未设 → 空集合。 */
|
|
18
|
+
export function clientDeliveryEnabledBrandIds() {
|
|
19
|
+
const raw = process.env["CLIENT_DELIVERY_PORTAL_BRAND_IDS"];
|
|
20
|
+
if (!raw)
|
|
21
|
+
return new Set();
|
|
22
|
+
return new Set(raw
|
|
23
|
+
.split(",")
|
|
24
|
+
.map((s) => s.trim())
|
|
25
|
+
.filter((s) => s.length > 0));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 某 brand 是否开启客户交付。**零 SQL**:空集合或未命中直接 false,供发布门在第一条 SQL 前短路
|
|
29
|
+
* (flag 关时对 evaluateReviewGateInTx 零行为影响)。brandId 为空/未知一律 false(fail-closed)。
|
|
30
|
+
*/
|
|
31
|
+
export function isClientDeliveryEnabledForBrand(brandId) {
|
|
32
|
+
if (!brandId)
|
|
33
|
+
return false;
|
|
34
|
+
return clientDeliveryEnabledBrandIds().has(brandId);
|
|
35
|
+
}
|
|
36
|
+
/** 发布硬门是否 enforce(默认 shadow:只有显式 =enforce 才阻断)。 */
|
|
37
|
+
export function isClientDeliveryPublishGateEnforced() {
|
|
38
|
+
return process.env["CLIENT_DELIVERY_PUBLISH_GATE_ENFORCED"] === "enforce";
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=client-delivery-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-delivery-config.js","sourceRoot":"","sources":["../src/client-delivery-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,qDAAqD;AACrD,MAAM,UAAU,6BAA6B;IAC3C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAC5D,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,GAAG,EAAE,CAAC;IAC3B,OAAO,IAAI,GAAG,CACZ,GAAG;SACA,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAC/B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,+BAA+B,CAC7C,OAAkC;IAElC,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,OAAO,6BAA6B,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACtD,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,mCAAmC;IACjD,OAAO,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,KAAK,SAAS,CAAC;AAC5E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-delivery-config.test.d.ts","sourceRoot":"","sources":["../src/client-delivery-config.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { clientDeliveryEnabledBrandIds, isClientDeliveryEnabledForBrand, isClientDeliveryPublishGateEnforced, } from "./client-delivery-config.js";
|
|
3
|
+
const BRAND = "brand-a";
|
|
4
|
+
afterEach(() => {
|
|
5
|
+
delete process.env.CLIENT_DELIVERY_PORTAL_BRAND_IDS;
|
|
6
|
+
delete process.env.CLIENT_DELIVERY_PUBLISH_GATE_ENFORCED;
|
|
7
|
+
});
|
|
8
|
+
describe("client-delivery feature flag", () => {
|
|
9
|
+
it("defaults to empty set (all brands off)", () => {
|
|
10
|
+
expect(clientDeliveryEnabledBrandIds().size).toBe(0);
|
|
11
|
+
expect(isClientDeliveryEnabledForBrand(BRAND)).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
it("parses comma-separated allowlist, trimming blanks", () => {
|
|
14
|
+
process.env.CLIENT_DELIVERY_PORTAL_BRAND_IDS = ` ${BRAND} , , brand-b `;
|
|
15
|
+
const set = clientDeliveryEnabledBrandIds();
|
|
16
|
+
expect(set.has(BRAND)).toBe(true);
|
|
17
|
+
expect(set.has("brand-b")).toBe(true);
|
|
18
|
+
expect(set.has("")).toBe(false);
|
|
19
|
+
expect(isClientDeliveryEnabledForBrand(BRAND)).toBe(true);
|
|
20
|
+
expect(isClientDeliveryEnabledForBrand("brand-c")).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
it("null/undefined/empty brandId is fail-closed false", () => {
|
|
23
|
+
process.env.CLIENT_DELIVERY_PORTAL_BRAND_IDS = BRAND;
|
|
24
|
+
expect(isClientDeliveryEnabledForBrand(null)).toBe(false);
|
|
25
|
+
expect(isClientDeliveryEnabledForBrand(undefined)).toBe(false);
|
|
26
|
+
expect(isClientDeliveryEnabledForBrand("")).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
it("publish gate defaults to shadow; only =enforce enforces", () => {
|
|
29
|
+
expect(isClientDeliveryPublishGateEnforced()).toBe(false);
|
|
30
|
+
process.env.CLIENT_DELIVERY_PUBLISH_GATE_ENFORCED = "shadow";
|
|
31
|
+
expect(isClientDeliveryPublishGateEnforced()).toBe(false);
|
|
32
|
+
process.env.CLIENT_DELIVERY_PUBLISH_GATE_ENFORCED = "enforce";
|
|
33
|
+
expect(isClientDeliveryPublishGateEnforced()).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
//# sourceMappingURL=client-delivery-config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-delivery-config.test.js","sourceRoot":"","sources":["../src/client-delivery-config.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EACL,6BAA6B,EAC7B,+BAA+B,EAC/B,mCAAmC,GACpC,MAAM,6BAA6B,CAAC;AAErC,MAAM,KAAK,GAAG,SAAS,CAAC;AAExB,SAAS,CAAC,GAAG,EAAE;IACb,OAAO,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC;IACpD,OAAO,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC;AAC3D,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,6BAA6B,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,+BAA+B,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,OAAO,CAAC,GAAG,CAAC,gCAAgC,GAAG,IAAI,KAAK,eAAe,CAAC;QACxE,MAAM,GAAG,GAAG,6BAA6B,EAAE,CAAC;QAC5C,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChC,MAAM,CAAC,+BAA+B,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,CAAC,+BAA+B,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,OAAO,CAAC,GAAG,CAAC,gCAAgC,GAAG,KAAK,CAAC;QACrD,MAAM,CAAC,+BAA+B,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1D,MAAM,CAAC,+BAA+B,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/D,MAAM,CAAC,+BAA+B,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CAAC,mCAAmC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,qCAAqC,GAAG,QAAQ,CAAC;QAC7D,MAAM,CAAC,mCAAmC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,qCAAqC,GAAG,SAAS,CAAC;QAC9D,MAAM,CAAC,mCAAmC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内容复审的服务端计分与 verdict 判定(纯函数,权威口径)。
|
|
3
|
+
*
|
|
4
|
+
* LLM 只产出六维原始分与证据;综合分由这里按版本化权重计算,verdict 由
|
|
5
|
+
* precedence 规则判定——防止模型自报综合分漂移/越权(独立模型换代后分布
|
|
6
|
+
* 变化不影响阈值语义)。策略可整体替换(system_settings 覆盖默认),每次
|
|
7
|
+
* 复审记录 thresholdSnapshot 供金丝雀跨版本对比。
|
|
8
|
+
*/
|
|
9
|
+
export type ContentReviewDimensionKey = "context_anchoring" | "burstiness" | "ai_tone" | "persona_voice" | "compliance" | "self_repetition";
|
|
10
|
+
export type ContentReviewDimensionScores = Record<ContentReviewDimensionKey, {
|
|
11
|
+
score: number;
|
|
12
|
+
issues: string[];
|
|
13
|
+
}>;
|
|
14
|
+
export type ContentReviewPolicy = {
|
|
15
|
+
policyVersion: string;
|
|
16
|
+
/** 六维权重(加权平均,权重和不必为 1,内部归一)。 */
|
|
17
|
+
weights: Record<ContentReviewDimensionKey, number>;
|
|
18
|
+
/** 综合分 < blockBelow → block;< reviseBelow → revise;否则 pass。 */
|
|
19
|
+
blockBelow: number;
|
|
20
|
+
reviseBelow: number;
|
|
21
|
+
/** 一票否决:该维 < 阈值直接 block(情境锚定最能暴露 AI 且骗不了)。 */
|
|
22
|
+
contextAnchoringBlockBelow: number;
|
|
23
|
+
/** 合规红线:该维 < 阈值直接 block(品牌硬推/导流/站外联系)。 */
|
|
24
|
+
complianceBlockBelow: number;
|
|
25
|
+
/** 静默期账号更克制:各阈值整体上移的偏置。 */
|
|
26
|
+
quietStageThresholdBonus: number;
|
|
27
|
+
};
|
|
28
|
+
export declare const CONTENT_REVIEW_RUBRIC_VERSION = "v1";
|
|
29
|
+
export declare const DEFAULT_CONTENT_REVIEW_POLICY: ContentReviewPolicy;
|
|
30
|
+
export type ContentReviewScoreInput = {
|
|
31
|
+
dimensions: ContentReviewDimensionScores;
|
|
32
|
+
/** 账号阶段;静默期收紧阈值。 */
|
|
33
|
+
stage?: string | null;
|
|
34
|
+
policy?: ContentReviewPolicy;
|
|
35
|
+
};
|
|
36
|
+
export type ContentReviewScoreResult = {
|
|
37
|
+
verdict: "pass" | "revise" | "block";
|
|
38
|
+
computedScore: number;
|
|
39
|
+
/** 触发 verdict 的规则(审计/调参可读)。 */
|
|
40
|
+
decidedBy: "compliance_hard_fail" | "context_anchoring_hard_fail" | "score_block" | "score_revise" | "score_pass";
|
|
41
|
+
thresholdSnapshot: ContentReviewThresholdSnapshot;
|
|
42
|
+
};
|
|
43
|
+
export type ContentReviewThresholdSnapshot = {
|
|
44
|
+
policyVersion: string;
|
|
45
|
+
weights: Record<ContentReviewDimensionKey, number>;
|
|
46
|
+
blockBelow: number;
|
|
47
|
+
reviseBelow: number;
|
|
48
|
+
contextAnchoringBlockBelow: number;
|
|
49
|
+
complianceBlockBelow: number;
|
|
50
|
+
stageBonusApplied: number;
|
|
51
|
+
stage: string | null;
|
|
52
|
+
};
|
|
53
|
+
/** 本次请求实际生效的策略快照(degraded/deterministic 分支也要完整留档)。 */
|
|
54
|
+
export declare function getContentReviewThresholdSnapshot(policy: ContentReviewPolicy, stage?: string | null): ContentReviewThresholdSnapshot;
|
|
55
|
+
/** system_settings 覆盖默认策略时的合并(部分覆盖,未给字段用默认)。 */
|
|
56
|
+
export declare function mergeContentReviewPolicy(override: Partial<ContentReviewPolicy> | null | undefined): ContentReviewPolicy;
|
|
57
|
+
export declare function scoreContentReview(input: ContentReviewScoreInput): ContentReviewScoreResult;
|
|
58
|
+
/**
|
|
59
|
+
* 确定性合规预检(LLM 之前,命中即 block 不烧 token):
|
|
60
|
+
* 链接/站外联系方式是评论红线中可确定性识别的部分;品牌硬推语义仍交 LLM。
|
|
61
|
+
*/
|
|
62
|
+
export type DeterministicComplianceHit = {
|
|
63
|
+
rule: "url" | "email" | "handle_solicitation";
|
|
64
|
+
match: string;
|
|
65
|
+
};
|
|
66
|
+
export declare function detectDeterministicComplianceHits(draft: string): DeterministicComplianceHit[];
|
|
67
|
+
//# sourceMappingURL=content-review-scoring.d.ts.map
|