@gobing-ai/knowledge-kit 0.1.8 → 0.1.9

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 (30) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/board/collect/index.js +725 -1101
  3. package/dist/board/daily-voice/index.js +722 -101
  4. package/dist/board/itc/index.js +13 -0
  5. package/dist/index.js +1792 -102
  6. package/package.json +1 -1
  7. package/plugins/ingestions/horizon-ingest/dist/index.js +24 -1
  8. package/plugins/ingestions/horizon-ingest/src/index.ts +34 -0
  9. package/plugins/kk/config.example.yaml +8 -0
  10. package/plugins/kk/plugin.json +1 -1
  11. package/plugins/kk/publish-presets/daily-ai-voice.json +123 -0
  12. package/plugins/kk/schedules/kk-daily-publish3-cn-peak.cron.tmpl +20 -0
  13. package/plugins/kk/schedules/kk-daily-publish3-overseas.cron.tmpl +17 -0
  14. package/plugins/kk/scripts/daily-stages.ts +580 -78
  15. package/plugins/kk/scripts/kk-workflow-stages.ts +11 -1
  16. package/plugins/kk/scripts/topic-boundary.json +18 -83
  17. package/plugins/kk/workflows/kk-collect.yaml +2 -0
  18. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +75 -6
  19. package/plugins/kk/workflows/kk-daily-ai-voice3.yaml +842 -0
  20. package/plugins/kk/workflows/kk-daily-publish3.yaml +282 -0
  21. package/plugins/publishings/podcast-pub/README.md +65 -0
  22. package/plugins/publishings/podcast-pub/dist/index.js +1709 -274
  23. package/plugins/publishings/podcast-pub/plugin.json +1 -1
  24. package/plugins/publishings/podcast-pub/src/backfill.ts +465 -0
  25. package/plugins/publishings/podcast-pub/src/index.ts +211 -34
  26. package/plugins/publishings/podcast-pub/src/microfeed-client.ts +32 -3
  27. package/plugins/publishings/podcast-pub/src/seo.ts +1081 -0
  28. package/plugins/publishings/podcast-pub/src/show-notes.ts +71 -6
  29. package/plugins/publishings/surfdash-pub/dist/index.js +66 -5
  30. package/plugins/publishings/surfdash-pub/src/index.ts +94 -10
