@gobing-ai/knowledge-kit 0.0.36 → 0.1.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.
Files changed (159) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/board/collect/index.css +654 -16
  3. package/dist/board/collect/index.js +191 -64
  4. package/dist/board/daily-voice/index.css +679 -12
  5. package/dist/board/daily-voice/index.js +185 -66
  6. package/dist/board/itc/index.css +679 -13
  7. package/dist/board/itc/index.js +188 -66
  8. package/dist/index.js +2878 -152
  9. package/package.json +3 -2
  10. package/plugins/generations/content-gen/dist/index.js +158 -12
  11. package/plugins/generations/core-facts-gen/dist/index.js +158 -12
  12. package/plugins/generations/daily-article-gen/dist/index.js +158 -12
  13. package/plugins/generations/dailynews-gen/dist/index.js +180 -12
  14. package/plugins/generations/dailynews-gen/src/script-plan.ts +32 -0
  15. package/plugins/generations/episode-plan-gen/dist/index.js +158 -12
  16. package/plugins/generations/gemini-tts/README.md +4 -3
  17. package/plugins/generations/gemini-tts/dist/index.js +158 -12
  18. package/plugins/generations/image-gen/dist/index.js +158 -12
  19. package/plugins/generations/news-report-gen/dist/index.js +158 -12
  20. package/plugins/generations/omni-voice-gen/README.md +15 -4
  21. package/plugins/generations/omni-voice-gen/profiles.json +1 -2
  22. package/plugins/generations/voice-gen/dist/index.js +158 -12
  23. package/plugins/generations/voxcpm-tts/README.md +21 -1
  24. package/plugins/generations/voxcpm-tts/dist/index.js +173 -13
  25. package/plugins/generations/voxcpm-tts/scripts/audit-speaker-consistency.py +109 -0
  26. package/plugins/generations/voxcpm-tts/src/voxcpm-client.ts +28 -1
  27. package/plugins/ingestions/aihot-ingest/dist/index.js +158 -12
  28. package/plugins/ingestions/horizon-ingest/dist/index.js +158 -12
  29. package/plugins/ingestions/last30days-ingest/dist/index.js +158 -12
  30. package/plugins/ingestions/web-search/dist/index.js +158 -12
  31. package/plugins/kk/commands/workflow-run.md +56 -18
  32. package/plugins/kk/decisions/kk-itc.yaml +46 -0
  33. package/plugins/kk/decisions/kk-seo-keyword.yaml +57 -0
  34. package/plugins/kk/decisions/kk-seo-page.yaml +230 -0
  35. package/plugins/kk/decisions/kk-seo-pair.yaml +21 -0
  36. package/plugins/kk/decisions/kk-seo-site.yaml +90 -0
  37. package/plugins/kk/plugin.json +1 -1
  38. package/plugins/kk/publish-presets/chinese-article.json +14 -0
  39. package/plugins/kk/publish-presets/technical-article.json +14 -0
  40. package/plugins/kk/references/smart-seo-attribution.md +85 -0
  41. package/plugins/kk/scripts/daily-stages.ts +42 -2
  42. package/plugins/kk/scripts/daily-voice-style.zh.md +1 -1
  43. package/plugins/kk/scripts/itc-stages.ts +25 -118
  44. package/plugins/kk/scripts/kk-workflow-stages.ts +38 -1
  45. package/plugins/kk/scripts/seo-decisions.ts +477 -0
  46. package/plugins/kk/scripts/seo-enrich.ts +728 -0
  47. package/plugins/kk/scripts/seo-facts.ts +610 -0
  48. package/plugins/kk/scripts/seo-fetch.ts +766 -0
  49. package/plugins/kk/scripts/seo-report.ts +460 -0
  50. package/plugins/kk/scripts/seo-rules.ts +1882 -0
  51. package/plugins/kk/scripts/seo-stages.ts +1044 -0
  52. package/plugins/kk/skills/article-adapt/SKILL.md +2 -2
  53. package/plugins/kk/skills/smart-seo/SKILL.md +183 -0
  54. package/plugins/kk/skills/smart-seo/references/method.md +126 -0
  55. package/plugins/kk/skills/smart-seo/references/narrative.md +125 -0
  56. package/plugins/kk/skills/smart-seo/references/operations.md +202 -0
  57. package/plugins/kk/skills/smart-seo/references/seo-geo-sources.md +90 -0
  58. package/plugins/kk/workflows/feature-verification.yaml +28 -24
  59. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +86 -18
  60. package/plugins/kk/workflows/kk-itc-author.yaml +406 -0
  61. package/plugins/kk/workflows/kk-itc.yaml +208 -682
  62. package/plugins/kk/workflows/kk-smart-seo.yaml +251 -0
  63. package/plugins/publishings/bilibili-pub/dist/index.js +21782 -0
  64. package/plugins/publishings/bilibili-pub/dist/profile-check.js +20410 -0
  65. package/plugins/publishings/bilibili-pub/package.json +20 -0
  66. package/plugins/publishings/bilibili-pub/plugin.json +12 -0
  67. package/plugins/publishings/bilibili-pub/src/flow.ts +1296 -0
  68. package/plugins/publishings/bilibili-pub/src/index.ts +139 -0
  69. package/plugins/publishings/bilibili-pub/src/profile-check.ts +19 -0
  70. package/plugins/publishings/bilibili-pub/src/selectors.ts +222 -0
  71. package/plugins/publishings/bilibili-pub/tsconfig.json +4 -0
  72. package/plugins/publishings/cnblogs-pub/dist/index.js +21383 -0
  73. package/plugins/publishings/cnblogs-pub/dist/profile-check.js +20412 -0
  74. package/plugins/publishings/cnblogs-pub/package.json +19 -0
  75. package/plugins/publishings/cnblogs-pub/plugin.json +12 -0
  76. package/plugins/publishings/cnblogs-pub/src/flow.ts +708 -0
  77. package/plugins/publishings/cnblogs-pub/src/index.ts +172 -0
  78. package/plugins/publishings/cnblogs-pub/src/profile-check.ts +24 -0
  79. package/plugins/publishings/cnblogs-pub/src/selectors.ts +104 -0
  80. package/plugins/publishings/cnblogs-pub/tsconfig.json +4 -0
  81. package/plugins/publishings/csdn-pub/dist/index.js +21552 -0
  82. package/plugins/publishings/csdn-pub/dist/profile-check.js +20963 -0
  83. package/plugins/publishings/csdn-pub/package.json +19 -0
  84. package/plugins/publishings/csdn-pub/plugin.json +12 -0
  85. package/plugins/publishings/csdn-pub/src/index.ts +1178 -0
  86. package/plugins/publishings/csdn-pub/src/profile-check.ts +23 -0
  87. package/plugins/publishings/csdn-pub/tsconfig.json +4 -0
  88. package/plugins/publishings/emdash-pub/dist/index.js +158 -12
  89. package/plugins/publishings/emdash-pub/plugin.json +22 -1
  90. package/plugins/publishings/infoq-pub/dist/index.js +160 -12
  91. package/plugins/publishings/infoq-pub/dist/profile-check.js +160 -12
  92. package/plugins/publishings/infoq-pub/plugin.json +21 -0
  93. package/plugins/publishings/jike-pub/dist/index.js +21379 -0
  94. package/plugins/publishings/jike-pub/dist/profile-check.js +20951 -0
  95. package/plugins/publishings/jike-pub/package.json +19 -0
  96. package/plugins/publishings/jike-pub/plugin.json +12 -0
  97. package/plugins/publishings/jike-pub/src/index.ts +800 -0
  98. package/plugins/publishings/jike-pub/src/profile-check.ts +21 -0
  99. package/plugins/publishings/jike-pub/tsconfig.json +4 -0
  100. package/plugins/publishings/juejin-pub/dist/index.js +160 -12
  101. package/plugins/publishings/juejin-pub/dist/profile-check.js +160 -12
  102. package/plugins/publishings/juejin-pub/plugin.json +21 -0
  103. package/plugins/publishings/medium-pub/dist/index.js +158 -12
  104. package/plugins/publishings/medium-pub/plugin.json +22 -1
  105. package/plugins/publishings/podcast-pub/dist/index.js +158 -12
  106. package/plugins/publishings/qiita-pub/dist/index.js +158 -12
  107. package/plugins/publishings/qiita-pub/plugin.json +24 -1
  108. package/plugins/publishings/shipinhao-pub/dist/index.js +21715 -0
  109. package/plugins/publishings/shipinhao-pub/dist/profile-check.js +20431 -0
  110. package/plugins/publishings/shipinhao-pub/package.json +19 -0
  111. package/plugins/publishings/shipinhao-pub/plugin.json +12 -0
  112. package/plugins/publishings/shipinhao-pub/src/flow.ts +1323 -0
  113. package/plugins/publishings/shipinhao-pub/src/index.ts +173 -0
  114. package/plugins/publishings/shipinhao-pub/src/profile-check.ts +25 -0
  115. package/plugins/publishings/shipinhao-pub/src/selectors.ts +241 -0
  116. package/plugins/publishings/shipinhao-pub/tsconfig.json +4 -0
  117. package/plugins/publishings/sspai-pub/dist/index.js +21371 -0
  118. package/plugins/publishings/sspai-pub/dist/profile-check.js +20414 -0
  119. package/plugins/publishings/sspai-pub/package.json +19 -0
  120. package/plugins/publishings/sspai-pub/plugin.json +12 -0
  121. package/plugins/publishings/sspai-pub/src/flow.ts +624 -0
  122. package/plugins/publishings/sspai-pub/src/index.ts +167 -0
  123. package/plugins/publishings/sspai-pub/src/profile-check.ts +25 -0
  124. package/plugins/publishings/sspai-pub/src/selectors.ts +122 -0
  125. package/plugins/publishings/sspai-pub/tsconfig.json +4 -0
  126. package/plugins/publishings/substack-pub/dist/index.js +160 -12
  127. package/plugins/publishings/substack-pub/dist/profile-check.js +160 -12
  128. package/plugins/publishings/substack-pub/plugin.json +21 -0
  129. package/plugins/publishings/surfdash-pub/dist/index.js +158 -12
  130. package/plugins/publishings/surfdash-pub/plugin.json +22 -1
  131. package/plugins/publishings/threads-pub/dist/index.js +158 -12
  132. package/plugins/publishings/wechatmp-pub/dist/index.js +1669 -502
  133. package/plugins/publishings/wechatmp-pub/dist/profile-check.js +204 -21
  134. package/plugins/publishings/wechatmp-pub/plugin.json +32 -0
  135. package/plugins/publishings/wechatmp-pub/src/cgi-draft.ts +540 -0
  136. package/plugins/publishings/wechatmp-pub/src/cgi-media.ts +380 -0
  137. package/plugins/publishings/wechatmp-pub/src/cgi.ts +485 -0
  138. package/plugins/publishings/wechatmp-pub/src/flow.ts +104 -0
  139. package/plugins/publishings/weibo-pub/dist/index.js +160 -12
  140. package/plugins/publishings/weibo-pub/dist/profile-check.js +160 -12
  141. package/plugins/publishings/weibo-pub/plugin.json +35 -0
  142. package/plugins/publishings/x-pub/dist/index.js +160 -12
  143. package/plugins/publishings/x-pub/dist/profile-check.js +160 -12
  144. package/plugins/publishings/x-pub/plugin.json +40 -0
  145. package/plugins/publishings/xhs-pub/dist/index.js +160 -12
  146. package/plugins/publishings/xhs-pub/dist/profile-check.js +160 -12
  147. package/plugins/publishings/xhs-pub/plugin.json +37 -0
  148. package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +21491 -0
  149. package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +20417 -0
  150. package/plugins/publishings/xiaoyuzhou-pub/package.json +19 -0
  151. package/plugins/publishings/xiaoyuzhou-pub/plugin.json +12 -0
  152. package/plugins/publishings/xiaoyuzhou-pub/src/flow.ts +826 -0
  153. package/plugins/publishings/xiaoyuzhou-pub/src/index.ts +174 -0
  154. package/plugins/publishings/xiaoyuzhou-pub/src/profile-check.ts +26 -0
  155. package/plugins/publishings/xiaoyuzhou-pub/src/selectors.ts +125 -0
  156. package/plugins/publishings/xiaoyuzhou-pub/tsconfig.json +4 -0
  157. package/plugins/publishings/zenn-pub/dist/index.js +158 -12
  158. package/plugins/publishings/zenn-pub/plugin.json +22 -1
  159. package/plugins/generations/omni-voice-gen/voices/robin-news.wav +0 -0
