6705 Commits
Author SHA1 Message Date
WaterrrForever 12ce8c2ffd chore: release v0.9.9 (#7095) v0.9.9 2026-11-03 11:28:41 -05:00
Miguel Ángel 1071ff35a7 docs: point the Discord links at the new community server (#7097) 2026-11-03 10:44:08 -05:00
Miguel Ángel 67d11c0a5c fix(engine): a script the page cancelled is not a failed load (#7093) 2026-11-03 15:00:39 +00:00
Miguel Ángel fc006f713b fix(studio-server): a render that ran over 5 minutes can still be read after it finishes (#7087)
* fix(studio-server): a render that ran over 5 minutes can still be read after it finishes

* fix(studio-server): keep the finished-jobs helper's reasoning in its names
2026-11-03 14:22:36 +00:00
Miguel Ángel 4ceec0171f perf(studio-server): preview steps skip the page parse when a tag scan answers (#7092)
* test(studio-server): a stamped preview build parses its page once

* perf(studio-server): preview steps skip the page parse when a tag scan answers
2026-11-03 14:16:22 +00:00
Miguel Ángel dec465949b feat(engine): a script-load failure names where the script came from and why it failed (#7088)
* feat(engine): a script-load failure names where the script came from and why it failed

* fix(engine): keep the script-load cause helpers private and within the engine size budget
2026-11-03 13:54:20 +00:00
Wenbo ZhuandClaude Opus 5.5 b2acef5869 fix(cli): no auto-update across 0.x minors; install skills at the CLI's release tag (#7013)
* fix(cli): no auto-update across 0.x minors; install skills at the CLI's release tag

- autoUpdate: before 1.0 a minor bump counts as a major one, so 0.8.x -> 0.9.x
  is a notice, not a silent background install. From 1.0 on, the major alone.
- skills: `skills update` / bare `skills` / `init` install from
  heygen-com/hyperframes#v<VERSION>, and `skills check` plus the background
  freshness nudge read the manifest at the same tag. A build the repo has no
  tag for (dev build) falls back to main, as before.
- skills: run the upstream installer as `npx skills@1.7.4` instead of an
  unpinned `npx skills`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): keep the comment share of the touched files from rising

Comment ratchet: trim the comments added in the previous commit. No code change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): pass the CLI version into the skills check instead of importing it

scripts/clawhub-unlist-retired.mjs loads skillsManifest.ts from source under
plain node, which cannot resolve a relative `../version.js` import. Callers now
pass `cliVersion` (and skillsSourceRef takes it as an argument), so the module
keeps importing only node builtins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): read skills at the CLI's tag without a probe; pin skills@1.5.13

- A release build reads the manifest at its own tag directly (publish pushes
  the tag before npm publish). Only a 404 for that manifest falls back to main;
  a timeout, missing git or proxy error now surfaces instead of quietly
  comparing against main, so the background nudge can't cache a false
  "outdated" count.
- checkSkills returns the ref it read, and skills update installs from that
  same ref, so check and install can't disagree within one run.
- Pin skills@1.5.13 (it parses `#<ref>` too) so the pin matches the version
  agentDirs.generated.ts and the lock-path notes were checked against.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(cli): split the tag and main manifest fetches

Fallow flagged fetchRemoteManifest's CRAP score (31.6 > 30). The tag fetch
(404 is the only fallback to main) and the SHA-pinned main fetch are now their
own functions. No behaviour change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): never auto-upgrade a Homebrew install

`brew upgrade hyperframes` installs whatever formula the tap holds, so a
background run scheduled for an allowed patch could still cross a 0.x minor.
Homebrew installs now only get the update notice naming `brew upgrade`.
A new test drives the scheduler through the real installer detection and
installer script: no spawn on Homebrew, a versioned npm install for a patch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 13:21:39 +00:00
Miguel Ángel 35753f3821 perf(parsers): ensureHfIds skips the parse when every element already has its id (#7069)
* test(parsers): a stamped document needs no parse to keep its ids

* perf(parsers): ensureHfIds skips the parse when every element already has its id

* fix(parsers): a fragment checks every tag, a stray body tag included

* fix(parsers): keep the parse for a page not opening head then body

* refactor(parsers): split the head check out of the page shape check

* fix(parsers): find the head end tag without stripping comments
2026-11-03 13:16:44 +00:00
Miguel Ángel 47dddedd6c fix(core): Safari audio frozen after its cut is pulled onto the playhead at 0.5 s behind, not 1 s (#7082)
* fix(core): Safari audio that sits frozen after its cut is pulled onto the playhead in 0.4 s, not 1 s

* fix(core): shorten the frozen-audio comment

* fix(core): leave a WebKit audio element that is still filling data out of the frozen rule
2026-11-03 11:17:40 +00:00
WaterrrForeverandClaude Opus 5.5 2daedcdafc feat(skills): style templates on the HyperFrames plan (#6842)
* fix(cli,skills): say a step's credits are its total, and keep cost out of retries

An out-of-credits refusal named what the step needs, and an agent read
that as the shortfall. It now says the number is the step's total.

After its last try, an agent told the person what was spent and what
another try would cost. The HeyGen reference now says not to.

* fix(cli): plan avatar credits from the HyperFrames price rows

An avatar's credits came from its old API price, about five times what
the plan charges, so --max-credits refused calls that fit and a top-up
asked for too much. They now use the Avatar IV photo row plus the
general avatar video row, 950 credits a minute.

* fix(cli): pin the avatar price test and keep pricing under the comment share

* fix(cli): plan avatar seconds on the Avatar IV row alone

The general avatar row is only checked at admission and billed on
seconds left after Avatar IV, so adding it to every second made a 15 s
script plan at 238 instead of 200 and a 220 limit refuse a call that fits.

* feat(skills): creator templates on the HyperFrames plan

A Pro account asks for a video in a creator's format and the agent pulls that
template on demand. The packs sit in the private bucket behind the plan-gated
bundle route, so a Free account gets a 402 and the upgrade step. A pull costs
no credits; making the film spends them as Reverse does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(skills): make creator-remix the one template skill

SKILL.md holds the catalog, the Pro openings, the pull and the credit
rules. references/remix.md is how to make the episode, from Desktop's
creator-remix with only what the app alone has swapped for the CLI.
The packs now hold only the template, so their hashes change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(skills): the creator-remix skill and its packs

SKILL.md holds the catalog, the Pro openings, the pull and the credit
rules. references/remix.md is how to make the episode, from Desktop's
creator-remix with only what the app alone has swapped for the CLI.
The packs now hold only the template, so their hashes change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(skills): word creator-remix's sign-in steps the way media-use does

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(skills): a creator-remix presenter runs on the plan by default

The person's own HeyGen avatar is recommended, not required: without
one, 小Lin's presenter is made with HeyGen Video 1 from their photo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): pin template packs whose voice bills the plan first

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): offload media named inside template tags

References in a composition's <template> were never rewritten, because the DOM
query stops at the tag and template.content does not serialize, so the file stayed
in the zip. A stem matched as a plain word and a same-named file elsewhere counted
as a mention too. A 5-minute presented film went from 196 MB to 71 MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): lipsync, and avatars that speak a finished voice

Video 1 takes audio as a reference and re-paces it, so a shot's lips follow its
own track and drift up to a second from the voice. generate --type lipsync redoes
the mouth to the exact voice. generate --type avatar takes --audio, and --avatar
for the person's own look, so the lips land on the voice from one job.

Also moves the plan rows to the 2026-10-30 price book, estimates a voice's credits
so --max-credits can hold it, logs jobs from a folder with no project, and describes
the resolve flags.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(skills): presenters go avatar first, and a voice can be stock

The person's own avatar, then Avatar IV from their photo, then Video 1 with
lipsync. With no clone or recording, a stock voice named in the credits. Pins the
five rebuilt packs. The archive test keeps its ../ under GNU tar with -P.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cli): fix avatar price and sfx provider tests, trim comments

A wav gives the avatar price test an exact length on every platform. The
sfx tests now expect HeyGen first. Comment share is back under the base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(core): read a font shorthand whose size nests parentheses

`font: calc(var(--size) * 1px)/1 "X"` cut at the first closing parenthesis, so a
bad family name failed the render.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): one ledger row per job, and search skips a template's own pictures

A job done at once logs one row, transcribe logs to the project, and resolve no
longer offers a component's file for a search. --reuse and --provider get help text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(skills): pin the tested template packs, and a board needs no voice

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cli): read the transcribe ledger row as JSON, for Windows paths

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(cli): pin the Big Questions pack without the film length check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(skills): call them style templates, and the docs example pulls a real slug

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(cli): pin the cleaned template packs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(skills): drop the stock-presenter ban and the no-spend-before-storyboard rule

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(cli): pin the Paper Trail and Long Story Short packs without the stock-presenter ban

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(skills): rename creator-remix to style-remix, and the five templates to names that say the look

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(skills): style-remix lists the five templates when none is named

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(cli): pin the Finance Explainer pack that varies its motion graphics

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): media-use generate --type cutout takes a shot out of its background on the plan

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(cli): pin the Finance Explainer pack without the provisional variety rule

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(cli): pin the five style packs with variety rules measured on real films

