Commit Graph
6678 Commits
Author SHA1 Message Date
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
Wenbo ZhuandClaude Opus 5.5 7da583ec69 feat(cli): reverse start stops a Free account when the server blocks it (#7017)
When /v1/hyperframes/reverse/start answers gate "block" for plan "free", `reverse start`
saves the session, then exits non-zero. The error and its `fix` carry the upgrade link that
credits the purchase to this run: the server's `upgrade_url`, else the plans page with
`feature=reverse&rs=<session>`. Pro, unknown plans, `record`, and every error still go on.
The reverse-any-video skill tells the agent to pass that exact link on and stop.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 17:27:31 +00:00
Miguel Ángel e3a7cdd2b8 feat(player): loading assets shows the blurred frame and the mark, like Desktop (#6934)
* feat(player): loading assets shows the blurred frame and the mark, like Desktop

* fix(player): name the mark mask instead of commenting it
2026-11-02 17:14:29 +00:00
James RussoandClaude Opus 5.5 ceea238911 test(studio): the dev server render job tests wait up to 20 s for the render (#6992)
vi.waitFor gives up after 1 s by default, inside a describe that already
allows 30 s per test. On a loaded Windows runner the blur test saw the job
still "rendering" at 1 s and failed the #6980 merge group (job 125103352086).
Reproduced by making the mocked render take 1.5 s: main fails with the same
"expected 'rendering' to be 'complete'"; this passes.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 16:50:02 +00:00
James RussoandClaude Opus 5.5 bcd7b9b857 test(producer): the stall tests wait out a slow cold start before moving fake time (#6991)
runFor shared one 1 s real-time budget between the render's start and every
step. When the cold start alone spent it, fake time jumped before the stalled
attempt opened, so the 400 s window ran out with no retry: "expected [] to
deeply equal [2]" on the first test, four merge-group failures since 10-30.
The first hold now gets its own 4 s budget. Reproduced by holding the compile
stage mock for 1.5 s of real time; it fails before and passes after.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 16:49:52 +00:00
Wenbo ZhuandClaude Opus 5.5 7d3c225207 feat(cli): reverse start records a reverse run; cloud render carries its session (#7016)
* feat(cli): reverse start notes a reverse run with HeyGen and cloud render carries its session

`npx hyperframes reverse start <video> [--dir <project>]` probes the video (the same JSON
as `measure probe`), then, when signed in, calls POST /v1/hyperframes/reverse/start and saves
the returned session id in the project's hyperframes.json. It never stops the run: signed
out, offline, a 404 from a server without the route, or a malformed answer all continue
with the probe and no session. `cloud render` sends `reverse_session_id` only when the
project holds one, which only a successful /reverse/start writes.

`init` now treats a folder holding only hyperframes.json as empty, so `reverse start --dir x`
followed by `init x` works and keeps the session. The reverse-any-video skill opens with
`reverse start` instead of `auth status` + `measure probe`, with a fallback for older CLIs.

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

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

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

* fix(cli): reverse start keeps unknown-plan sessions, prints the attributed upgrade link, takes only rvs ids

- A `plan: "unknown"` answer is a valid session: saved, `recorded: true`, printed as
  `plan: null`; it never blocks and never falls back to the client-side plan read.
- On Free, `reverse.upgrade_url` is printed: the server's link, else the plans page with
  `feature=reverse&rs=<session>` (the fields checkout reads). The skill passes that exact
  link on instead of `billing subscribe`.
- Session ids must match the render endpoint's pattern (`rvs_<lowercase uuid>`). A saved
  value that does not is ignored: not sent on `cloud render`, and `reverse start` opens a
  fresh session.

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

* docs(skills): name no CLI version for reverse start's fallback

0.9.4 shipped without `reverse start` and skills go live from main before the CLI release,
so the fallback is described as "a CLI without it" instead of a version number.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 16:47:05 +00:00
Miguel Ángel c25e092114 fix(core): Safari audio is ready at the cut, and a stuck one resyncs in 1 s (#7024)
* fix(core): WebKit audio seeks to its in-point before its cut, and a stuck one resyncs in 1 s

* fix(core): shorten the WebKit stuck-audio comment

* fix(core): let the stuck-audio constant name stand without a comment
2026-11-02 16:46:47 +00:00
Miguel Ángel c14d92a227 chore: release v0.9.5 (#7025) v0.9.5 2026-11-02 16:20:52 +00:00
Miguel Ángel 6fd96ddd04 revert: stop releasing a WebGL context when its clip's window has ended (#7020) 2026-11-02 15:48:12 +00:00
Miguel Ángel 070780c695 fix(producer): a render stops when a capture session sees every composition script fail (#7009)
* fix(producer): a render stops when a capture session sees every composition script fail

* fix(producer): only capture workers decide a script failure; prep sessions stop without recording it

* fix(producer): a script failure in a capture worker still reports as the composition-script error

* style(producer): format the script-failure test
2026-11-02 15:46:50 +00:00
Miguel Ángel 5a07ea2e5f fix(cli): catalog says the registry could not be read instead of listing nothing (#7015) 2026-11-02 14:47:04 +00:00
Jerrai 346f0e49fa feat(producer): report browser-computed reference backgrounds (#7007)
* feat(producer): report browser-computed reference backgrounds

Preserve one initial root/page reference across local, streamed and distributed
render results without guessing from film pixels.

— Jerrai

* fix(producer): preserve plan compatibility and optional inspection

Keep reference metadata outside the v2 manifest hash and read it from the
verified execution-plan artifact. Static planning uses a non-gating page
inspection instead of requiring render-worker capabilities.

— Jerrai

* fix(producer): poll readiness independently of animation frames

Use timed polling while the render clock parks animation frames. Cover readiness
that changes after the first check with a short real-browser regression.

— Jerrai

* fix(producer): budget measured reference metadata runtime growth

Producer adds 2,228 compressed bytes over current main. Tests, fixtures and source
maps are already absent from its dist-only tarball. Allow 7,766 bytes of headroom
above the verified 837,234-byte payload while retaining the unpacked limit.

— Jerrai
2026-11-02 14:42:18 +00:00
Miguel Ángel 34a8d48160 chore: release v0.9.4 (#7018) v0.9.4 2026-11-02 14:33:44 +00:00
Miguel Ángel b23b51f4f0 fix(cli): snapshot fails when an on-screen video frame cannot be read (#7011)
* fix(cli): snapshot reads video frames through any ffprobe and fails on an unreadable one

* fix(cli): snapshot reads video frames through any ffprobe and fails on an unreadable one

* fix(cli): snapshot counts only a failed frame read as unreadable

* fix(cli): snapshot fails when an on-screen video frame cannot be read
2026-11-02 13:48:20 +00:00
Miguel Ángel 036e41fa09 fix(core): iPhone Safari keeps one connection per file for the take on screen and the next (#7012)
* fix(core): iPhone Safari keeps one connection per file for the take on screen and the next

* fix(core): drop the doc comments the comment ratchet counts in studioPreviewMark
2026-11-02 13:15:47 +00:00
Miguel Ángel cd5984261a fix(studio): an undo that drops the keyframes clears the transform the old preview still shows (#7005)
A restore that removes the GSAP script cannot be patched in place, so the preview reloads - and until it lands it keeps the transform the timeline composed, though the file it is going back to places the box with CSS alone. Only the elements the restored script stops animating are repainted; anything it still animates keeps what the timeline gave it.
2026-11-02 12:29:46 +00:00
Miguel Ángel 8891449401 fix(cli): exit-time telemetry stays silent when the temp folder cannot be written (#6925) 2026-11-02 12:18:46 +00:00
Miguel Ángel f6536e9f5d chore: release v0.9.3 (#7010) v0.9.3 2026-11-02 12:04:35 +00:00
WaterrrForever 8089f0049c feat(skills): soft-launch reverse-any-video, and fix what Wanbi's Pro test found (#7006)
* feat(skills): publish reverse-any-video, render it at high quality, and stop on a stuck shot

From Wanbi's Pro test of reverse-any-video (11-01, checked against 0.9.2):

- A standard install never found the skill: it was internal, and /hyperframes did not route to
  it. It is now an on-demand skill on every catalog surface, and the routing line names it.
- The cloud render in SKILL.md had no --quality high, so the final rendered at standard.
- A re-shoot that stayed pending had no rule; the run waited four hours. people.md now runs the
  command once more after generate's 30-minute stop, then treats the shot as failed.

* feat(cli): generate takes a language for --voice, so a Pro sign-in gets a HeyGen voice

Wanbi's Pro run fell back to Kokoro: --voice needed an id, and the only way to list ids was the
separate heygen CLI and its own login. /v3/voices answers on the HyperFrames sign-in, so:

- --voice <language> picks HeyGen's first public voice in it and names it in the output.
- A missing --voice lists six English voices to pick from instead of a bare error.
- A voice id still goes straight through, with no extra call.

* fix(cli): a voice picked by language stays with its request across re-runs

Rames: `--voice Chinese` re-picked HeyGen's first voice on every run, and the voice is part of the
body and the idempotency key. If the catalog reordered while a job was pending, the same command
could start a second paid job instead of resuming the first.

The pick is now bound to the request (every field, plus the account scope a job bills) in the
project's heygen-jobs.jsonl, or the CLI config dir outside a project, and a re-run reuses it.
Regression: a pending timeout, a reordered catalog, the same command, for voice and avatar; the
second POST keeps the voice, body and key.

* fix(cli): a voice pick waits for a known account instead of binding to none

Rames: when /v3/users/me failed, senderScope() was null and the pick was bound to an empty
scope; once the account came back, the same command missed that binding and could pick another
voice and paid key. pickVoice now stops before picking or posting while the account is unknown,
and never reads or writes a blank-account binding.

Regressions, both directions: unknown then known refuses with no POST, then picks once; known
then unknown refuses the re-run with no second POST, then resumes with the same body and key.
Also README: a non-interactive install gets all 8 skills, not 7.
2026-11-02 11:48:55 +00:00
Miguel Ángel d17b958318 feat(studio-server): stamp a project hf-ids without holding the event loop (#7004) 2026-11-02 11:15:42 +00:00
Miguel ÁngelandHuntsDesk c54f84859d fix(studio-server): long media clips no longer leave the host unanswered (#7002)
* fix(studio-server): more than six clips on one media file no longer starve check and preview of connections

Every file server answered `Range: bytes=N-` with the rest of the file. A
media element reads that until its buffer is full, then stops reading but
keeps the response open, so each <video> held one of Chrome's six HTTP/1.1
connections to the host. With seven or more <video> clips on the server's
host, every other request queued behind them and `hyperframes check`
stalled at 0% CPU. A long recording cut into trimmed segments, each its own
<video> on the same file, is enough.

An open-ended range of audio or video now gets at most 4 MiB
(OPEN_MEDIA_RANGE_BYTES), as a normal 206 with an exact Content-Range. The
element asks for the next range as it plays or seeks. Bounded and suffix
ranges, and files that aren't media, are served as before. The helper lives
in studio-server; its preview route and the CLI's static project server and
composition server use it.

Fixes #6029

Claude-Session: https://claude.ai/code/session_014aw1z3VrTNenka3LGDBBLa

* fix(studio-server): carry the range cap's reason in names and tests, not comments

The comment ratchet doesn't let a file's comment share rise: mediaRange.ts
was 48% comments against the package's 7%, and staticProjectServer.ts gained
a line. The cap is now MAX_OPEN_MEDIA_RANGE_BYTES, and the why and the
repro stay in the three tests and #6029.

Claude-Session: https://claude.ai/code/session_014aw1z3VrTNenka3LGDBBLa

* fix(producer): the render file server bounds open-ended media ranges too

parseRangeHeader takes the response's content type and caps an open-ended
`bytes=N-` on audio or video with openRangeEnd, like the CLI and Studio
servers. The producer imports it from a new `media-range` subpath of
@hyperframes/studio-server, as it does `encoder-options`, so a render
doesn't load the rest of the package.

Claude-Session: https://claude.ai/code/session_01DW4V8HZZx5JUgMffb3juVV

* test(cli): eight clips on one file no longer leave the host unanswered

A real Chromium page buffers eight long video clips of one file from the project server and then asks the same host for another file; with open-ended ranges unbounded the request stays unanswered, with the bounded piece it is answered at once.

* docs(producer): range comments name the open-ended form in lowercase

* docs(cli): the range test's comment says what holds now

---------

Co-authored-by: HuntsDesk <hunter@talloak.com>
2026-11-02 11:03:55 +00:00
WaterrrForever 618c486734 fix(cli): measure and diff report why they failed, and reverse reports its runs (#6997)
* fix(cli): measure and diff report why they failed, and reverse reports its runs

Since launch, 97 people saw a bare "Command failed" from measure: runMeasure
and diff called failCommand() without the error, so telemetry kept only the
wrapper. 59 agents ran `measure <file>` with no op and got no hint. Reverse
runs had no skill tag, so nobody could count how many finished.

- runMeasure and diff pass the error to failCommand.
- An unknown word that is an existing file names the commands to put first.
- references/start.md sends skill_invoked and skill_completed beacons.

* fix(cli): the file hint stays out of the root command list

Jerrai: `hyperframes index.html` would list every top-level command. fileHint moves to
didYouMean.ts, answers only inside a command group, and has tests for the root, a missing
file and a group with no commands.
2026-11-02 10:03:08 +00:00
Miguel Ángel a46412ee07 fix(studio): a preview reload writes the composition as it is now, not as it was when it asked (#6998)
* fix(studio): a preview swap writes the composition as it is now, not as it was when it asked

Claude-Session: https://claude.ai/code/session_01BzWPCkrNkiYrpRYeqVmKyK

* fix(studio): a shadow reload that a write outran loads the file again

Claude-Session: https://claude.ai/code/session_01BzWPCkrNkiYrpRYeqVmKyK

* fix(studio): count the project's revision once per write, not again on its echo

Claude-Session: https://claude.ai/code/session_01BzWPCkrNkiYrpRYeqVmKyK

* refactor(studio): move the swap freshness check next to the swap it guards

* refactor(studio): name the refetch bound instead of explaining it
2026-11-02 09:52:07 +00:00
Miguel Ángel aaa5b63f55 fix(core): keep media prefetch queue ordered (#6996)
* fix(core): keep media prefetch queue ordered

* fix(core): refresh queued prefetch priorities on timing edits
2026-11-02 09:24:45 +00:00
Miguel Ángel 43f8b1a3fc fix(lint): a helper script's timeline nested by the page is not reported as unregistered (#6999) 2026-11-02 09:20:46 +00:00
Miguel Ángel 5e50ac1c47 chore: release v0.9.2 (#6994)
* chore: release v0.9.2

* chore: release v0.9.2
v0.9.2
2026-11-02 08:42:46 +00:00
Miguel Ángel 26d6e3eef1 fix(engine): a scene that has ended gives up its WebGL context so the next scene is not slowed (#6984)
* fix(engine): a scene that has ended gives up its WebGL context so the next scene is not slowed

* build(engine): raise the engine package size budget for the WebGL release module

* chore(engine): shorten the WebGL release comment

* fix(engine): a scene gives up its WebGL context only when the runtime says its window ended

* chore(engine): drop a comment the type already says

* chore(engine): shorten a comment line
2026-11-02 07:50:16 +00:00
Miguel Ángel 5fca435623 feat(studio): hosts can ask which action holds a key combination (#6979)
* feat(studio): hosts can ask which Studio action holds a key combination

* fix(studio): the clash check skips a key map entry that is not a combo

* refactor(studio): studioKeyClash in fewer lines
2026-11-02 07:42:18 +00:00