@@ -11984,6 +11984,83 @@ var init_file_system_node = () => {};
11984
11984
  import { dirname as dirname2 } from "path";
11985
11985
  import { parseArgs } from "util";
11986
11986
 
11987
+ // ../../packages/kk-core/src/content-limits.ts
11988
+ var PLATFORM_CONTENT_LIMITS = Object.freeze({
11989
+ schemaVersion: 5,
11990
+ tagLimits: Object.freeze({
11991
+ default: 7,
11992
+ imageText: Object.freeze({
11993
+ instagram: 5,
11994
+ douyin: 5,
11995
+ kuaishou: 4,
11996
+ weixin: 1,
11997
+ xiaohongshu: 10
11998
+ }),
11999
+ shortVideo: Object.freeze({
12000
+ douyin: 5,
12001
+ kuaishou: 4,
12002
+ xiaohongshu: 10,
12003
+ bilibili: 10,
12004
+ zhihu: 5,
12005
+ tiktok: 20,
12006
+ youtube: 60
12007
+ })
12008
+ }),
12009
+ titleCapabilities: Object.freeze({
12010
+ dynamic: Object.freeze({
12011
+ bilibili: Object.freeze({ max: 20, unit: "utf16_code_units" }),
12012
+ zhihu: Object.freeze({ max: 50, unit: "utf16_code_units" }),
12013
+ douban: Object.freeze({ max: 70, unit: "utf16_code_units" }),
12014
+ x: null,
12015
+ threads: null,
12016
+ weibo: null,
12017
+ jike: null,
12018
+ zsxq: null,
12019
+ toutiao: null,
12020
+ juejin: null
12021
+ }),
12022
+ imageText: Object.freeze({
12023
+ xiaohongshu: Object.freeze({ max: 20, unit: "xiaohongshu_title_weighted" }),
12024
+ weixin: Object.freeze({ max: 20, unit: "utf16_code_units" }),
12025
+ douyin: Object.freeze({ max: 20, unit: "utf16_code_units" }),
12026
+ shipinhao: Object.freeze({ max: 22, unit: "utf16_code_units" }),
12027
+ reddit: Object.freeze({ max: 300, unit: "utf16_code_units" })
12028
+ }),
12029
+ article: Object.freeze({
12030
+ juejin: Object.freeze({ max: 100, unit: "utf16_code_units" }),
12031
+ csdn: Object.freeze({ max: 100, unit: "utf16_code_units" }),
12032
+ cnblogs: Object.freeze({ max: 128, unit: "utf16_code_units" }),
12033
+ weibo: Object.freeze({ max: 30, unit: "utf16_code_units" }),
12034
+ sspai: Object.freeze({ max: 80, unit: "utf16_code_units" })
12035
+ }),
12036
+ shortVideo: Object.freeze({
12037
+ shipinhao: Object.freeze({ max: 16, unit: "utf16_code_units" }),
12038
+ douyin: Object.freeze({ max: 30, unit: "utf16_code_units" }),
12039
+ bilibili: Object.freeze({ max: 80, unit: "utf16_code_units" })
12040
+ })
12041
+ }),
12042
+ bodyCapabilities: Object.freeze({
12043
+ dynamic: Object.freeze({
12044
+ threads: Object.freeze({ max: 500, unit: "unicode_code_points", behavior: "split_thread" }),
12045
+ x: Object.freeze({ max: 280, unit: "weighted", behavior: "split_thread" }),
12046
+ weibo: Object.freeze({ max: 2000, unit: "unicode_code_points" }),
12047
+ jike: Object.freeze({ max: 1000, unit: "unicode_code_points" })
12048
+ }),
12049
+ imageText: Object.freeze({
12050
+ xiaohongshu: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
12051
+ douyin: Object.freeze({ max: 1000, unit: "unicode_code_points" })
12052
+ }),
12053
+ article: Object.freeze({
12054
+ wechatmp: Object.freeze({ max: 50000, unit: "unicode_code_points" }),
12055
+ juejin: Object.freeze({ max: 50000, unit: "unicode_code_points" })
12056
+ }),
12057
+ shortVideo: Object.freeze({
12058
+ douyin: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
12059
+ kuaishou: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
12060
+ shipinhao: Object.freeze({ max: 1000, unit: "unicode_code_points" })
12061
+ })
12062
+ })
12063
+ });
11987
12064
  // ../../packages/kk-core/src/kinds.ts