Animated Science gets a worlds-and-boards budget, 3D True Crime a main board and a
brief corner note, 3D Brand caps by measured rate, Finance and Collage set pieces and
more than one look.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(skills): media-use names the on-plan cutout and lipsync commands, not the heygen CLI ones

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): cutout retries dedupe on request_id, uploads carry their real type; style-remix is public

The background-removal route dedupes on the body's request_id and ignores the Idempotency-Key
header, so a retry after a lost answer started a second job: runJob now also sends the key in the
body for such a route. Uploads typed every non-MP4 file as audio, so WebM and MOV inputs were
refused: they now take their type from the extension. Fails without the fix: the new cutout test
in generate.test.ts sees audio/mpeg for WebM and MOV.

style-remix drops metadata.internal and is listed with the other published skills.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(cli): keep the comment share of the touched files at main's

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: count style-remix in the README and CLAUDE.md skill lists

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(skills): rename style-remix to style-sampling

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 11:07:02 +00:00
Jerrai 3f42dc1c73 feat(cli): diff --offset compares the original at t with yours at t + offset (#7057)
* feat(cli): diff --offset compares the original at t with yours at t + offset

Every per-frame measure (SSIM per frame and per cell, frames.tsv, cuts,
missing content) reads yours offset seconds later, and a project folder is
captured at the shifted times. The window keeps to where both sides have
frames and says so in warnings; an offset with no overlap, or one that is
not a number, is refused with a fix. Sound keeps its own offset reading,
duration is reported as not measured, and the result echoes offset.

* refactor(cli): trim diff offset comments under the file's comment share

* fix(cli): rebase full-length diff offsets and reject overflow

Keep offset streams on a zero-based clock even when every frame is selected. Cover an untrimmed delayed render and reject non-finite offsets before file or project processing.

— Jerrai

* refactor(cli): remove redundant offset filter comment

— Jerrai
2026-11-03 10:48:47 +00:00
WaterrrForever 7ae108005a fix(cli): measure cuts reads a zoom that speeds up as a move, not a cut (#7075)
* fix(cli): measure cuts reads a zoom that speeds up as a move, not a cut

Bug bash (Stripe "pay", 0.9.6): the app window's zoom-in at 0.2 s changes
3, 9, 12, 5 grey levels a frame, and the 12 crossed a cut's level, so
measure cuts listed a cut there and diff failed the rebuild (pass: false at
0.93 similarity) for missing it.

A single-frame cut is now a fast move when the frames either side move too
(30% of its change) and the frame before, zoomed 0.8-1.25x and shifted a few
pixels, comes within 40% of its change of the frame after. A punch-in also
zooms, but out of a still picture, so it stays a cut.

* fix(cli): a zoom is a move only when the picture moves on both sides

Review (hf-review): motion on one side hid a real punch-in out of a still
picture next to a moving shot, and the mirrored punch-out. isMove now needs
both neighbours moving (min, not max). The 33-film check now changes only
Stripe's 0.2 s zoom; league-promo 95.93 s and M1000 5.43 s are cuts again.
2026-11-03 10:20:03 +00:00
WaterrrForever e9de593fe9 fix(cli): measure picks more than 100 frames on ffmpeg 8 (#7071)
* fix(cli): measure picks more than 100 frames on ffmpeg 8

ffmpeg 8 refuses a flat sum of more than 100 terms ("Error while parsing
expression"), and every frame pick in measure, diff and snapshot was one flat
sum of eq(n,k). The ffmpeg HyperFrames downloads is 8.1.2, so measure frames,
diff, face, person, drawn boxes and snapshot all failed past 100 frames.
PostHog: every such cli_error since 0.9.3 came from Windows agents on ffmpeg 8.

sumOf keeps a sum of up to 100 terms flat and nests a longer one in halves.

* fix(cli): sums nest from 51 terms, so ffmpeg 8 takes every pick count

Review (hf-review): 100-term leaves still failed on ffmpeg 8.1.2 once nested,
at 199-201 eq() picks, and snapshot's deeper terms failed at 97-100 times.
ffmpeg 8 limits how deep an expression nests, and a flat sum counts a level
a term. Leaves of 50 pass every count from 1 to 1000 in both shapes, and
1500-5000. Tests decode 199/200/237/401 picks and 97/100/199 snapshot times.
2026-11-03 10:14:37 +00:00
James RussoandClaude Opus 5.5 d89bf35b1d fix(core): keep overflowing content and SVG clip paths in motion-blurred frames (#7080)
* fix(core): keep overflowing content and SVG clip paths in motion-blurred frames

A blurred mover was copied only within its own layout box, so text wider
than the box (a one-line wordmark in a fixed-width container) was cut in
blurred frames. The movers-alone capture also hid every non-mover, including
the SVG clipPath a mover references, which empties the clip and makes the
mover vanish. Both showed as flicker during playback and in full renders.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(core): keep the motion-blur fix within each file's comment share

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(core): measure overflowing text with a Range, ignore rects inside the mover

A block child is as wide as its mover however long its no-wrap text, so
reading child boxes missed the common overflow layout. Read the text's own
rects through a Range. On a rotated mover each child's upright client rect
covers the mover's corners, which doubled the box at 45 degrees and could
pull a neighbouring mover into the trail; only rects that leave the
mover's own client rect now grow it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(core): measure a rotated mover's overflow unrotated

The upright client rect of a rotated mover has empty corners, so overflow
that landed there passed the "stays inside" check and was still cropped.
Measure a rotated mover with its own transform off and map the rects back
through its layout pose, then restore its style.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(core): keep a rotated mover the containing block while measured

With only transform: none, a mover with no position stops being the
containing block of its absolute and fixed children, so they jump to an
outer ancestor while measured and their real overflow is cropped. Add
will-change: transform during the swap, and skip the swap when the mover
has no transform of its own.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-03 09:59:56 +00:00
WaterrrForever 91e7f4d48f fix(cli): measure and diff tell an agent what to type after a wrong call (#7072)
The top measure and diff errors since 0.9.4 (PostHog, CLI project) were
agents' calls, not bad videos:

- A flag from another command: --from/--to on ops that take --start/--end
  (extract is the other way round), -o for --out. The unknown-flag error now
  names the flag that means the same here.
- measure sync without --roi stopped at citty's bare "Missing required
  argument"; diff with one video at "Missing required positional argument:
  YOURS". Both now reach their own message with the command to run.
- A Windows path whose backslashes bash dropped (D:videoclip.mp4) was only
  "no file at". The fix line now says to quote it or write it with /.
2026-11-03 09:59:39 +00:00
WaterrrForever 95a86a19cc docs(skills): reverse checks the plan before downloading a linked video (#7076)
Bug bash (Miguel's #2): given a link, the agent downloaded the video before
reverse start could tell it the account was Free. The skill now runs auth
status first. To stay in budget, the no-reverse-start fallback lives only in
start.md (it was in both), and two lines are shorter.
2026-11-03 09:55:43 +00:00
Miguel Ángel ce9830fece fix(studio-server): a project's signature no longer freezes the host while it walks the files (#7073)
* fix(studio-server): a project's signature no longer freezes the host while it walks the files

* style(studio-server): format the signature changes

* fix(studio-server): keep the adapter for the preview's early-error script
2026-11-03 09:16:40 +00:00
Miguel Ángel 9ceced05e2 chore: release v0.9.8 (#7077) v0.9.8 2026-11-03 09:15:54 +00:00
James RussoandClaude Opus 5.5 10bc426e32 feat(cli): call HeyGen's v3 media routes through the generated client (#7063)
* feat(cli): call HeyGen's v3 media routes through the generated client

The media-use generate routes (image, video, avatar, voice, music), HeyGen
transcription and the public voice list called fetch by hand. They now go
through the generated cloud client, regenerated with the create and status
methods for those routes.

runJob takes a job route (its path, how it starts, how its status is read)
in place of a path and a poll-path function. Keys, retries, timeouts,
polling and error messages are unchanged. Speech answers in the request and
the API takes no Idempotency-Key there, so it has no status to poll and no
key header is sent.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(cli): keep v3.ts comment share at the base

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(cli): regenerate the cloud client without API description text

The generated client and types now carry names and types only, with no
JSDoc taken from the API spec's summaries and descriptions. The source
contract test checks that no method carries generated description text,
in place of checking the uploadAsset description.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-03 08:59:27 +00:00
Miguel Ángel b84835c413 feat(studio-server): the preview records the kind and origin of each early error (#7041)
* feat(studio-server): the preview records the kind and origin of each early error

* fix(studio-server): write preview script values with U+2028/2029 and < escaped
2026-11-03 07:47:49 +00:00
Miguel Ángel 9a2cd9e61b fix(producer): a BeginFrame capture failure re-renders on a screenshot session (#7070) 2026-11-03 07:45:44 +00:00
Miguel Ángel 79b6af9b48 feat(producer): a render request can force screenshot capture for that render (#7066)
* feat(producer): a render request can force screenshot capture for that render

A retried render can ask for the slower, safer capture mode without changing the pod's environment.

* fix(producer): drop the comment line the comment ratchet flags
2026-11-03 07:32:17 +00:00
Miguel Ángel 3eead5432d fix(core): audio no longer plays fast for a moment after Play (#7067)
* fix(core): audio no longer plays fast for a moment after Play

* refactor(core): split the audio start wait out of the clock read
2026-11-03 07:30:57 +00:00
Miguel Ángel 3baf81f9a9 fix(cli): reverse on Free runs billing subscribe credited to the run (#7046)
* fix(skills): reverse on Free opens the upgrade page for the person

* docs(skills): reverse on Free opens the upgrade page with a quoted link

* fix(cli): reverse on Free runs billing subscribe credited to the run

* fix(cli): trim the reverse skill and drop a doc comment for lint budgets
2026-11-03 05:46:34 +00:00
James RussoandClaude Opus 5.5 8c799ebd1b chore: release v0.9.7 (#7064)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v0.9.7
2026-11-03 05:21:25 +00:00
Miguel Ángel bc9f33488b docs(skills): media-use voiceover is free only on the Free plan (#7065) 2026-11-03 05:15:09 +00:00
heygen-rui-botandJames Russo 58a01f1692 chore(codegen): resync cloud client from EF 59dfee8c (#7058)
Co-authored-by: James Russo <james.russo@heygen.com>
2026-11-03 04:55:22 +00:00
James RussoandClaude Opus 5.5 dffc640e85 fix(cli): drop --page-motion-blur from cloud render (#7062)
* fix(cli): drop --page-motion-blur from cloud render

The flag sent page_motion_blur to the hosted render API, which never
accepted that field and rejects unknown fields. Motion blur is set in
the composition with data-motion-blur, and the runtime turns it off on
its own when it cannot render correctly, so the cloud flag is removed
with its request plumbing, test and docs row.

Local render keeps its own --page-motion-blur flag.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(ci): allow deleting the cloud render page motion blur test

The deletion guard blocks removed files unless they are listed. The
test file only covered the cloud render --page-motion-blur flag that
this branch removes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-03 04:21:08 +00:00
Miguel Ángel 0f85af7c7f fix(core): preview playback lands where a seek does when a set interrupts a tween (#7061)
* fix(core): preview playback lands where a seek does when a set interrupts a tween

* test(core): the preview overlap fix holds under a nested time scale and fires each callback once

* style(core): shorten the overlap comment

* refactor(core): the overlap span names its fields and carries no comments
2026-11-03 04:16:09 +00:00
James RussoandClaude Opus 5.5 112ba53302 fix(studio): motion blur falls back for a WebGL canvas Studio reaches first (#7060)
* fix(core): motion blur notices a WebGL canvas the Studio page touched first

When a same-origin host page (Studio) reaches a preview element before the
scene does, for example through a MutationObserver record, Chrome gives that
element the host's prototype. The scene's getContext("webgl") then ran the
host's unpatched getContext, so the canvas never reached the accelerated
canvas set, capture motion blur stayed on, and the WebGL canvas froze during
preview playback.

The tracker now records a canvas in the set of the canvas's own window, and
installs itself in a same-origin parent too, compiled with the parent's own
Function. Cross-origin parents are skipped.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(studio): track accelerated canvases in Studio's window, not the embedder's

The preview runtime no longer installs the canvas tracker in its parent
window. That ran on every preview boot, so any same-origin page embedding
the player got its getContext wrapped, and a host CSP without
unsafe-eval reported a violation on each load. Studio now installs the
tracker in its own window when its Player module loads, and core
exposes it at @hyperframes/core/page-motion-blur.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore: keep the comment share of the touched files from rising

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-03 03:59:23 +00:00
Miguel Ángel 05ddddbbf0 docs(skills): reverse tops up once for the run's total and inits with --video (#7048)
* docs(skills): reverse asks to top up once for the run's total

* docs(skills): reverse builds the project with init --video

* test(skills): pin reverse's init --video and top-up-once wording

* docs(skills): a refusal with no amount runs a plain top-up
2026-11-03 03:13:11 +00:00
Miguel Ángel 5e30bbdbe1 feat(lint): flag a set that fires inside a running tween on the same property (#7059)
* feat(lint): flag a set that fires inside a running tween on the same property

* fix(lint): shorten a comment line in the tween overlap helper
2026-11-03 02:47:18 +00:00
James RussoandClaude Opus 5.5 d03bdeae9d fix(cli): prefer OAuth session over HEYGEN_API_KEY (#7051)
* fix(cli): prefer OAuth session over HEYGEN_API_KEY

A signed-in `hyperframes auth login` session now wins over an API key in
the environment, so a paid plan on the login is used even when the shell
also exports HEYGEN_API_KEY.

New order: HEYGEN_ACCESS_TOKEN (host app), the stored login (refreshed
when expired), HEYGEN_API_KEY, HYPERFRAMES_API_KEY, the saved API key.
The env key still comes first when HEYGEN_API_URL or HEYGEN_API_BASE
names a host app's gateway, and takes over for the rest of the process
when the login cannot be refreshed. The media-use audio helper follows
the same order.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cli): fall back to the env key only when HeyGen refuses the refresh

A 503, a network error or an overlapping refresh no longer switches the
process to HEYGEN_API_KEY. The fallback now needs a refused grant or an
expired login with no refresh token. A login another process stored since
is re-read first, and refreshes that overlap in one process share one
request. The media-use helper no longer treats HeyGen's own API base as a
gateway.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cli): keep the login when the refresh reply can't be read

Only a 400/401 from the token endpoint marks the grant as refused and
switches to the env key. A 200 whose body can't be parsed now throws and
leaves the login in place, so the next call refreshes again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-03 01:52:30 +00:00
Miguel Ángel 92943d99f0 fix(studio-server): two exports in the same second get separate jobs (#7053)
* fix(studio-server): two exports in the same second get separate jobs

* fix(studio-server): name the same-second job id helper instead of commenting it
2026-11-03 01:15:11 +00:00
Miguel Ángel 81b93cf055 fix(cli): snapshot stops Chrome decoding videos it already replaces with extracted frames (#6949)
* fix(cli): snapshot stops Chrome decoding videos it already replaces with extracted frames

* fix(cli): a held video keeps its intrinsic size in snapshot, as in render

* fix(cli): shorten a comment line in the held-video hints
2026-11-03 01:14:27 +00:00
Miguel Ángel 8a6636efe3 fix(cli): the latest version in _meta is never older than the running CLI (#7049)
* fix(cli): the latest version in _meta is never older than the running CLI

* fix(cli): keep the daily recheck cadence when the cached latest is older than the running CLI
2026-11-03 01:13:02 +00:00
Miguel Ángel eb3562966f feat(preview): an unplayable video shows its own frame while its copy is made (#6589)
* feat(preview): an unplayable video shows its own frame while its copy is made

* fix(preview): drop the comment on the copy's light flag

* fix(preview): shorten the still helper's doc comment

* test(preview): each proxy-wait test starts with no copy report left over

* fix(preview): cached still frames are swept with the proxy copies
2026-11-03 00:23:50 +00:00
Miguel Ángel b22af8f25c chore(studio-server): raise the package size budget for the preview features (#7044) 2026-11-02 23:48:00 +00:00
Miguel Ángel de2603e637 fix(cli): auth login --device works without a terminal (#7045) 2026-11-02 23:46:36 +00:00
James RussoandClaude Opus 5.5 6feb4bec7a docs(changelog): weekly digest 2026-10-26-2026-11-02 (#7032)
* docs(changelog): weekly digest 2026-10-26-2026-11-02

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs(changelog): embed the Oct 26-Nov 2 weekly video

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs(changelog): tighten two claims from review

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-02 22:24:40 +00:00
Miguel Ángel 4ef8c1de5c docs: add four missing entries to the v0.9.6 release notes (#7040) 2026-11-02 21:14:20 +00:00
Jerrai 398ef50624 chore: release v0.9.6 (#7037) v0.9.6 2026-11-02 20:19:33 +00:00
Vance IngallsandClaude Opus 5.5 2bdc866c85 fix(core): a canvas in a blurred composition stays where it was laid out (#6975)
* fix(core): a canvas in a blurred composition stays where it was laid out

drawElementImage drops a nested canvas's layout offset unless the canvas
has a transform, so with capture blur on, `top: 200px` painted at 0. A zero
`translate` while captured is enough, and leaves `top`, the composition's
own `transform` and its `translate` alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(core): give the nested canvas rule no specificity

A class on a composition's canvas lost to the rule, so its own `translate`
was dropped while captured. Under `:where()` any composition rule wins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 11:41:01 -08:00
Vance IngallsandClaude Opus 5.5 01c0bf4962 fix(core): a motion blur worker starting mid-film keeps the right state (#6967)
* fix(core): a motion blur worker starting mid-film keeps the right state

The mover scan walks the whole film with callbacks live, then seeks back to
the worker's start. GSAP fires one-off calls in both directions, so a worker
starting between two calls took the later call's state and held it until the
next call. Going through 0 first gives every worker what frame 0 sees.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(core): rewind in the mover scan, so the filter tier gets it too

Both tiers call scanMovers and then seek to the worker's start. Rewinding
inside the scan covers them both, including the rescan after a tier handoff.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 11:40:25 -08:00
Miguel Ángel 3bb0a9a5b5 perf(engine): skip the screenshot on frames the page reports unchanged in beginframe capture (#7027)
* perf(engine): skip the screenshot on frames the page reports unchanged in beginframe capture

* fix(engine): beginframe reuse never returns a frame older than a cross-frame page change

* fix(engine): the BeginFrame reuse test gets a headless shell in the flake gate

* fix(ci): run the headless shell installer through its package

* fix(ci): install the headless shell with bunx, npx finds no bin in the workspace
2026-11-02 19:17:31 +00:00
Miguel Ángel 472b750acb fix(cli): cloud export re-encodes media whose bytes differ from its extension (#7033)
* fix(cli): cloud export re-encodes media whose bytes are not the type its extension claims

* test(cli): generated media in the offload tests carries its container header

* fix(cli): an audio-only MP4 named as video is re-encoded as audio

* test(cli): type the header fixture

* test(cli): give the heavy-media dry-run fixture a real mp4 header
2026-11-02 19:14:24 +00:00
JerraiandXuanru Li acd5c30e60 feat(cli): read audio stems through your HeyGen account (#6993)
* feat(cli): audio --read organises the reading by stem

Measures where each stem is heard, leaves the drums' recurring hits to the
music while it plays, names the stem of each remaining effect, writes a
voice-free mix next to the stems, and describes each audible stem and the
whole recording with Gemini.

* feat(cli): audio --read reads stems as vocals, drums, bass and melodic

Piano, guitar and other are mixed into melodic.wav next to the stems; levels,
spans, descriptions, effects and pieces are read per group, and a group heard
for under two seconds or 5% of the recording is left out of the reading.

* feat(cli): audio --read describes the six stems and writes a BGM prompt from them

Stems are read one by one again; a stem heard under two seconds or 5% of the
recording is still left out. Each stem's description gives the times of its
changes and names the effects it hears. The overall and the BGM prompt are
written from those descriptions and the measured spans, not by listening to
the mix again; the BGM prompt orders the arrangement without seconds.

* fix(cli): audio --read writes no BGM prompt for a recording without music

Foley and effects in a rhythm are not music; the reading says so instead of a
prompt that would make a remix generate a track the reference never had.

* fix(cli): audio --read prompts name no example sounds

* fix(cli): describe audio through HeyGen and keep effects measured

* docs(cli): document measured effect events without sound kinds

* fix(cli): clear audio description review checks

Use portable stem path assertions and simplify response validation.
Keep auth-derived URLs out of cache hashes and trim redundant comments.

— Jerrai

---------

Co-authored-by: Xuanru Li <157947275+xuanruli@users.noreply.github.com>
2026-11-02 18:42:05 +00:00
Miguel Ángel 0bcd816d5b feat(engine): a chunk-fed encoder and the rule for which films may use it, off by default (#7029)
* feat(engine): a chunk-fed encoder and the rule for which films may use it, off by default

* test(engine): end the chunk encoder's ffprobe call with --
2026-11-02 17:53:57 +00:00
Miguel Ángel e8ffaa410f fix(core): preview recovers its WebGL scenes and effects after a lost context (#7028)
* fix(core): preview repaints and rebuilds effects after the GPU restores a lost WebGL context

* fix(core): a paused scene redraws and a playing one repaints effects after a context restore

* test(core): a paused scene is redrawn after a WebGL context restore

* test(core): give the restore redraw test a clip

* fix(core): shorten the context recovery header comment

* fix(core): rebuild effects only after every lost context has restored

* test(core): the restore rebuild waits while another context is still lost

* test(core): count context requests to show the rebuild waits

* test(core): the mock reports the lost flag the test sets
2026-11-02 17:47:44 +00:00
James RussoandClaude Opus 5.5 4c1c1782af test(studio): quarantine the seqplainkeys cases whose undo sometimes leaves the box off (#6990)
* test(studio): quarantine the seqplainkeys case whose undo sometimes leaves the box 240px off

seqplainkeys-none-center-r0-root-z100 failed 2 of 3 runs in the merge group
for two version-bump PRs (#6970, #6987). In each failing undo the bytes come
back but the box stays 240.42px off, which is a Studio undo race, not noise.
It stays measured and listed; #6989 tracks the fix, which removes this entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(studio): quarantine the two sibling seqplainkeys center cases with the same undo race

center-r30-root (undo box 313.6px off) and center-r0-nested (redo box
240.7px off) ejected 3 and 1 merge-queue entries since 10-30, the same race
as center-r0-root. With all three quarantined, the gate passes on the shard
results of all 8 failed merge-group runs since 10-30; main's gate fails each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(studio): quarantine the pct and px seqplainkeys cases with the same undo race

The race also hits the pct and px placements: 5 more cases left the box
230-400px off after undo or redo in the 9 failed merge-group runs since
10-30. Merge-group run 41485305789 bounced #6990 on px-r0-nested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 17:44:31 +00:00