* 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.
* 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>
* 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.
A regenerated shot never lines up with a whip frame by frame, so diff listed every fast move.
A jump now counts only above the original's own motion around it.
fails without the fix: reads a change no stronger than the original's motion as the same fast move
* fix(cli): snapshot shoots a frame again when Chrome drops the screenshot mid-paint
* fix(cli): keep the first screenshot error when the retry fails differently
* fix: cache paths follow HYPERFRAMES_CACHE_DIR
Every place HyperFrames keeps downloaded or built files now takes its
folder from one helper, hyperframesCacheDir() in @hyperframes/core. The
default stays ~/.cache/hyperframes; HYPERFRAMES_CACHE_DIR moves it.
* fix: register the cache-dir subpath for bun and node, and build inspector from core source
* test: the cache-dir override test uses a folder that is absolute on every platform
* fix: the opencv cache follows HYPERFRAMES_CACHE_DIR
* fix(cli): npm installs run in their staging dir, so no cache path reaches a command line
* fix(cli): whisper.cpp is cloned inside its build dir, so no cache path is a git argument
* test(cli): the whisper clone test finds no system whisper on Windows either
* fix(cli): say a font or image URL is a download, not a page, when capture is given one
* fix(cli): word the capture download hint as a likely cause, not a fact
* fix(core): accept 65-point .cube LUTs, the size grading apps export
* fix(core): shrink LUTs wider than the GPU texture limit instead of rendering black
* fix(cli): keep browser sign-in open through checkout
The loopback listener timed out after 5 minutes, but plan checkout runs
inside the OAuth flow. A slower purchase ended on ERR_CONNECTION_REFUSED.
Raise the default to 60 minutes, matching pacific's plan-checkout handoff.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(cli): show the sign-in limit and how to recover
The waiting line now states the 60-minute limit and Ctrl+C. After a
timed-out browser login the CLI says to run auth login again.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(cli): drop unverified purchase claim, harden timer test, document limit
Review fixes: the timeout hint no longer says purchases stay on the account
(the CLI cannot observe billing); the no-timer-left test now asserts before
any explicit close() and uses node:http so undici timers cannot pollute the
count; docs mention the 60-minute window and Ctrl+C.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(cli): close the sign-in listener on every path and record Ctrl+C
Ctrl+C is now the advertised exit, but events only flush on exit, which a
default SIGINT skips, so an abandoned login vanished from telemetry. Record
it as aborted and exit 130 through requestCliExit. Close the listener on any
throw, so a failure before the wait can't hold the process for the full
window. Derive the waiting line and timeout message from the listener's own
limit, and name the constant for why it is long, which clears the comment
ratchet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(cli): a stale mask takes its frame's colours instead of delogo's streaks
Delogo drew the box's edges across large boxes, and HeyGen painted the streaks into the shot.
Shrink the box to 3x3 and blur it back, so no letter or streak survives.
fails without the fix: leaves no streaks in a box filled while the picture changes
* fix(cli): the stale mask fill drops gblur, which blacked out light boxes
On a 3x3 picture gblur turned light fills black on an FFmpeg 8 build.
Bicubic from 3x3 is already smooth.
* fix(cli): a masked box follows the frame while the camera moves
A temporal median under a moving camera froze older frames in the box, and HeyGen painted them in.
Fill from the box's own edges on each frame when the picture round it changes.
fails without the fix: fills a masked box from the current frame while the picture changes
* fix(cli): the mask scan stops where the guide does, and small boxes still blur
A long guide overflowed the decode buffer.
A narrow box asked for more blur than its chroma plane holds.
* feat(cli): the api exports offloadHeavyMedia and every offloaded file reports its asset_id
* fix(cli): drop the asset_id doc comment that raised the file's comment share
* fix(cli): the cut detector keeps real cuts in live footage
#6894 left held frames out of every baseline and called any run over 3 frames a fast
move. On live footage that raised the baseline past real jump cuts and dropped blur
transitions between shots. Only animation that moves and holds in turn gets the moving
baseline now, and a run stays a cut when its tones change.
* fix(cli): a wipe through black is a cut, and a cut near a guide's edge is refused
A whip keeps its tones through every frame of the run, so that is the test, not only
the two ends: johnny's wipes between two views of one map match at the ends alone.
foldShort folds pieces of a few frames now. At 0.5 s it let a real cut through to a
paid shot, and the classifier already handles the flashes it was there for. --allow-cuts
is in the skills.
* refactor(cli): name the whip check instead of commenting it
* fix(cli): check and snapshot free video clips the playhead left, halving memory on cut films
* fix(core): drop the releaseMedia doc comment that raised the file's comment share
* fix(cli): a shot from --first-frame comes back in that frame's shape
HeyGen can answer 1344x768 for a 16:9 first frame. The crop only ran with --aspect or
with no first frame, so these shots kept its size. Without --aspect the first frame now
sets the shape.
* refactor(cli): drop a comment the function name already says
* fix(cli): the first frame's shape is the one sent, not the one stored
A phone JPEG with Orientation=6 uploads as portrait but stores landscape pixels, so a correct
portrait answer was cropped to 90x50. The shape now comes from the same conversion the upload uses.
fails without the fix: keeps a portrait shot from a phone photo stored sideways
* fix(cli): snapshot gives a seek that will not settle its own --timeout, not three minutes
* fix(cli): a seek budget never outlives the protocol timeout; say so in --timeout help
* fix(cli): billing and Desktop handoff read their hosts from settings
* fix(cli): OAuth client and API hosts read from settings
* test(cli): host and client settings tests run the same on any machine
* chore(cli): format and refresh the skills manifest
* chore(cli): trim a comment
* chore(cli): keep the stylesheet as it was
* feat(cli): measure ink reads text size, --out and --output both work, logo --variant wordmark
measure ink reports the bounds, size and lines of text in a region of one frame, so a rebuild can be sized without a script. measure and snapshot take --out and --output for the same folder. media-use resolve --type logo --variant wordmark returns the brand's wordmark when theSVG has one.
* fix(cli): measure ink stays under the complexity and comment limits, one projectOf
* fix(cli): snapshot and measure also accept --output-dir
* fix(cli): projectOf moves without adding comment lines
* fix(cli): measure output alias list type-checks as an argument
* fix(cli): a logo variant never answers a default logo request; test output flags
* style(cli): format the new tests
* fix(cli): one cut detector that tells real cuts from flashes and fast moves
* fix(cli): a flash must come back within a few frames; trim comments, format
* test(cli): the cut fixture gets a 60 s timeout; format
* test(cli): drop a history comment
* fix(cli): cloud render uses the composition's data-fps and skips unused heavy files
* fix(cli): satisfy typecheck and comment ratchet for cloud render fps
* fix(cli): stopping remove-background on a Mac no longer aborts the process
* fix(cli): a quit after background removal released its session exits at once
* Revert "fix(cli): a quit after background removal released its session exits at once"
This reverts commit 3b9d6ffc24.
* fix(core): a seek that never settles ends after 30 s and names what it waits on
* fix(core): keep the seek-wait comments within the comment limits
* fix(core): only a caller's limit ends a seek wait; check and snapshot end it before their timeout
* fix(producer): auto renders with color-graded media stay SDR instead of promoting to HDR
* fix(producer): keep auto renders SDR when a graded canvas is present
* 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.
* fix(cli): the credits link opens the web plan page, which is live
* test(cli): pin the production credits link too
* chore(skills): regenerate the skills manifest after merging main
* docs(cli): keep the person measure's doc comments to four lines
* docs(cli): keep the measure doc comments to four lines
* docs(cli): keep the measure comment lines to 120 characters
---------
Co-authored-by: Miguel Angel Simon Sierra <miguel.sierra@heygen.com>
* fix(lint): the podcast-lanes.html page Desktop writes is not a second root composition
HyperFrames Desktop's podcast edit writes its camera lanes page, podcast-lanes.html,
at the project root beside index.html: Studio reads no composition inside a dot
folder, and Desktop opens the page directly as its own view. index.html never mounts
it and no render plays it, but multiple_root_compositions fails lint and check on
every podcast project, and its fix hint tells an agent to remove the file. Exempt it
the way caption-skin.html already is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(lint): name the pages opened beside index.html instead of commenting them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>