@lightcone-ai/daemon 0.15.68 → 0.15.70

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,21 @@
1
+ #!/usr/bin/env node
2
+ // Thin-proxy. Real impl on the lightcone server.
3
+ // Source of truth: src/mcp-services/weixin-tools.js
4
+ import { startThinProxy } from '../../_thin-proxy/forward.js';
5
+
6
+ await startThinProxy({
7
+ serverId: 'weixin-tools',
8
+ serverName: 'official-weixin-tools',
9
+ tools: [
10
+ {
11
+ name: 'weixin_login_start',
12
+ description: 'Start a WeChat (iLink bot) scan-login for this agent. Returns { qrImageUrl, expiresInSec, status, instructions } — post qrImageUrl to the user as an image, then poll weixin_login_status.',
13
+ inputSchema: {},
14
+ },
15
+ {
16
+ name: 'weixin_login_status',
17
+ description: 'Check the in-progress WeChat scan-login for this agent. Returns { status: pending|scaned|confirmed|expired|none, qrImageUrl, botId, botName }.',
18
+ inputSchema: {},
19
+ },
20
+ ],
21
+ });
@@ -0,0 +1,11 @@
1
+ {
2
+ "id": "weixin-tools",
3
+ "name": "Official Weixin Tools MCP",
4
+ "version": "0.1.0",
5
+ "runtime": "node",
6
+ "entrypoint": "index.js",
7
+ "tool_declarations": [
8
+ { "name": "weixin_login_start", "classification": "local" },
9
+ { "name": "weixin_login_status", "classification": "local" }
10
+ ]
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.15.68",
3
+ "version": "0.15.70",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/mcp-config.js CHANGED
@@ -83,12 +83,12 @@ const SERVER_BACKED_MCP_SERVERS = new Set([
83
83
  'portfolio-analysis',
84
84
  // thin-proxy MCP services migrated to the server (roadmap §4) — every one of
85
85
  // these uses startThinProxy() and requires the SERVER_URL/MACHINE_API_KEY/AGENT_ID triple.
86
- 'video-narration-planner',
87
86
  'page-understanding',
88
87
  'platform-policy-db',
89
88
  'keyword-research',
90
89
  'audience-research',
91
90
  'hook-pattern-library',
91
+ 'weixin-tools',
92
92
  ]);
93
93
 
94
94
  function baseEnvForServer(serverKey, { serverUrl, authToken, agentId, workspaceId, workspaceDir }) {
@@ -39,24 +39,32 @@ function deriveDurationMs(recorderOutput) {
39
39
  return lastTms > 0 ? lastTms : null;
40
40
  }
41
41
 
42
+ function planSegments(plan) {
43
+ if (!isPlainObject(plan)) return null;
44
+ for (const key of ['phases', 'sections', 'segments']) {
45
+ if (Array.isArray(plan[key]) && plan[key].length > 0) return plan[key];
46
+ }
47
+ return null;
48
+ }
49
+
42
50
  function derivePhaseCount({ plan, recorderOutput }) {
43
51
  const explicit = normalizeNumberOrNull(recorderOutput?.phases);
44
52
  if (explicit != null) return explicit;
45
53
 
46
- if (Array.isArray(plan?.phases)) return plan.phases.length;
47
- if (Array.isArray(plan?.sections)) return plan.sections.length;
48
- return null;
54
+ const segments = planSegments(plan);
55
+ return segments ? segments.length : null;
49
56
  }
50
57
 
51
- const PIPELINE_SENTINEL_KEY = 'detail_sections_version';
52
-
58
+ // record_url_narration is an atomic tool, not the tail of a fixed pipeline.
59
+ // The plan may be hand-written by the scripter or produced by plan_video_segments;
60
+ // it just needs a non-empty list of segments with per-segment visual action + duration
61
+ // so the recording stays in sync with the narration audio.
53
62
  function assertPipelineCompliance(plan) {
54
63
  if (!isPlainObject(plan)) return;
55
- if (!plan[PIPELINE_SENTINEL_KEY]) {
64
+ if (!planSegments(plan)) {
56
65
  throw new Error(
57
- 'pipeline_violation: plan must come from detail_sections output. '
58
- + 'Required pipeline: analyze_page plan_video detail_sections record_url_narration compose_video submit_to_library. '
59
- + 'Do not hand-write phases or bypass detail_sections.'
66
+ 'record_url_narration: `plan` must contain a non-empty `phases` (or `sections` / `segments`) array — '
67
+ + 'either hand-written or from plan_video_segments. Each entry should carry a visual action and a duration.'
60
68
  );
61
69
  }
62
70
  }
@@ -1,37 +0,0 @@
1
- #!/usr/bin/env node
2
- // Thin-proxy. Real impl on lightcone server.
3
- // Source of truth: src/mcp-services/video-narration-planner/
4
- import { z } from 'zod';
5
- import { startThinProxy } from '../../_thin-proxy/forward.js';
6
-
7
- await startThinProxy({
8
- serverId: 'video-narration-planner',
9
- serverName: 'official-video-narration-planner',
10
- tools: [
11
- {
12
- name: 'plan_video',
13
- description: 'Stage 2: plan narrative arc + phase plan for URL narration video. Enforces highlights<=3 and phases<=5.',
14
- inputSchema: {
15
- understanding: z.record(z.any()).describe('Stage 1 output page_understanding object.'),
16
- persona: z.string().optional(),
17
- target_platform: z.string(),
18
- total_duration_s: z.number().int().min(20).max(90).optional(),
19
- },
20
- },
21
- {
22
- name: 'detail_sections',
23
- description: 'Stage 3: take agent-written sentences, call TTS voiceover for each phase, and fill duration/dwell.',
24
- inputSchema: {
25
- strategy: z.record(z.any()),
26
- sentences: z.array(z.object({
27
- phase_id: z.string(),
28
- text: z.string(),
29
- })),
30
- workspace_id: z.string().optional(),
31
- credential_id: z.string().optional(),
32
- format: z.enum(['mp3', 'wav', 'flac']).optional(),
33
- strict_tts: z.boolean().optional(),
34
- },
35
- },
36
- ],
37
- });
@@ -1,44 +0,0 @@
1
- {
2
- "id": "video-narration-planner",
3
- "name": "Official Video Narration Planner MCP",
4
- "version": "0.1.0",
5
- "runtime": "node",
6
- "entrypoint": "index.js",
7
- "tool_declarations": [
8
- { "name": "plan_video", "classification": "cacheable" },
9
- { "name": "detail_sections", "classification": "mandatory" }
10
- ],
11
- "tool_block_rules": [
12
- {
13
- "workspace_id": "ae63cc9e-feff-4d7e-a62e-a7a7c5fd69d9",
14
- "agent_id": "91a45fd7-ce5f-4da6-9b27-e34bf7b7c0e2",
15
- "tools": ["plan_video"],
16
- "message": "plan_video blocked for editor_in_chief in CvMax. In this workspace, @short_video_scripter owns video planning."
17
- },
18
- {
19
- "workspace_id": "ae63cc9e-feff-4d7e-a62e-a7a7c5fd69d9",
20
- "agent_id": "91a45fd7-ce5f-4da6-9b27-e34bf7b7c0e2",
21
- "tools": ["detail_sections"],
22
- "message": "detail_sections blocked for editor_in_chief in CvMax. In this workspace, @short_video_scripter owns video planning."
23
- }
24
- ],
25
- "smoke_test": {
26
- "tool": "plan_video",
27
- "arguments": {
28
- "understanding": {
29
- "url": "https://example.com/job-detail",
30
- "core_message": "岗位职责与薪资透明,适合应届生快速判断是否投递",
31
- "visual_hotspots": [
32
- { "id": "hero", "y_range": [120, 320], "weight": 10, "text_excerpt": "岗位标题与薪资" },
33
- { "id": "requirements", "y_range": [780, 1080], "weight": 8, "text_excerpt": "学历与经验要求" },
34
- { "id": "apply", "y_range": [1420, 1660], "weight": 7, "text_excerpt": "投递入口" }
35
- ],
36
- "skip_zones": [
37
- { "y_range": [2300, 3200], "reason": "广告与推荐位" }
38
- ]
39
- },
40
- "persona": "校招求职学生",
41
- "target_platform": "douyin"
42
- }
43
- }
44
- }