@gobing-ai/knowledge-kit 0.0.34 → 0.0.35
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/.claude-plugin/marketplace.json +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/plugins/generations/daily-article-gen/dist/index.js +17 -3
- package/plugins/generations/daily-article-gen/src/index.ts +32 -3
- package/plugins/generations/dailynews-gen/dist/index.js +2 -1
- package/plugins/generations/dailynews-gen/src/script-builder.ts +6 -1
- package/plugins/generations/gemini-tts/dist/index.js +4 -3
- package/plugins/generations/omni-voice-gen/plugin.json +1 -1
- package/plugins/generations/voice-gen/dist/index.js +4 -3
- package/plugins/generations/voice-gen/src/index.ts +9 -3
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/daily-stages.ts +296 -39
- package/plugins/kk/scripts/kk-workflow-stages.ts +40 -0
- package/plugins/kk/skills/content-safety/references/platforms/wechatmp.md +2 -2
- package/plugins/kk/skills/content-safety/references/platforms/xiaoyuzhou.md +4 -2
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +32 -7
- package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +7 -4
- package/plugins/kk/workflows/kk-daily-safety.yaml +1 -1
- package/plugins/publishings/infoq-pub/dist/profile-check.js +30336 -0
- package/plugins/publishings/infoq-pub/plugin.json +1 -1
- package/plugins/publishings/juejin-pub/dist/profile-check.js +30322 -0
- package/plugins/publishings/juejin-pub/plugin.json +1 -1
- package/plugins/publishings/substack-pub/dist/profile-check.js +30392 -0
- package/plugins/publishings/substack-pub/plugin.json +1 -1
- package/plugins/publishings/wechatmp-pub/dist/index.js +307 -132
- package/plugins/publishings/wechatmp-pub/dist/profile-check.js +30271 -0
- package/plugins/publishings/wechatmp-pub/plugin.json +1 -1
- package/plugins/publishings/wechatmp-pub/src/enrich.ts +106 -145
- package/plugins/publishings/wechatmp-pub/src/flow.ts +283 -42
- package/plugins/publishings/wechatmp-pub/src/page.ts +20 -0
- package/plugins/publishings/x-pub/dist/profile-check.js +30328 -0
- package/plugins/publishings/x-pub/plugin.json +1 -1
- package/plugins/publishings/xhs-pub/dist/profile-check.js +30538 -0
- package/plugins/publishings/xhs-pub/plugin.json +1 -1
- package/plugins/generations/omni-voice-gen/dist/omni-voice-gen-prr8skpb. +0 -2
- package/plugins/generations/omni-voice-gen/dist/omni-voice-gen.js +0 -6
package/dist/index.js
CHANGED
|
@@ -43509,7 +43509,7 @@ function probeVerdict(exitCode, stdout) {
|
|
|
43509
43509
|
}
|
|
43510
43510
|
const fields = typeof parsed === "object" && parsed !== null ? parsed : undefined;
|
|
43511
43511
|
const status = typeof fields?.status === "string" ? fields.status : undefined;
|
|
43512
|
-
if (
|
|
43512
|
+
if (status === "verified" && exitCode === 0 || status === "refused" && exitCode === 1) {
|
|
43513
43513
|
return {
|
|
43514
43514
|
status,
|
|
43515
43515
|
...typeof fields?.profileDir === "string" ? { profileDir: fields.profileDir } : {},
|
package/package.json
CHANGED
|
@@ -22062,6 +22062,10 @@ function tagsFor(docs) {
|
|
|
22062
22062
|
var LEADING_H1_RE = /^#(?!#)\s*[^\n]+\n+/.source;
|
|
22063
22063
|
var NOTE_EXCLUDED_RE = /\u672A\u5199\u5165[\uFF1A:](.*)/g;
|
|
22064
22064
|
var HEX_ID_RE = /(?:[a-zA-Z0-9_-]+:)?[0-9a-f]{16,64}/g;
|
|
22065
|
+
var BOUNDARY_NOTE_RE = /^>[^\n]*(?:\u9009\u9898\u8FB9\u754C|\u672A\u5199\u5165)[^\n]*\n?/gm;
|
|
22066
|
+
function stripBoundaryNote(body) {
|
|
22067
|
+
return body.replace(BOUNDARY_NOTE_RE, "");
|
|
22068
|
+
}
|
|
22065
22069
|
function excludedIdsFromNote(body) {
|
|
22066
22070
|
const ids = new Set;
|
|
22067
22071
|
if (!body)
|
|
@@ -22080,18 +22084,27 @@ function docsToContent(docs, date6, bodyOverride) {
|
|
|
22080
22084
|
const title = episodeTitle(date6);
|
|
22081
22085
|
let body = renderDailyArticle(docs, date6);
|
|
22082
22086
|
if (bodyOverride) {
|
|
22083
|
-
const
|
|
22087
|
+
const cleaned = stripBoundaryNote(stripScaffold(bodyOverride.trim()).trim().replace(new RegExp(LEADING_H1_RE), "")).trim();
|
|
22084
22088
|
body = `# ${title}
|
|
22085
22089
|
|
|
22086
|
-
${
|
|
22090
|
+
${cleaned}
|
|
22087
22091
|
`;
|
|
22092
|
+
} else {
|
|
22093
|
+
body = stripBoundaryNote(body);
|
|
22088
22094
|
}
|
|
22095
|
+
const excludedIds = [...excludedIdsFromNote(bodyOverride)];
|
|
22089
22096
|
return ContentSchema.parse({
|
|
22090
22097
|
title,
|
|
22091
22098
|
body,
|
|
22092
22099
|
format: "markdown",
|
|
22093
22100
|
references: docs.filter((d) => d.sourceUri).map((d) => ({ url: d.sourceUri, title: d.title, cite: d.id })),
|
|
22094
|
-
metadata: {
|
|
22101
|
+
metadata: {
|
|
22102
|
+
generator: "kk:daily-article",
|
|
22103
|
+
date: date6,
|
|
22104
|
+
doc_count: docs.length,
|
|
22105
|
+
tags: tagsFor(docs),
|
|
22106
|
+
...excludedIds.length > 0 ? { excludedIds } : {}
|
|
22107
|
+
}
|
|
22095
22108
|
});
|
|
22096
22109
|
}
|
|
22097
22110
|
async function processGeneratorIO(inputPath, outputPath) {
|
|
@@ -22140,6 +22153,7 @@ if (import.meta.main) {
|
|
|
22140
22153
|
}
|
|
22141
22154
|
export {
|
|
22142
22155
|
stripScaffold,
|
|
22156
|
+
stripBoundaryNote,
|
|
22143
22157
|
renderDailyArticle,
|
|
22144
22158
|
processGeneratorIO,
|
|
22145
22159
|
main2 as main,
|
|
@@ -141,6 +141,20 @@ const LEADING_H1_RE = /^#(?!#)\s*[^\n]+\n+/.source;
|
|
|
141
141
|
const NOTE_EXCLUDED_RE = /未写入[::](.*)/g;
|
|
142
142
|
const HEX_ID_RE = /(?:[a-zA-Z0-9_-]+:)?[0-9a-f]{16,64}/g;
|
|
143
143
|
|
|
144
|
+
/**
|
|
145
|
+
* The 选题边界 note is pipeline bookkeeping: it names dropped doc ids and the rule that dropped them.
|
|
146
|
+
* It is the machine-readable exclusion signal (`excludedIdsFromNote`), so it stays in the agent's body
|
|
147
|
+
* file — but it must never reach a published body, where it printed an internal doc id and pipeline
|
|
148
|
+
* vocabulary to readers (operator report 2026-10-10). Matches only blockquote lines, which the article
|
|
149
|
+
* body reserves for exactly this note, so ordinary prose is untouched.
|
|
150
|
+
*/
|
|
151
|
+
const BOUNDARY_NOTE_RE = /^>[^\n]*(?:选题边界|未写入)[^\n]*\n?/gm;
|
|
152
|
+
|
|
153
|
+
/** Strip the internal boundary note from a body destined for a customer-visible surface. */
|
|
154
|
+
export function stripBoundaryNote(body: string): string {
|
|
155
|
+
return body.replace(BOUNDARY_NOTE_RE, '');
|
|
156
|
+
}
|
|
157
|
+
|
|
144
158
|
/** Parse the boundary-note ids out of an article body so references/metadata never cite excluded docs (judge finding 2026-10-02). */
|
|
145
159
|
export function excludedIdsFromNote(body: string | undefined): Set<string> {
|
|
146
160
|
const ids = new Set<string>();
|
|
@@ -162,15 +176,30 @@ export function docsToContent(docs: Doc[], date: string, bodyOverride?: string):
|
|
|
162
176
|
if (bodyOverride) {
|
|
163
177
|
// The canonical episode title is computed from the lead item; a leading H1 the agent may
|
|
164
178
|
// still have written is replaced (not duplicated) so body and metadata agree on every path.
|
|
165
|
-
|
|
166
|
-
|
|
179
|
+
// The boundary note is stripped here, not dropped upstream: the agent's body file keeps it as
|
|
180
|
+
// the exclusion signal, this Content is what readers see.
|
|
181
|
+
const cleaned = stripBoundaryNote(
|
|
182
|
+
stripScaffold(bodyOverride.trim()).trim().replace(new RegExp(LEADING_H1_RE), ''),
|
|
183
|
+
).trim();
|
|
184
|
+
body = `# ${title}\n\n${cleaned}\n`;
|
|
185
|
+
} else {
|
|
186
|
+
body = stripBoundaryNote(body);
|
|
167
187
|
}
|
|
188
|
+
// The ids the note named become metadata, so consumers that must filter excluded docs (show-notes
|
|
189
|
+
// plan copies) read a field instead of re-parsing a note that no longer exists.
|
|
190
|
+
const excludedIds = [...excludedIdsFromNote(bodyOverride)];
|
|
168
191
|
return ContentSchema.parse({
|
|
169
192
|
title,
|
|
170
193
|
body,
|
|
171
194
|
format: 'markdown',
|
|
172
195
|
references: docs.filter((d) => d.sourceUri).map((d) => ({ url: d.sourceUri, title: d.title, cite: d.id })),
|
|
173
|
-
metadata: {
|
|
196
|
+
metadata: {
|
|
197
|
+
generator: 'kk:daily-article',
|
|
198
|
+
date,
|
|
199
|
+
doc_count: docs.length,
|
|
200
|
+
tags: tagsFor(docs),
|
|
201
|
+
...(excludedIds.length > 0 ? { excludedIds } : {}),
|
|
202
|
+
},
|
|
174
203
|
});
|
|
175
204
|
}
|
|
176
205
|
|
|
@@ -22116,6 +22116,7 @@ function buildNewsVoiceScript(docs, options) {
|
|
|
22116
22116
|
const defaultEffectPreset = options?.effectPreset;
|
|
22117
22117
|
const broadcastDate = options?.date ?? new Date;
|
|
22118
22118
|
const dateStr = formatBroadcastDate(broadcastDate, language);
|
|
22119
|
+
const episodeDate = `${broadcastDate.getFullYear()}-${String(broadcastDate.getMonth() + 1).padStart(2, "0")}-${String(broadcastDate.getDate()).padStart(2, "0")}`;
|
|
22119
22120
|
const showName = "\u54B8\u8BDD\u54B8\u8BF4\u4E4B\u6BCF\u65E5AI\u4E71\u7096";
|
|
22120
22121
|
let hostName = options?.speakers?.host?.name || voiceProfile.replace(/[-_]?(news|voice|bot|profile)$/i, "") || voiceProfile;
|
|
22121
22122
|
if (hostName.length > 0) {
|
|
@@ -22157,7 +22158,7 @@ function buildNewsVoiceScript(docs, options) {
|
|
|
22157
22158
|
segments: segments2
|
|
22158
22159
|
});
|
|
22159
22160
|
}
|
|
22160
|
-
const title = options?.title ||
|
|
22161
|
+
const title = options?.title || `\u6BCF\u65E5AI\u4E71\u7096 \xB7 ${episodeDate}`;
|
|
22161
22162
|
const segments = [];
|
|
22162
22163
|
const scriptPlan = isZh ? options?.scriptPlan : undefined;
|
|
22163
22164
|
const planItems = new Map((scriptPlan?.items ?? []).map((item) => [item.id, item]));
|
|
@@ -313,6 +313,11 @@ export function buildNewsVoiceScript(docs: Doc[], options?: ScriptBuilderOptions
|
|
|
313
313
|
const defaultEffectPreset = options?.effectPreset;
|
|
314
314
|
const broadcastDate = options?.date ?? new Date();
|
|
315
315
|
const dateStr = formatBroadcastDate(broadcastDate, language);
|
|
316
|
+
// Canonical episode identifier: date-only, matching daily-article-gen's `episodeTitle`. One
|
|
317
|
+
// title has to cover the post body, ID3, RSS and the show notes, so the voicescript default
|
|
318
|
+
// must not invent its own form (it used to be `共N条`, which disagreed with the article title
|
|
319
|
+
// on every path where the workflow passes no explicit title).
|
|
320
|
+
const episodeDate = `${broadcastDate.getFullYear()}-${String(broadcastDate.getMonth() + 1).padStart(2, '0')}-${String(broadcastDate.getDate()).padStart(2, '0')}`;
|
|
316
321
|
// Column of 咸话咸说. The show is not the speaker; the host is.
|
|
317
322
|
const showName = '咸话咸说之每日AI乱炖';
|
|
318
323
|
let hostName =
|
|
@@ -365,7 +370,7 @@ export function buildNewsVoiceScript(docs: Doc[], options?: ScriptBuilderOptions
|
|
|
365
370
|
});
|
|
366
371
|
}
|
|
367
372
|
|
|
368
|
-
const title = options?.title ||
|
|
373
|
+
const title = options?.title || `每日AI乱炖 · ${episodeDate}`;
|
|
369
374
|
const segments: VoiceScriptSegment[] = [];
|
|
370
375
|
|
|
371
376
|
// F613: the plan owns the words on the zh path; en keeps templates (design: substitution is
|
|
@@ -23041,7 +23041,8 @@ async function processGeneratorIO(inputPath, outputPath, clientOverride, transco
|
|
|
23041
23041
|
let loudnessDip = false;
|
|
23042
23042
|
let lastError;
|
|
23043
23043
|
let attemptBody = generateBody;
|
|
23044
|
-
|
|
23044
|
+
const maxVerifyAttempts = Number.parseInt(process.env.VOICEBOX_MAX_VERIFY_ATTEMPTS ?? "2", 10) || 0;
|
|
23045
|
+
for (let verifyAttempt = 0;verifyAttempt <= maxVerifyAttempts && wavBytes === undefined; verifyAttempt++) {
|
|
23045
23046
|
if (verifyAttempt > 0) {
|
|
23046
23047
|
verifyRetries = verifyAttempt;
|
|
23047
23048
|
const chunkChars = Math.max(VOICEBOX_CHUNK_CHARS_MIN, Math.floor(attemptBody.max_chunk_chars / 2));
|
|
@@ -23079,7 +23080,7 @@ async function processGeneratorIO(inputPath, outputPath, clientOverride, transco
|
|
|
23079
23080
|
try {
|
|
23080
23081
|
const trans = await client.transcribe(attemptBytes, language);
|
|
23081
23082
|
const fidelity = transcriptionFidelity(segment.text, trans.text);
|
|
23082
|
-
if (fidelity < TRANSCRIPTION_FIDELITY_FLOOR && verifyAttempt <
|
|
23083
|
+
if (fidelity < TRANSCRIPTION_FIDELITY_FLOOR && verifyAttempt < maxVerifyAttempts) {
|
|
23083
23084
|
echoError(`voice-gen: transcription fidelity ${fidelity.toFixed(2)} below ${TRANSCRIPTION_FIDELITY_FLOOR} (looped/garbage audio): "${trans.text.slice(0, 80)}..."`);
|
|
23084
23085
|
continue;
|
|
23085
23086
|
}
|
|
@@ -23088,7 +23089,7 @@ async function processGeneratorIO(inputPath, outputPath, clientOverride, transco
|
|
|
23088
23089
|
}
|
|
23089
23090
|
const dip = detectLoudnessDip(attemptBytes);
|
|
23090
23091
|
loudnessDip = dip.dip;
|
|
23091
|
-
if (dip.dip && verifyAttempt <
|
|
23092
|
+
if (dip.dip && verifyAttempt < maxVerifyAttempts) {
|
|
23092
23093
|
echoError(`voice-gen: loudness dip detected at ${dip.dipAtSec ?? "?"}s \u2014 regenerating segment`);
|
|
23093
23094
|
continue;
|
|
23094
23095
|
}
|
|
@@ -23038,7 +23038,8 @@ async function processGeneratorIO(inputPath, outputPath, clientOverride, transco
|
|
|
23038
23038
|
let loudnessDip = false;
|
|
23039
23039
|
let lastError;
|
|
23040
23040
|
let attemptBody = generateBody;
|
|
23041
|
-
|
|
23041
|
+
const maxVerifyAttempts = Number.parseInt(process.env.VOICEBOX_MAX_VERIFY_ATTEMPTS ?? "2", 10) || 0;
|
|
23042
|
+
for (let verifyAttempt = 0;verifyAttempt <= maxVerifyAttempts && wavBytes === undefined; verifyAttempt++) {
|
|
23042
23043
|
if (verifyAttempt > 0) {
|
|
23043
23044
|
verifyRetries = verifyAttempt;
|
|
23044
23045
|
const chunkChars = Math.max(VOICEBOX_CHUNK_CHARS_MIN, Math.floor(attemptBody.max_chunk_chars / 2));
|
|
@@ -23076,7 +23077,7 @@ async function processGeneratorIO(inputPath, outputPath, clientOverride, transco
|
|
|
23076
23077
|
try {
|
|
23077
23078
|
const trans = await client.transcribe(attemptBytes, language);
|
|
23078
23079
|
const fidelity = transcriptionFidelity(segment.text, trans.text);
|
|
23079
|
-
if (fidelity < TRANSCRIPTION_FIDELITY_FLOOR && verifyAttempt <
|
|
23080
|
+
if (fidelity < TRANSCRIPTION_FIDELITY_FLOOR && verifyAttempt < maxVerifyAttempts) {
|
|
23080
23081
|
echoError(`voice-gen: transcription fidelity ${fidelity.toFixed(2)} below ${TRANSCRIPTION_FIDELITY_FLOOR} (looped/garbage audio): "${trans.text.slice(0, 80)}..."`);
|
|
23081
23082
|
continue;
|
|
23082
23083
|
}
|
|
@@ -23085,7 +23086,7 @@ async function processGeneratorIO(inputPath, outputPath, clientOverride, transco
|
|
|
23085
23086
|
}
|
|
23086
23087
|
const dip = detectLoudnessDip(attemptBytes);
|
|
23087
23088
|
loudnessDip = dip.dip;
|
|
23088
|
-
if (dip.dip && verifyAttempt <
|
|
23089
|
+
if (dip.dip && verifyAttempt < maxVerifyAttempts) {
|
|
23089
23090
|
echoError(`voice-gen: loudness dip detected at ${dip.dipAtSec ?? "?"}s \u2014 regenerating segment`);
|
|
23090
23091
|
continue;
|
|
23091
23092
|
}
|
|
@@ -212,7 +212,13 @@ export async function processGeneratorIO(
|
|
|
212
212
|
let loudnessDip = false;
|
|
213
213
|
let lastError: unknown;
|
|
214
214
|
let attemptBody = generateBody;
|
|
215
|
-
|
|
215
|
+
// Verify-retry budget. Hosted TTS is billed per request against a daily quota
|
|
216
|
+
// (Gemini Tier 1: 100/day), and each retry is a full re-synthesis — a 41-segment
|
|
217
|
+
// episode with the default 2 retries asks for up to 123 requests and dies partway
|
|
218
|
+
// through the render with no audio (daily-generate failures of 2026-10-06/07/09/10).
|
|
219
|
+
// Lower it for a quota-bound driver; 0 keeps the dip/fidelity result for QC instead.
|
|
220
|
+
const maxVerifyAttempts = Number.parseInt(process.env.VOICEBOX_MAX_VERIFY_ATTEMPTS ?? '2', 10) || 0;
|
|
221
|
+
for (let verifyAttempt = 0; verifyAttempt <= maxVerifyAttempts && wavBytes === undefined; verifyAttempt++) {
|
|
216
222
|
if (verifyAttempt > 0) {
|
|
217
223
|
verifyRetries = verifyAttempt;
|
|
218
224
|
const chunkChars = Math.max(VOICEBOX_CHUNK_CHARS_MIN, Math.floor(attemptBody.max_chunk_chars / 2));
|
|
@@ -262,7 +268,7 @@ export async function processGeneratorIO(
|
|
|
262
268
|
try {
|
|
263
269
|
const trans = await client.transcribe(attemptBytes, language);
|
|
264
270
|
const fidelity = transcriptionFidelity(segment.text, trans.text);
|
|
265
|
-
if (fidelity < TRANSCRIPTION_FIDELITY_FLOOR && verifyAttempt <
|
|
271
|
+
if (fidelity < TRANSCRIPTION_FIDELITY_FLOOR && verifyAttempt < maxVerifyAttempts) {
|
|
266
272
|
echoError(
|
|
267
273
|
`voice-gen: transcription fidelity ${fidelity.toFixed(2)} below ${TRANSCRIPTION_FIDELITY_FLOOR} (looped/garbage audio): "${trans.text.slice(0, 80)}..."`,
|
|
268
274
|
);
|
|
@@ -278,7 +284,7 @@ export async function processGeneratorIO(
|
|
|
278
284
|
// (dogfood 2026-09-08 A/B: same text regenerated clean, so retry usually clears).
|
|
279
285
|
const dip = detectLoudnessDip(attemptBytes);
|
|
280
286
|
loudnessDip = dip.dip;
|
|
281
|
-
if (dip.dip && verifyAttempt <
|
|
287
|
+
if (dip.dip && verifyAttempt < maxVerifyAttempts) {
|
|
282
288
|
echoError(`voice-gen: loudness dip detected at ${dip.dipAtSec ?? '?'}s — regenerating segment`);
|
|
283
289
|
continue;
|
|
284
290
|
}
|
package/plugins/kk/plugin.json
CHANGED