11988
12065
  init_zod();
11989
12066
 
@@ -12074,6 +12151,67 @@ var BrowserSessionDescriptorSchema = object({
12074
12151
  }
12075
12152
  }).optional()
12076
12153
  }).strict();
12154
+ var ContentCountUnitSchema = _enum([
12155
+ "utf16_code_units",
12156
+ "unicode_code_points",
12157
+ "weighted",
12158
+ "xiaohongshu_title_weighted"
12159
+ ]);
12160
+ var LimitEntrySchema = object({
12161
+ max: number2().int().positive(),
12162
+ unit: ContentCountUnitSchema
12163
+ }).strict();
12164
+ var PublishingProfileLimitsSchema = object({
12165
+ title: LimitEntrySchema.optional(),
12166
+ body: LimitEntrySchema.optional()
12167
+ }).strict();
12168
+ var PublishingProfileRequiresSchema = object({
12169
+ cover: boolean2().optional(),
12170
+ orderedImages: boolean2().optional()
12171
+ }).strict();
12172
+ var PublishingProfileModeBindingSchema = object({
12173
+ metadata: record(string2(), unknown())
12174
+ }).strict();
12175
+ var PublishingProfileModesSchema = object({
12176
+ draft: PublishingProfileModeBindingSchema.optional(),
12177
+ live: PublishingProfileModeBindingSchema.optional()
12178
+ }).strict().superRefine((modes, ctx) => {
12179
+ if (modes.draft === undefined && modes.live === undefined) {
12180
+ ctx.addIssue({
12181
+ code: "custom",
12182
+ message: 'modes must define at least one of "draft" or "live"'
12183
+ });
12184
+ }
12185
+ });
12186
+ var PublishingProfileSchema = object({
12187
+ format: string2().min(1),
12188
+ locale: string2().regex(/^(source|[a-z]{2,3}(-[A-Za-z0-9]+)*)$/, {
12189
+ message: 'locale must be "source" or a valid language tag'
12190
+ }),
12191
+ limits: PublishingProfileLimitsSchema.optional(),
12192
+ requires: PublishingProfileRequiresSchema.optional(),
12193
+ modes: PublishingProfileModesSchema
12194
+ }).strict();
12195
+ var PublishingCapabilitiesSchema = object({
12196
+ defaultProfile: string2().min(1),
12197
+ profiles: record(string2().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/), PublishingProfileSchema)
12198
+ }).strict().superRefine((data, ctx) => {
12199
+ const keys = Object.keys(data.profiles);
12200
+ if (keys.length === 0) {
12201
+ ctx.addIssue({
12202
+ code: "custom",
12203
+ path: ["profiles"],
12204
+ message: "profiles record must be nonempty"
12205
+ });
12206
+ }
12207
+ if (!(data.defaultProfile in data.profiles)) {
12208
+ ctx.addIssue({
12209
+ code: "custom",
12210
+ path: ["defaultProfile"],
12211
+ message: `defaultProfile "${data.defaultProfile}" must reference a declared profile`
12212
+ });
12213
+ }
12214
+ });
12077
12215
  var PluginManifestSchemaBase = looseObject({
12078
12216
  name: string2().min(1),
12079
12217
  kind: PluginKindSchema,
@@ -12082,23 +12220,31 @@ var PluginManifestSchemaBase = looseObject({
12082
12220
  version: string2().optional(),
12083
12221
  description: string2().optional(),
12084
12222
  operations: record(string2(), PluginOperationSchema).optional(),
12085
- browserSession: BrowserSessionDescriptorSchema.optional()
12223
+ browserSession: BrowserSessionDescriptorSchema.optional(),
12224
+ publishing: PublishingCapabilitiesSchema.optional()
12086
12225
  });
12087
12226
  var PluginManifestSchema = PluginManifestSchemaBase.superRefine((manifest, ctx) => {
12088
- if (manifest.browserSession === undefined)
12089
- return;
12090
- if (manifest.kind !== "publish") {
12091
- ctx.addIssue({
12092
- code: "custom",
12093
- path: ["browserSession"],
12094
- message: `browserSession requires kind "publish", not "${manifest.kind}"`
12095
- });
12227
+ if (manifest.browserSession !== undefined) {
12228
+ if (manifest.kind !== "publish") {
12229
+ ctx.addIssue({
12230
+ code: "custom",
12231
+ path: ["browserSession"],
12232
+ message: `browserSession requires kind "publish", not "${manifest.kind}"`
12233
+ });
12234
+ }
12235
+ if (manifest.check === undefined) {
12236
+ ctx.addIssue({
12237
+ code: "custom",
12238
+ path: ["browserSession"],
12239
+ message: "browserSession requires a check probe entry that owns the session routines"
12240
+ });
12241
+ }
12096
12242
  }
12097
- if (manifest.check === undefined) {
12243
+ if (manifest.publishing !== undefined && manifest.kind !== "publish") {
12098
12244
  ctx.addIssue({
12099
12245
  code: "custom",
12100
- path: ["browserSession"],
12101
- message: "browserSession requires a check probe entry that owns the session routines"
12246
+ path: ["publishing"],
12247
+ message: `publishing requires kind "publish", not "${manifest.kind}"`
12102
12248
  });
12103
12249
  }
12104
12250
  });
@@ -12582,6 +12728,24 @@ function parseStyleContract(markdown) {
12582
12728
  }
12583
12729
  return StyleContractSchema.parse(parsed);
12584
12730
  }
12731
+ var REDUNDANT_INSIGHT_SPAN = 14;
12732
+ var REDUNDANT_INSIGHT_CLAUSE_LIMIT = 3;
12733
+ function redundantInsightClauses(insight, reference) {
12734
+ const ref = reference.replace(/[\s\p{P}\p{S}]+/gu, "");
12735
+ let count = 0;
12736
+ for (const clause of insight.split(/[\u3002\uFF01\uFF1F\uFF1B\uFF0C]/)) {
12737
+ const norm = clause.replace(/[\s\p{P}\p{S}]+/gu, "");
12738
+ if (norm.length < REDUNDANT_INSIGHT_SPAN)
12739
+ continue;
12740
+ for (let i = 0;i + REDUNDANT_INSIGHT_SPAN <= norm.length; i++) {
12741
+ if (ref.includes(norm.slice(i, i + REDUNDANT_INSIGHT_SPAN))) {
12742
+ count++;
12743
+ break;
12744
+ }
12745
+ }
12746
+ }
12747
+ return count;
12748
+ }
12585
12749
  function validateScriptPlan(plan, docs, style) {
12586
12750
  const parsed = ScriptPlanSchema.safeParse(plan);
12587
12751
  if (!parsed.success) {
@@ -12615,6 +12779,10 @@ function validateScriptPlan(plan, docs, style) {
12615
12779
  errors.push({ itemId: item.id, rule: "cap:lead" });
12616
12780
  if (item.insight.length > style.caps.insight)
12617
12781
  errors.push({ itemId: item.id, rule: "cap:insight" });
12782
+ const doc = docs.find((entry) => entry.id === item.id);
12783
+ if (doc !== undefined && redundantInsightClauses(item.insight, `${item.lead}${doc.body}`) >= REDUNDANT_INSIGHT_CLAUSE_LIMIT) {
12784
+ errors.push({ itemId: item.id, rule: "redundant-insight" });
12785
+ }
12618
12786
  }
12619
12787
  const fields = [
12620
12788
  { itemId: "episode", text: valid.intro },
@@ -104,9 +104,33 @@ export function parseStyleContract(markdown: string): StyleContract {
104
104
  * - `cap:<field>`: a field over its cap (`intro`/`outro` report `itemId: episode`)
105
105
  * - `banned:<phrase>`: a field containing a banned phrase
106
106
  * - `duplicate-insight`: the same insight text on two items in one episode
107
+ * - `redundant-insight`: the insight restates the narration — ≥ 3 of its clauses each share a
108
+ * ≥ 14-char verbatim span with the item's spoken facts (lead + doc body). 20261014 episode: the
109
+ * prompt told the planner to restate facts, so the listener heard near-identical wording twice
110
+ * per item. One repeated number is tolerable; three copied clauses is a second narration.
107
111
  *
108
112
  * All-or-nothing by contract (Q&A): a partially valid plan is never partially applied.
109
113
  */
114
+ /** Verbatim-span length (normalized) that counts a clause as restated, not as a fact mention. */
115
+ const REDUNDANT_INSIGHT_SPAN = 14;
116
+ /** A listener tolerates one restated clause; at this many the item reads as a second narration. */
117
+ const REDUNDANT_INSIGHT_CLAUSE_LIMIT = 3;
118
+
119
+ function redundantInsightClauses(insight: string, reference: string): number {
120
+ const ref = reference.replace(/[\s\p{P}\p{S}]+/gu, '');
121
+ let count = 0;
122
+ for (const clause of insight.split(/[。!?;,]/)) {
123
+ const norm = clause.replace(/[\s\p{P}\p{S}]+/gu, '');
124
+ if (norm.length < REDUNDANT_INSIGHT_SPAN) continue;
125
+ for (let i = 0; i + REDUNDANT_INSIGHT_SPAN <= norm.length; i++) {
126
+ if (ref.includes(norm.slice(i, i + REDUNDANT_INSIGHT_SPAN))) {
127
+ count++;
128
+ break;
129
+ }
130
+ }
131
+ }
132
+ return count;
133
+ }
110
134
  export function validateScriptPlan(plan: unknown, docs: Doc[], style: StyleContract): ScriptPlanValidation {
111
135
  const parsed = ScriptPlanSchema.safeParse(plan);
112
136
  if (!parsed.success) {
@@ -134,6 +158,14 @@ export function validateScriptPlan(plan: unknown, docs: Doc[], style: StyleContr
134
158
  for (const item of valid.items) {
135
159
  if (item.lead.length > style.caps.lead) errors.push({ itemId: item.id, rule: 'cap:lead' });
136
160
  if (item.insight.length > style.caps.insight) errors.push({ itemId: item.id, rule: 'cap:insight' });
161
+ const doc = docs.find((entry) => entry.id === item.id);
162
+ // unknown-id is already reported above; skip the redundancy check rather than double-report.
163
+ if (
164
+ doc !== undefined &&
165
+ redundantInsightClauses(item.insight, `${item.lead}${doc.body}`) >= REDUNDANT_INSIGHT_CLAUSE_LIMIT
166
+ ) {
167
+ errors.push({ itemId: item.id, rule: 'redundant-insight' });
168
+ }
137
169
  }
138
170
 
139
171
  const fields: { itemId: string; text: string }[] = [
@@ -11984,6 +11984,83 @@ var init_file_system_node = () => {};
11984
11984
  import { dirname as dirname2 } from "path";
11985
11985
  import { parseArgs } from "util";
11986
11986
 
11987
+ // ../../packages/kk-core/src/content-limits.ts
11988
+ var PLATFORM_CONTENT_LIMITS = Object.freeze({
11989
+ schemaVersion: 5,
11990
+ tagLimits: Object.freeze({
11991
+ default: 7,
11992
+ imageText: Object.freeze({
11993
+ instagram: 5,
11994
+ douyin: 5,
11995
+ kuaishou: 4,
11996
+ weixin: 1,
11997
+ xiaohongshu: 10
11998
+ }),
11999
+ shortVideo: Object.freeze({
12000
+ douyin: 5,
12001
+ kuaishou: 4,
12002
+ xiaohongshu: 10,
12003
+ bilibili: 10,
12004
+ zhihu: 5,
12005
+ tiktok: 20,
12006
+ youtube: 60
12007
+ })
12008
+ }),
12009
+ titleCapabilities: Object.freeze({
12010
+ dynamic: Object.freeze({
12011
+ bilibili: Object.freeze({ max: 20, unit: "utf16_code_units" }),
12012
+ zhihu: Object.freeze({ max: 50, unit: "utf16_code_units" }),
12013
+ douban: Object.freeze({ max: 70, unit: "utf16_code_units" }),
12014
+ x: null,
12015
+ threads: null,
12016
+ weibo: null,
12017
+ jike: null,
12018
+ zsxq: null,
12019
+ toutiao: null,
12020
+ juejin: null
12021
+ }),
12022
+ imageText: Object.freeze({
12023
+ xiaohongshu: Object.freeze({ max: 20, unit: "xiaohongshu_title_weighted" }),
12024
+ weixin: Object.freeze({ max: 20, unit: "utf16_code_units" }),
12025
+ douyin: Object.freeze({ max: 20, unit: "utf16_code_units" }),
12026
+ shipinhao: Object.freeze({ max: 22, unit: "utf16_code_units" }),
12027
+ reddit: Object.freeze({ max: 300, unit: "utf16_code_units" })
12028
+ }),
12029
+ article: Object.freeze({
12030
+ juejin: Object.freeze({ max: 100, unit: "utf16_code_units" }),
12031
+ csdn: Object.freeze({ max: 100, unit: "utf16_code_units" }),
12032
+ cnblogs: Object.freeze({ max: 128, unit: "utf16_code_units" }),
12033
+ weibo: Object.freeze({ max: 30, unit: "utf16_code_units" }),
12034
+ sspai: Object.freeze({ max: 80, unit: "utf16_code_units" })
12035
+ }),
12036
+ shortVideo: Object.freeze({
12037
+ shipinhao: Object.freeze({ max: 16, unit: "utf16_code_units" }),
12038
+ douyin: Object.freeze({ max: 30, unit: "utf16_code_units" }),
12039
+ bilibili: Object.freeze({ max: 80, unit: "utf16_code_units" })
12040
+ })
12041
+ }),
12042
+ bodyCapabilities: Object.freeze({
12043
+ dynamic: Object.freeze({
12044
+ threads: Object.freeze({ max: 500, unit: "unicode_code_points", behavior: "split_thread" }),
12045
+ x: Object.freeze({ max: 280, unit: "weighted", behavior: "split_thread" }),
12046
+ weibo: Object.freeze({ max: 2000, unit: "unicode_code_points" }),
12047
+ jike: Object.freeze({ max: 1000, unit: "unicode_code_points" })
12048
+ }),
12049
+ imageText: Object.freeze({
12050
+ xiaohongshu: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
12051
+ douyin: Object.freeze({ max: 1000, unit: "unicode_code_points" })
12052
+ }),
12053
+ article: Object.freeze({
12054
+ wechatmp: Object.freeze({ max: 50000, unit: "unicode_code_points" }),
12055
+ juejin: Object.freeze({ max: 50000, unit: "unicode_code_points" })
12056
+ }),
12057
+ shortVideo: Object.freeze({
12058
+ douyin: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
12059
+ kuaishou: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
12060
+ shipinhao: Object.freeze({ max: 1000, unit: "unicode_code_points" })
12061
+ })
12062
+ })
12063
+ });
11987
12064
  // ../../packages/kk-core/src/kinds.ts
11988
12065
  init_zod();
11989
12066
 
@@ -12074,6 +12151,67 @@ var BrowserSessionDescriptorSchema = object({
12074
12151
  }
12075
12152
  }).optional()
12076
12153
  }).strict();
12154
+ var ContentCountUnitSchema = _enum([
12155
+ "utf16_code_units",
12156
+ "unicode_code_points",
12157
+ "weighted",
12158
+ "xiaohongshu_title_weighted"
12159
+ ]);
12160
+ var LimitEntrySchema = object({
12161
+ max: number2().int().positive(),
12162
+ unit: ContentCountUnitSchema
12163
+ }).strict();
12164
+ var PublishingProfileLimitsSchema = object({
12165
+ title: LimitEntrySchema.optional(),
12166
+ body: LimitEntrySchema.optional()
12167
+ }).strict();
12168
+ var PublishingProfileRequiresSchema = object({
12169
+ cover: boolean2().optional(),
12170
+ orderedImages: boolean2().optional()
12171
+ }).strict();
12172
+ var PublishingProfileModeBindingSchema = object({
12173
+ metadata: record(string2(), unknown())
12174
+ }).strict();
12175
+ var PublishingProfileModesSchema = object({
12176
+ draft: PublishingProfileModeBindingSchema.optional(),
12177
+ live: PublishingProfileModeBindingSchema.optional()
12178
+ }).strict().superRefine((modes, ctx) => {
12179
+ if (modes.draft === undefined && modes.live === undefined) {
12180
+ ctx.addIssue({
12181
+ code: "custom",
12182
+ message: 'modes must define at least one of "draft" or "live"'
12183
+ });
12184
+ }
12185
+ });
12186
+ var PublishingProfileSchema = object({
12187
+ format: string2().min(1),
12188
+ locale: string2().regex(/^(source|[a-z]{2,3}(-[A-Za-z0-9]+)*)$/, {
12189
+ message: 'locale must be "source" or a valid language tag'
12190
+ }),
12191
+ limits: PublishingProfileLimitsSchema.optional(),
12192
+ requires: PublishingProfileRequiresSchema.optional(),
12193
+ modes: PublishingProfileModesSchema
12194
+ }).strict();
12195
+ var PublishingCapabilitiesSchema = object({
12196
+ defaultProfile: string2().min(1),
12197
+ profiles: record(string2().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/), PublishingProfileSchema)
12198
+ }).strict().superRefine((data, ctx) => {
12199
+ const keys = Object.keys(data.profiles);
12200
+ if (keys.length === 0) {
12201
+ ctx.addIssue({
12202
+ code: "custom",
12203
+ path: ["profiles"],
12204
+ message: "profiles record must be nonempty"
12205
+ });
12206
+ }
12207
+ if (!(data.defaultProfile in data.profiles)) {
12208
+ ctx.addIssue({
12209
+ code: "custom",
12210
+ path: ["defaultProfile"],
12211
+ message: `defaultProfile "${data.defaultProfile}" must reference a declared profile`
12212
+ });
12213
+ }
12214
+ });
12077
12215
  var PluginManifestSchemaBase = looseObject({
12078
12216
  name: string2().min(1),
12079
12217
  kind: PluginKindSchema,
@@ -12082,23 +12220,31 @@ var PluginManifestSchemaBase = looseObject({
12082
12220
  version: string2().optional(),
12083
12221
  description: string2().optional(),
12084
12222
  operations: record(string2(), PluginOperationSchema).optional(),
12085
- browserSession: BrowserSessionDescriptorSchema.optional()
12223
+ browserSession: BrowserSessionDescriptorSchema.optional(),
12224
+ publishing: PublishingCapabilitiesSchema.optional()
12086
12225
  });
12087
12226
  var PluginManifestSchema = PluginManifestSchemaBase.superRefine((manifest, ctx) => {
12088
- if (manifest.browserSession === undefined)
12089
- return;
12090
- if (manifest.kind !== "publish") {
12091
- ctx.addIssue({
12092
- code: "custom",
12093
- path: ["browserSession"],
12094
- message: `browserSession requires kind "publish", not "${manifest.kind}"`
12095
- });
12227
+ if (manifest.browserSession !== undefined) {
12228
+ if (manifest.kind !== "publish") {
12229
+ ctx.addIssue({
12230
+ code: "custom",
12231
+ path: ["browserSession"],
12232
+ message: `browserSession requires kind "publish", not "${manifest.kind}"`
12233
+ });
12234
+ }
12235
+ if (manifest.check === undefined) {
12236
+ ctx.addIssue({
12237
+ code: "custom",
12238
+ path: ["browserSession"],
12239
+ message: "browserSession requires a check probe entry that owns the session routines"
12240
+ });
12241
+ }
12096
12242
  }
12097
- if (manifest.check === undefined) {
12243
+ if (manifest.publishing !== undefined && manifest.kind !== "publish") {
12098
12244
  ctx.addIssue({
12099
12245
  code: "custom",
12100
- path: ["browserSession"],
12101
- message: "browserSession requires a check probe entry that owns the session routines"
12246
+ path: ["publishing"],
12247
+ message: `publishing requires kind "publish", not "${manifest.kind}"`
12102
12248
  });
12103
12249
  }
12104
12250
  });
@@ -30,9 +30,10 @@ history entry (`GET /history` 500s on the current build). Default origin: `http:
30
30
 
31
31
  Write that body to a scratch file, for example `$T/consent.wav`.
32
32
 
33
- The source clip is already in the repo and already 24 kHz mono 16-bit PCM, about 28 s:
33
+ The source clip is operator-private (voice assets never live in git, task 0159) and already 24 kHz
34
+ mono 16-bit PCM, about 28 s:
34
35
 
35
- `plugins/generations/omni-voice-gen/voices/robin-news.wav`
36
+ `~/.config/kk/voices/robin-news.wav`
36
37
 
37
38
  ## Convert a clip that is not already 24 kHz mono s16
38
39
 
@@ -56,7 +57,7 @@ then be synthesized with either TTS model.
56
57
  T=$(mktemp -d)
57
58
  # fetch the consent sample into "$T/consent.wav" (see above)
58
59
  GEMINI_API_KEY="$GEMINI_API_KEY" bun plugins/generations/gemini-tts/src/voice-create.ts \
59
- --source plugins/generations/omni-voice-gen/voices/robin-news.wav \
60
+ --source ~/.config/kk/voices/robin-news.wav \
60
61
  --consent "$T/consent.wav" \
61
62
  --name robin-news
62
63
  rm -rf "$T"