@lightcone-ai/daemon 0.11.0 → 0.13.0

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,240 @@
1
+ function pickField(source, keys = []) {
2
+ for (const key of keys) {
3
+ const value = source?.[key];
4
+ if (value === undefined || value === null) continue;
5
+ const asText = typeof value === 'string' ? value.trim() : value;
6
+ if (asText === '') continue;
7
+ return value;
8
+ }
9
+ return null;
10
+ }
11
+
12
+ export function normalizeBoolean(value, fallback = false) {
13
+ if (value === undefined || value === null) return fallback;
14
+ if (typeof value === 'boolean') return value;
15
+ const normalized = String(value).trim().toLowerCase();
16
+ if (!normalized) return fallback;
17
+ if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) return true;
18
+ if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) return false;
19
+ return fallback;
20
+ }
21
+
22
+ function normalizeRole(value) {
23
+ return String(value ?? '').trim().toLowerCase();
24
+ }
25
+
26
+ function normalizeStringArray(value) {
27
+ if (!Array.isArray(value)) return [];
28
+ return value
29
+ .map(item => String(item ?? '').trim())
30
+ .filter(Boolean);
31
+ }
32
+
33
+ export function composeDraftText({ title, text, tags = [] } = {}) {
34
+ const lines = [];
35
+ if (title && String(title).trim()) lines.push(String(title).trim());
36
+ if (text && String(text).trim()) lines.push(String(text).trim());
37
+ const normalizedTags = normalizeStringArray(tags);
38
+ if (normalizedTags.length > 0) {
39
+ lines.push(normalizedTags.map(tag => (tag.startsWith('#') ? tag : `#${tag}`)).join(' '));
40
+ }
41
+ return lines.join('\n').trim();
42
+ }
43
+
44
+ function collectPayloadFlags(payload = {}) {
45
+ const accountId = pickField(payload, [
46
+ 'account_id',
47
+ 'accountId',
48
+ 'target_account_id',
49
+ 'targetAccountId',
50
+ ]);
51
+ const accountRole = pickField(payload, [
52
+ 'account_role',
53
+ 'accountRole',
54
+ 'target_account_role',
55
+ 'targetAccountRole',
56
+ ]);
57
+
58
+ const autoPublishViaDelegation = normalizeBoolean(
59
+ pickField(payload, [
60
+ 'auto_publish_via_delegation',
61
+ 'autoPublishViaDelegation',
62
+ 'auto_publish',
63
+ 'autoPublish',
64
+ ]),
65
+ false
66
+ );
67
+
68
+ const contentIndicatesCollaboration = normalizeBoolean(
69
+ pickField(payload, [
70
+ 'content_indicates_collaboration',
71
+ 'contentIndicatesCollaboration',
72
+ 'is_ad_content',
73
+ 'isAdContent',
74
+ 'ad_content',
75
+ 'adContent',
76
+ ]),
77
+ false
78
+ );
79
+
80
+ const hasAdDisclosure = normalizeBoolean(
81
+ pickField(payload, [
82
+ 'has_ad_disclosure',
83
+ 'hasAdDisclosure',
84
+ 'ad_disclosure',
85
+ 'adDisclosure',
86
+ ]),
87
+ false
88
+ );
89
+
90
+ const contentContainsAiGenerated = normalizeBoolean(
91
+ pickField(payload, [
92
+ 'content_contains_ai_generated',
93
+ 'contentContainsAiGenerated',
94
+ 'ai_generated',
95
+ 'aiGenerated',
96
+ ]),
97
+ false
98
+ );
99
+
100
+ const hasAiLabel = normalizeBoolean(
101
+ pickField(payload, [
102
+ 'has_ai_label',
103
+ 'hasAiLabel',
104
+ 'ai_labeled',
105
+ 'aiLabeled',
106
+ ]),
107
+ false
108
+ );
109
+
110
+ const publishHistory = Array.isArray(payload?.publish_history)
111
+ ? payload.publish_history
112
+ : (Array.isArray(payload?.publishHistory) ? payload.publishHistory : []);
113
+
114
+ return {
115
+ accountId: accountId ? String(accountId).trim() : null,
116
+ accountRole: normalizeRole(accountRole),
117
+ autoPublishViaDelegation,
118
+ contentIndicatesCollaboration,
119
+ hasAdDisclosure,
120
+ contentContainsAiGenerated,
121
+ hasAiLabel,
122
+ publishHistory,
123
+ };
124
+ }
125
+
126
+ function summarizePolicyViolations(policyScan = {}) {
127
+ const violations = Array.isArray(policyScan.violations) ? policyScan.violations : [];
128
+ const blockers = [];
129
+ const warnings = [];
130
+ for (const violation of violations) {
131
+ const severity = String(violation?.severity ?? '').trim().toLowerCase();
132
+ const item = {
133
+ code: String(violation?.rule_id ?? 'policy_violation'),
134
+ message: String(violation?.reason ?? violation?.term ?? 'policy violation').trim(),
135
+ snippet: violation?.snippet ?? null,
136
+ action: violation?.action ?? null,
137
+ term: violation?.term ?? null,
138
+ };
139
+ if (severity === 'blocker') blockers.push(item);
140
+ else warnings.push(item);
141
+ }
142
+ return { blockers, warnings };
143
+ }
144
+
145
+ function evaluatePrimaryAccountProtection(flags) {
146
+ if (flags.accountRole !== 'primary') return null;
147
+ if (!flags.autoPublishViaDelegation) return null;
148
+ return {
149
+ code: 'primary_account_protection',
150
+ message: 'primary account cannot use auto-publish via delegation; manual confirmation is required',
151
+ };
152
+ }
153
+
154
+ function evaluateLabelBlockers(flags, policyScan = {}) {
155
+ const blockers = [];
156
+ if (policyScan.ai_label_required && flags.contentContainsAiGenerated && !flags.hasAiLabel) {
157
+ blockers.push({
158
+ code: 'policy_ai_label_required',
159
+ message: 'AI-generated content is detected but AI label is missing',
160
+ });
161
+ }
162
+ if (policyScan.ad_disclosure_required && flags.contentIndicatesCollaboration && !flags.hasAdDisclosure) {
163
+ blockers.push({
164
+ code: 'policy_ad_disclosure_required',
165
+ message: 'ad/collaboration content requires platform disclosure label',
166
+ });
167
+ }
168
+ return blockers;
169
+ }
170
+
171
+ export async function runPublishPrecheck({
172
+ platform,
173
+ title,
174
+ text,
175
+ tags = [],
176
+ payload = {},
177
+ callTool,
178
+ }) {
179
+ if (typeof callTool !== 'function') {
180
+ throw new Error('callTool function is required');
181
+ }
182
+
183
+ const draftText = composeDraftText({ title, text, tags });
184
+ const flags = collectPayloadFlags(payload ?? {});
185
+ const blockers = [];
186
+ const warnings = [];
187
+
188
+ const policyScan = await callTool({
189
+ serverId: 'platform-policy-db',
190
+ toolName: 'platform_policy_db',
191
+ argumentsPayload: {
192
+ operation: 'scan_text',
193
+ platform,
194
+ text: draftText,
195
+ max_matches: 20,
196
+ },
197
+ });
198
+ const policySummary = summarizePolicyViolations(policyScan);
199
+ blockers.push(...policySummary.blockers);
200
+ warnings.push(...policySummary.warnings);
201
+
202
+ const primaryProtection = evaluatePrimaryAccountProtection(flags);
203
+ if (primaryProtection) blockers.push(primaryProtection);
204
+
205
+ blockers.push(...evaluateLabelBlockers(flags, policyScan));
206
+
207
+ let advisory = null;
208
+ try {
209
+ advisory = await callTool({
210
+ serverId: 'publish-window-optimizer',
211
+ toolName: 'publish_window_optimizer',
212
+ argumentsPayload: {
213
+ platform,
214
+ account_id: flags.accountId ?? undefined,
215
+ history: flags.publishHistory,
216
+ limit: 3,
217
+ },
218
+ });
219
+ if (advisory?.advisory?.level === 'suggest_delay') {
220
+ warnings.push({
221
+ code: 'publish_window_suggest_delay',
222
+ message: String(advisory?.advisory?.message ?? 'recommended publish window suggests delay').trim(),
223
+ });
224
+ }
225
+ } catch (error) {
226
+ warnings.push({
227
+ code: 'publish_window_optimizer_unavailable',
228
+ message: `publish-window-optimizer unavailable: ${error.message}`,
229
+ });
230
+ }
231
+
232
+ return {
233
+ ok: blockers.length === 0,
234
+ blockers,
235
+ warnings,
236
+ policyScan,
237
+ advisory,
238
+ payloadFlags: flags,
239
+ };
240
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {