demobites 1.29.0 → 1.30.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "demobites",
3
- "version": "1.29.0",
3
+ "version": "1.30.0",
4
4
  "description": "The DemoBites agentic recorder — you prompt, it films a real browser, and DemoBites turns the take into an editable demo bite.",
5
5
  "bin": {
6
6
  "demobites": "launcher/index.mjs"
@@ -45,8 +45,10 @@ const tb = man.timebase;
45
45
  // of it compounded two shifts (-0.71 then -1.67 on one take) and produced a
46
46
  // clock that matched nothing. Re-base first, then anchor once.
47
47
  if (tb && typeof man.record_from === "number") {
48
- tb.a = 1; tb.k = 1; tb.b = -man.record_from; tb.videoRecordFrom = man.record_from;
49
- tb.method = "identity (raw is wall-rate; see 2026-08-09 four-lane verification)";
48
+ // The base keeps the take's speed (trim.mjs 1.30.0: one uniform compression to fit the plan): video = (wall - record_from) / speed.
49
+ const speed = typeof man.speed === "number" && man.speed > 0 ? man.speed : 1;
50
+ tb.a = 1 / speed; tb.k = 1; tb.speed = speed; tb.b = -man.record_from / speed; tb.videoRecordFrom = man.record_from;
51
+ tb.method = speed === 1 ? "identity (raw is wall-rate; see 2026-08-09 four-lane verification)" : `identity, then one uniform ${speed}x compression to fit the plan`;
50
52
  }
51
53
  if (!tb || typeof tb.a !== "number" || typeof tb.b !== "number") {
52
54
  console.error("manifest.json has no timebase stamp. Run trim.mjs first.");
package/scripts/trim.mjs CHANGED
@@ -129,21 +129,44 @@ if (lastStepEnd > r0) {
129
129
  }
130
130
  }
131
131
  if (cutAt !== r0 - beaconDelta) console.log("beacon: corrected cut clamped at 0 — head shorter than the anchor error");
132
+
133
+ // THE TAKE FITS ITS PLAN (founder 2026-10-18: "it's no shame to run it via ffmpeg and make it 1.4 times faster… planned
134
+ // 30, took 46 — make it 33 or 34"). When the filmed story runs past the storyboard's own clock, the whole take is
135
+ // sped up by ONE factor (1.5 at most) so it lands near the plan, and the timebase carries that factor: every stamped
136
+ // moment (steps, cursor, shots) maps through video = (wall - record_from) / speed, so nothing drifts. Not a rate
137
+ // fit (the identity law stands): a known, deliberate, uniform compression, recorded in the manifest.
138
+ let speed = 1;
139
+ try {
140
+ const { estimateTake } = await import("../skill/scripts/clock.mjs");
141
+ const sb = JSON.parse(fs.readFileSync(path.join(dir, "storyboard.json"), "utf8"));
142
+ const planned = estimateTake(sb.steps);
143
+ const filmed = lastStepEnd > r0 ? lastStepEnd - r0 + TAIL_BEAT_S : 0;
144
+ const target = planned * 1.1;
145
+ if (planned > 5 && filmed > target * 1.03) {
146
+ speed = Math.min(1.5, Math.round((filmed / target) * 100) / 100);
147
+ console.log(`speed: planned ${planned.toFixed(1)}s, filmed ${filmed.toFixed(1)}s — the take is sped up ${speed}x to ${(filmed / speed).toFixed(1)}s`);
148
+ void postEvent("finishing", { text: `Fitting the take to its plan (${speed}x)…` });
149
+ } else console.log(`speed: planned ${planned.toFixed(1)}s, filmed ${filmed.toFixed(1)}s — as filmed`);
150
+ } catch (e) {
151
+ console.log("speed: as filmed (" + String(e?.message ?? e).split("\n")[0] + ")");
152
+ }
153
+ man.speed = speed;
132
154
  man.timebase = {
133
- a: 1,
134
- b: -r0,
135
- method: "identity (raw is wall-rate; see 2026-08-09 four-lane verification)" + beaconMethod,
155
+ a: 1 / speed,
156
+ b: -r0 / speed,
157
+ method: (speed === 1 ? "identity (raw is wall-rate; see 2026-08-09 four-lane verification)" : `identity, then one uniform ${speed}x compression to fit the plan`) + beaconMethod,
136
158
  k: 1,
159
+ speed,
137
160
  videoRecordFrom: r0,
138
161
  ...(beaconMethod ? { beaconDelta } : {}),
139
162
  };
140
163
  fs.writeFileSync(manPath, JSON.stringify(man, null, 2));
141
- console.log(`timebase: identity, video = wall - ${r0.toFixed(3)}${beaconMethod ? ` (cut at raw ${cutAt.toFixed(3)}s)` : ""}`);
164
+ console.log(`timebase: video = (wall - ${r0.toFixed(3)})${speed === 1 ? "" : ` / ${speed}`}${beaconMethod ? ` (cut at raw ${cutAt.toFixed(3)}s)` : ""}`);
142
165
 
143
166
  execFileSync(FFMPEG(), [
144
167
  "-y", "-loglevel", "error",
145
168
  "-i", raw,
146
- "-filter_complex", `[0:v]trim=${tailCut ? `start=${cutAt}:end=${tailCut}` : `start=${cutAt}`},setpts=PTS-STARTPTS,fps=30,format=yuv420p[out]`,
169
+ "-filter_complex", `[0:v]trim=${tailCut ? `start=${cutAt}:end=${tailCut}` : `start=${cutAt}`},setpts=(PTS-STARTPTS)/${speed},fps=30,format=yuv420p[out]`,
147
170
  "-map", "[out]",
148
171
  "-c:v", "libx264", "-preset", "medium", "-crf", "19",
149
172
  "-movflags", "+faststart",