@@ -1426,7 +1426,12 @@ function dailyPatchSurfdash(args: string[]): void {
1426
1426
  const pod = targetOf(podcastResult);
1427
1427
  const postPath = (sd.metadata as Record<string, unknown> | undefined)?.postPath;
1428
1428
  if (typeof postPath !== 'string' || postPath === '') {
1429
- failWith(1, 'patch-surfdash: surfdash result carries no postPath');
1429
+ // A surfdash publish that never landed (2026-10-21: its `git push` was rejected with
1430
+ // `fetch first`) leaves no postPath to patch. `publish-classify` has already recorded the
1431
+ // run as `partial`, and failing here turned a published podcast episode into a `failed`
1432
+ // run — the folded tail must not escalate what the classifier already routed.
1433
+ console.error('patch-surfdash: surfdash result carries no postPath — nothing to patch, skip');
1434
+ return;
1430
1435
  }
1431
1436
  const resultPath = join(workDir, '31-publish', `${runDate}_23_patch-surfdash_sync-result.json`);
1432
1437
  if (process.env.KK_SURFDASH_PATCH_OFFLINE === '1') {
@@ -1742,6 +1747,11 @@ if (import.meta.main)
1742
1747
  dailyPublishWechat(rest);
1743
1748
  break;
1744
1749
  }
1750
+ case 'daily-bundle-variants': {
1751
+ const { dailyBundleVariants } = await loadDailyStages();
1752
+ dailyBundleVariants(rest);
1753
+ break;
1754
+ }
1745
1755
  case 'daily-publish-classify':
1746
1756
  dailyPublishClassify(rest);
1747
1757
  break;
@@ -1,92 +1,27 @@
1
1
  {
2
- "version": 1,
3
- "note": "Task 0189 R2 topic admission for the AI 乱炖 column, with the 2026-10-02 operator scope correction: the political-diplomatic rule is China-only (中国政治/外交主线). Foreign public-affairs items are NOT floor-excluded — they flow to the plan and are handled by the article-write/script-write/cover 选题边界 prompts (skip + note / plan `excluded`) and the content-safety review. Excluded items are recorded with id, rule, reason and the matched keyword. Sources are never deleted or modified. Scan covers the title plus the first scanChars chars of the body. Precision first: these keywords are a floor, not a sieve — a rare false negative is caught by the prompt-layer boundaries and the content-safety skill review, and a suspected false positive is restored by hand with a recorded reason (manual review), never silently.",
2
+ "version": 2,
4
3
  "scanChars": 200,
5
4
  "rules": [
6
5
  {
7
- "id": "political-diplomatic",
8
- "reason": "中国政治/外交主线:涉及中国党政机构、领导人、外交部门或台海/中美关系的条目,不属于 AI 乱炖的技术分享范围(0189 R2;2026-10-02 操作者裁定本规则仅限中国,外国政治条目由选题边界提示与内容安全评审处理)。",
6
+ "id": "prc-leadership",
7
+ "reason": "中国最高领导人敏感线:现任及历任国家最高领导人属平台审核红线,不论条目是否与 AI 相关一律排除(2026-10-22 操作者裁定,取代 0189 R2 的五条规则——诉讼、出口管制、监管博弈、军事技术、社会事件本身都是 AI 产业新闻的主线,整类排除过严)。",
9
8
  "keywords": [
10
- "领导人",
11
- "主席访",
9
+ "习近平",
10
+ "毛泽东",
11
+ "邓小平",
12
+ "江泽民",
13
+ "胡锦涛",
14
+ "华国锋",
15
+ "赵紫阳",
16
+ "xi jinping",
17
+ "jiang zemin",
18
+ "deng xiaoping",
19
+ "hu jintao",
20
+ "mao zedong",
21
+ "国家主席",
12
22
  "总书记",
13
- "国务院总理",
14
- "外交部",
15
- "大使馆",
16
- "国事访问",
17
- "政治局",
18
- "中南海",
19
- "台海",
20
- "两岸关系",
21
- "中美关系"
22
- ]
23
- },
24
- {
25
- "id": "trade-controls",
26
- "reason": "外交/贸易管制主线:制裁、关税、出口管制、实体清单等政府间管制动作属于公共事务,不是技术实践(0189 R2;涉管制的技术影响可在人工复审后改写为纯技术内容)。",
27
- "keywords": [
28
- "制裁",
29
- "关税",
30
- "出口管制",
31
- "实体清单",
32
- "贸易战",
33
- "禁令",
34
- "制裁清单",
35
- "sanctions",
36
- "tariff",
37
- "export control",
38
- "trade war",
39
- "entity list"
40
- ]
41
- },
42
- {
43
- "id": "military",
44
- "reason": "军事主线:军队、武器、作战相关的条目一律排除(0189 R2)。",
45
- "keywords": [
46
- "军队",
47
- "军事",
48
- "解放军",
49
- "武器",
50
- "导弹",
51
- "无人机袭击",
52
- "战场",
53
- "military",
54
- "pentagon",
55
- "missile"
56
- ]
57
- },
58
- {
59
- "id": "policy-dispute",
60
- "reason": "公共政策争议主线:立法、听证、反垄断等政策博弈是公共事务主线,不是技术内容(0189 R2;纯技术子话题需人工复审并重写后才可使用)。",
61
- "keywords": [
62
- "立法",
63
- "听证",
64
- "政策争议",
65
- "反垄断",
66
- "监管机构",
67
- "监管新规",
68
- "法案",
69
- "regulation",
70
- "antitrust",
71
- "legislation",
72
- "lawsuit"
73
- ]
74
- },
75
- {
76
- "id": "social-event",
77
- "reason": "社会公共事件主线:事故、灾害、冲突现场等社会事件的报道不属于技术分享(0189 R2)。",
78
- "keywords": [
79
- "社会事件",
80
- "踩踏",
81
- "骚乱",
82
- "抗议",
83
- "示威",
84
- "罢工",
85
- "遇难",
86
- "伤亡",
87
- "protest",
88
- "riot",
89
- "casualties"
23
+ "政治局常委",
24
+ "中南海"
90
25
  ]
91
26
  }
92
27
  ],
@@ -110,6 +110,8 @@ states:
110
110
  # the same 45m leash the daily workflow pins. Writes <work_dir>/10-ingest/<stamp>_02_collect_blended.json.
111
111
  # A single failed source degrades to an error entry in the merge (exit 0, surviving docs kept);
112
112
  # only an all-sources failure exits 1 and leaves the file unwritten.
113
+ # Empty 6h windows exit 0 with no digest; the plugin falls back to the newest digest ≤26h old
114
+ # (2026-10-23: Oct 21 contributed zero docs when every tick hit an empty window).
113
115
  command: >-
114
116
  kk executor fan-in
115
117
  --source "aihot-ingest:${vars.work_dir}/10-ingest/source.json"
@@ -91,9 +91,35 @@ vars:
91
91
  episode_min_minutes: "15"
92
92
  # 2026-10-09 operator decision: ingest bodies are spoken in full and today's pools render 22-46
93
93
  # minutes at the current item counts, so the 20-minute ceiling refused real episodes before any
94
- # TTS request. The pair drives BOTH the pre-spend estimate in `daily-generate` and the measured
95
- # gate in `daily-episode-duration`.
94
+ # TTS request. The pair drives the pre-spend estimates (`daily-qc-content`'s pool gate, and
95
+ # `daily-generate`'s script gate, which measures the authored VoiceScript) and the measured gate
96
+ # in `daily-episode-duration`.
96
97
  episode_max_minutes: "25"
98
+ # podcast-pub per-episode SEO (D9). Defaults match the plugin. Legacy publish output:
99
+ # podcast_seo=false, podcast_summary_paragraph=false, podcast_timeline_inline_style=before,
100
+ # podcast_auto_episode_number=false, podcast_link_field=url, podcast_slug=auto,
101
+ # podcast_round_duration=false. Title keywords stay in podcast-pub; ID3 keeps the date-only title.
102
+ podcast_seo: "true"
103
+ podcast_episode_title_mode: "as-is"
104
+ podcast_episode_title_pattern: "{series} · {date}:{keywords}"
105
+ podcast_series_name: "每日AI乱炖"
106
+ podcast_show_name: "咸话咸说"
107
+ podcast_title_max: "55"
108
+ podcast_title_max_keywords: "2"
109
+ podcast_seo_title_suffix: "|咸话咸说"
110
+ podcast_seo_title_max: "60"
111
+ podcast_seo_description_min: "120"
112
+ podcast_seo_description_max: "155"
113
+ podcast_seo_description_source: "plan"
114
+ podcast_summary_paragraph: "true"
115
+ podcast_timeline_inline_style: "after"
116
+ podcast_auto_episode_number: "true"
117
+ podcast_title_sanitize: "normalize"
118
+ podcast_link_field: "none"
119
+ podcast_slug: "pattern"
120
+ podcast_slug_pattern: "daily-ai-{date}"
121
+ podcast_social_image: "false"
122
+ podcast_round_duration: "true"
97
123
  # Run id, injected by spur at launch (0674 R5/R8) — the selections / ledger / mark-published
98
124
  # identity. A shell command may only reference declared vars.
99
125
  __runId: ""
@@ -200,7 +226,8 @@ nodes:
200
226
  store's `selections` ledger, written by the `select` node below). Writes
201
227
  <work_dir>/10-collect/pool.json (the fan-in blended.json shape) and prints {count}. An empty
202
228
  pool writes [] and exits 0 — the run's no-content path, not a failure. The `daily-extract`
203
- stage widens the window (×2, ×3, capped at 72h) only when the configured window's pool cannot
229
+ stage widens the window (×2, ×3, ×7, capped at 168h — extended 2026-10-23 when the 72h
230
+ ceiling closed a supply-collapsed day as no-content) only when the configured window's pool cannot
204
231
  reach `episode_min_minutes` (2026-10-19): a published episode consumes more stories than a day
205
232
  of collection supplies, so the tight window alone closed 2026-10-18/19 as no-content while
206
233
  older unselected items sat in the store.
@@ -580,7 +607,7 @@ nodes:
580
607
  options:
581
608
  onError: continue
582
609
  command: >-
583
- OMNIVOICE_SPEED=1.06 kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "${vars.vdriver}" "1" "${vars.episode_max_minutes}"
610
+ OMNIVOICE_SPEED=1.06 kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "${vars.vdriver}" "1" "${vars.episode_max_minutes}" "${vars.episode_min_minutes}"
584
611
 
585
612
  # Driver fallback chain (2026-10-15, operator decision): voxcpm -> gemini -> omni. Each sibling
586
613
  # runs the same stage with a pinned driver; its condition re-checks the pool AND that NO upstream
@@ -601,7 +628,7 @@ nodes:
601
628
  options:
602
629
  onError: continue
603
630
  command: >-
604
- kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "gemini" "1" "${vars.episode_max_minutes}"
631
+ kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "gemini" "1" "${vars.episode_max_minutes}" "${vars.episode_min_minutes}"
605
632
 
606
633
  - id: generate-fallback2
607
634
  description: "Voice tier fallback 2 (last, loud): omni-voice-gen when neither voxcpm nor gemini delivered"
@@ -616,7 +643,7 @@ nodes:
616
643
  kind: shell
617
644
  options:
618
645
  command: >-
619
- OMNIVOICE_SPEED=1.06 kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "ominivoice" "1" "${vars.episode_max_minutes}"
646
+ OMNIVOICE_SPEED=1.06 kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "ominivoice" "1" "${vars.episode_max_minutes}" "${vars.episode_min_minutes}"
620
647
 
621
648
  - id: intro
622
649
  description: "Prepend the 8s intro sting (intro_music var; empty = skip), update duration/audioPath"
@@ -718,6 +745,27 @@ nodes:
718
745
  kind: shell
719
746
  options:
720
747
  command: >-
748
+ PODCAST_PUB_SEO="${vars.podcast_seo}"
749
+ PODCAST_PUB_EPISODE_TITLE_MODE="${vars.podcast_episode_title_mode}"
750
+ PODCAST_PUB_EPISODE_TITLE_PATTERN="${vars.podcast_episode_title_pattern}"
751
+ PODCAST_PUB_SERIES_NAME="${vars.podcast_series_name}"
752
+ PODCAST_PUB_SHOW_NAME="${vars.podcast_show_name}"
753
+ PODCAST_PUB_TITLE_MAX="${vars.podcast_title_max}"
754
+ PODCAST_PUB_TITLE_MAX_KEYWORDS="${vars.podcast_title_max_keywords}"
755
+ PODCAST_PUB_SEO_TITLE_SUFFIX="${vars.podcast_seo_title_suffix}"
756
+ PODCAST_PUB_SEO_TITLE_MAX="${vars.podcast_seo_title_max}"
757
+ PODCAST_PUB_SEO_DESCRIPTION_MIN="${vars.podcast_seo_description_min}"
758
+ PODCAST_PUB_SEO_DESCRIPTION_MAX="${vars.podcast_seo_description_max}"
759
+ PODCAST_PUB_SEO_DESCRIPTION_SOURCE="${vars.podcast_seo_description_source}"
760
+ PODCAST_PUB_SUMMARY_PARAGRAPH="${vars.podcast_summary_paragraph}"
761
+ PODCAST_PUB_TIMELINE_INLINE_STYLE="${vars.podcast_timeline_inline_style}"
762
+ PODCAST_PUB_AUTO_EPISODE_NUMBER="${vars.podcast_auto_episode_number}"
763
+ PODCAST_PUB_TITLE_SANITIZE="${vars.podcast_title_sanitize}"
764
+ PODCAST_PUB_LINK_FIELD="${vars.podcast_link_field}"
765
+ PODCAST_PUB_SLUG="${vars.podcast_slug}"
766
+ PODCAST_PUB_SLUG_PATTERN="${vars.podcast_slug_pattern}"
767
+ PODCAST_PUB_SOCIAL_IMAGE="${vars.podcast_social_image}"
768
+ PODCAST_PUB_ROUND_DURATION="${vars.podcast_round_duration}"
721
769
  kk stage daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
722
770
 
723
771
  - id: publish-podcast
@@ -731,6 +779,27 @@ nodes:
731
779
  kind: shell
732
780
  options:
733
781
  command: >-
782
+ PODCAST_PUB_SEO="${vars.podcast_seo}"
783
+ PODCAST_PUB_EPISODE_TITLE_MODE="${vars.podcast_episode_title_mode}"
784
+ PODCAST_PUB_EPISODE_TITLE_PATTERN="${vars.podcast_episode_title_pattern}"
785
+ PODCAST_PUB_SERIES_NAME="${vars.podcast_series_name}"
786
+ PODCAST_PUB_SHOW_NAME="${vars.podcast_show_name}"
787
+ PODCAST_PUB_TITLE_MAX="${vars.podcast_title_max}"
788
+ PODCAST_PUB_TITLE_MAX_KEYWORDS="${vars.podcast_title_max_keywords}"
789
+ PODCAST_PUB_SEO_TITLE_SUFFIX="${vars.podcast_seo_title_suffix}"
790
+ PODCAST_PUB_SEO_TITLE_MAX="${vars.podcast_seo_title_max}"
791
+ PODCAST_PUB_SEO_DESCRIPTION_MIN="${vars.podcast_seo_description_min}"
792
+ PODCAST_PUB_SEO_DESCRIPTION_MAX="${vars.podcast_seo_description_max}"
793
+ PODCAST_PUB_SEO_DESCRIPTION_SOURCE="${vars.podcast_seo_description_source}"
794
+ PODCAST_PUB_SUMMARY_PARAGRAPH="${vars.podcast_summary_paragraph}"
795
+ PODCAST_PUB_TIMELINE_INLINE_STYLE="${vars.podcast_timeline_inline_style}"
796
+ PODCAST_PUB_AUTO_EPISODE_NUMBER="${vars.podcast_auto_episode_number}"
797
+ PODCAST_PUB_TITLE_SANITIZE="${vars.podcast_title_sanitize}"
798
+ PODCAST_PUB_LINK_FIELD="${vars.podcast_link_field}"
799
+ PODCAST_PUB_SLUG="${vars.podcast_slug}"
800
+ PODCAST_PUB_SLUG_PATTERN="${vars.podcast_slug_pattern}"
801
+ PODCAST_PUB_SOCIAL_IMAGE="${vars.podcast_social_image}"
802
+ PODCAST_PUB_ROUND_DURATION="${vars.podcast_round_duration}"
734
803
  kk stage daily-publish-podcast "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
735
804
 
736
805
  # ---- independent soft channels: xhs, wechat, locales (exit 0 on soft failure; fail-fast DAG) ----