@lightcone-ai/daemon 0.22.0 → 0.22.1

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.
@@ -267,9 +267,9 @@ server.tool(
267
267
 
268
268
  // ── compose_video_v2 (migrated from chat-bridge) ──────────────────────────
269
269
  // Tool-level enforcement of the standard chain: TTS-bearing segments require
270
- // plan_video_segments to have run earlier in this session. Without it manual
271
- // dwell/duration math has repeatedly produced misaligned subtitles, silent
272
- // tails, and re-records (Task #25/#26 trial).
270
+ // plan_video_segments to have run earlier in this session. Without it,
271
+ // hand-written dwell/duration math has repeatedly produced misaligned
272
+ // subtitles, silent tails, and full re-records in production runs.
273
273
  server.tool(
274
274
  'compose_video_v2',
275
275
  'Compose video(s) from a list of segments using ffmpeg. Each segment has a visual source (image / scroll / '
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.22.0",
3
+ "version": "0.22.1",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -329,6 +329,15 @@ function paramsForAtom(atomName, phase, fallbackFromY) {
329
329
  }
330
330
 
331
331
  // Phase fields → macro params. Macros validate their own inputs.
332
+ // Number(null) === 0 (a JS gotcha) — guard explicitly so a normalize step
333
+ // that fills missing fields with `null` (see normalizeSectionAsPhase) doesn't
334
+ // trick `Number.isFinite` into accepting a phantom zero.
335
+ function optionalFiniteNumber(value) {
336
+ if (value == null) return undefined;
337
+ const n = Number(value);
338
+ return Number.isFinite(n) ? n : undefined;
339
+ }
340
+
332
341
  function paramsForMacro(macroName, phase, fallbackFromY) {
333
342
  const durationMs = resolveDurationMs(phase, null);
334
343
  const targetY = resolveTargetY(phase, null);
@@ -337,8 +346,8 @@ function paramsForMacro(macroName, phase, fallbackFromY) {
337
346
  target_y: targetY,
338
347
  duration_ms: durationMs,
339
348
  from_y: fromY,
340
- transition_ms: Number.isFinite(Number(phase.transition_ms)) ? Number(phase.transition_ms) : undefined,
341
- transition_ratio: Number.isFinite(Number(phase.transition_ratio)) ? Number(phase.transition_ratio) : undefined,
349
+ transition_ms: optionalFiniteNumber(phase.transition_ms),
350
+ transition_ratio: optionalFiniteNumber(phase.transition_ratio),
342
351
  transition_curve: phase.transition_curve,
343
352
  amplitude_px: phase.amplitude_px,
344
353
  oscillate_period_ms: phase.oscillate_period_ms,
@@ -87,10 +87,11 @@ function assertPipelineCompliance(plan) {
87
87
  // would show 投递入口 / 二维码 / contact info, which violates the recruitment
88
88
  // content policy (see fragments.md frag.short.recruitment_url_mode_policy).
89
89
  //
90
- // Discovered after Task #25 v1 ended up dwelling on FunPlus's QR/投递 area:
91
- // the agent's plan declared target_y=2180 with dwell_ms=8500 without checking
92
- // what content lived at that pixel position. This is a prompt-level rule
93
- // that's been ignored often enough that we enforce it at the tool layer.
90
+ // Origin: in production runs the agent's plan repeatedly declared a target_y
91
+ // without checking what content lived at that pixel position, and ended up
92
+ // dwelling on QR codes / 投递 entries / 联系方式. The prompt-level rule
93
+ // requiring `target_y_content_label` has been ignored often enough that we
94
+ // enforce it at the tool layer instead.
94
95
  const FORBIDDEN_REGION_PATTERNS = [
95
96
  /二维码/, /扫码/, /扫一扫/,
96
97
  /投递入口/, /投递方式/, /投递通道/, /投递渠道/, /报名入口/, /报名方式/,
@@ -36,10 +36,10 @@ export const PART_RETRY_BASE_MS = 1_000; // 1s, 3s, 9s
36
36
  export const TERMINAL_JOB_TTL_MS = 7 * 24 * 3600 * 1000; // sweep done/dead_letter after 7 days
37
37
  export const HOUSEKEEPING_INTERVAL_MS = 6 * 3600 * 1000; // run housekeeping every 6h
38
38
  // Per-PUT timeout — Node's fetch has no overall request timeout. Without this
39
- // a stalled COS connection wedges the chunk loop forever (observed during the
40
- // first Task #25 upload: chunk 1 PUT hung 7+ minutes with no progress, no
41
- // error). 5 minutes covers slow networks for an 8MB chunk (~25kB/s floor)
42
- // while still letting failures surface to the chunk-level retry loop.
39
+ // a stalled COS connection wedges the chunk loop forever (observed in
40
+ // production: a chunk PUT hung 7+ minutes with no progress and no error).
41
+ // 5 minutes covers slow networks for an 8MB chunk (~25kB/s floor) while
42
+ // still letting failures surface to the chunk-level retry loop.
43
43
  export const PUT_REQUEST_TIMEOUT_MS = 5 * 60 * 1000;
44
44
 
45
45
  function nowIso() { return new Date().toISOString(); }