Commit Graph
155 Commits
Author SHA1 Message Date
Miguel Ángel e0ba41c024 chore: release v0.7.107 (#3228) 2026-08-11 16:19:20 -04:00
Miguel Ángel 636dc042a7 feat(core): sanitize rich text on the way into a composition (#3141)
* feat(core): sanitize rich text on the way into a composition

Studio's patch vocabulary was inline-style, attribute, html-attribute and
text-content. text-content assigns textContent, and the text-field model
escapes markup on the way out and refuses a change in child structure, so a
styled span had no route into a composition file.

Adds a rich-text operation with one, guarded by a single sanitizer called on
both ends of the trip: in the browser so the preview shows what will be
saved, and on the server because that is where the file is written. Tags and
style properties are a small allowlist, and an unexpected tag loses its
formatting rather than its words. Spans an edit adds get their ids in the
same write, so a follow-up write cannot race it.

No UI yet — this is the persistence contract the editor is built on.

* fix(core): document and test the sanitizer boundary

* fix(core): harden rich text sanitizer traversal
2026-08-11 02:03:47 -04:00
Miguel Ángel c9dd8413c3 chore: release v0.7.106 (#3197) 2026-08-10 22:53:34 -04:00
Vance Ingalls bd1c1af291 chore: release v0.7.105 (#3152) 2026-08-10 08:08:16 -07:00
Miguel Ángel c96b30c717 chore: release v0.7.104 (#3147) 2026-08-09 17:19:31 -07:00
Miguel Ángel bea32b8aae fix(studio): stop a Studio edit from reloading the preview (#3137)
* fix(studio): stop a Studio edit from reloading the preview as if it were external

Every mutation route wrote the file without leaving a write receipt, so the
watcher's broadcast of Studio's own edit arrived with no identity on it. The
external-change coordinator could not tell that echo from an agent or an editor
writing the file behind Studio's back, so it took the safe branch and did a full
iframe reload. That reload hides the stage for the length of the reload, which is
what the flash after a text edit was.

Every mutation write now goes through one helper that records the receipt, and
the client claims the write before the request goes out rather than after it: the
server writes and the watcher fires while the request is still in flight, so a
token marked from the response can arrive after the echo it was meant to match.

Reproduced in the browser before and after, with the reload path traced end to
end. Before, a patch-element write logged `token: null` then a reload from the
coordinator; after, the same write logs the token and `suppressed: own write
token`, with no reload.

Adds `hf-reload-debug` (localStorage, off by default) alongside the existing
`hf-resize-debug`: it records each file-change decision and its reason, plus the
stack of whoever asked for a full reload.

* fix(studio): claim the timeline and caption writes too, not just the DOM ones

The receipt only helps when the client marked the token it sent, and the GSAP
mutation writers never sent one. A drag commits through gsap-mutations, so the
server minted a token the client had never seen, the change came back looking
like someone else's, and the preview did the full reload the receipt was meant
to prevent.

Same one-line claim on both GSAP mutation writers, the timing sync's mutation
call, and the caption auto-save PUT.

The rollback call stays deliberately unclaimed and says why: it runs because a
mutation did not converge, so the preview is on bytes nobody can vouch for and
the reload is the point.

Verified live: a drag-shaped update-properties on the timeline now logs
`suppressed: own write token` with no reload, where it logged a coordinator
reload before.

* refactor(studio): keep timelineTimingSync under the size cap

Claiming the timeline writes pushed this file one line past the 600-line
gate. Same change as the branch made later, landed with the commit that
caused it.

* fix(studio): cover remaining write receipt paths

* fix(studio): preserve batch write receipts

* fix(cli): emit every file in a watcher burst
2026-08-09 19:10:02 -04:00
Vance Ingalls adb13ce125 chore: release v0.7.103 (#3127) 2026-08-09 03:14:27 -07:00
Miguel Ángel b1f7d8881b chore: release v0.7.102 (#3119) 2026-08-08 13:13:50 -07:00
Vance Ingalls eba96feda7 chore: release v0.7.101 2026-08-07 19:50:48 -07:00
Vance IngallsandMiguel Ángel d9b00e57eb chore: release v0.7.100 (#3093)
Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
2026-08-07 16:12:25 -07:00
Miguel Ángel 9aa90f6e3e chore: release v0.7.99 2026-08-07 15:31:57 +00:00
Vance Ingalls 6114749d8e chore: release v0.7.98 2026-08-06 23:37:23 -07:00
Miguel Ángel c4b41072c7 chore: release v0.7.97 2026-08-07 05:16:27 +00:00
Vance Ingalls 026e6941ac chore: release v0.7.96 2026-08-06 14:53:26 -07:00
Miguel Ángel fc0298de4a chore: release v0.7.95 (#3068) 2026-08-06 10:12:20 -07:00
Miguel ÁngelandCodex 96861cbafc perf(studio-server): coordinate cancelable thumbnail generation (#2720)
* perf(studio): schedule adaptive timeline thumbnails

* perf(studio): bound thumbnail decoding resources

* perf(studio): virtualize timeline thumbnail media

* perf(studio): prioritize timeline thumbnail work

* perf(studio-server): coordinate cancelable thumbnail generation

---------

Co-authored-by: Codex <codex@local>
2026-08-05 20:41:35 -07:00
Miguel Ángel 29f004cfc0 chore: release v0.7.94 (#3045) 2026-08-04 20:42:17 -07:00
Miguel Ángel a4eb602ee2 chore: release v0.7.93 (#3044) 2026-08-04 20:27:07 -07:00
Vance Ingalls f9ec93459f Merge pull request #2945 from heygen-com/ffprobe-6-argv-sweep
fix(cli,core,lint,producer,studio-server): terminate ffprobe options everywhere, pin the contract
2026-08-04 10:43:14 -07:00
Vance IngallsandClaude Opus 5 d0dbf11ef5 fix(producer,studio-server): finish the ffprobe argv sweep, pin the contract
The previous commit claimed "all nine now terminate their options". That
was false: `producer/src/utils/audioRegression.ts:307` still passed the
path bare, and it is production source used by the regression harness.
A repo-wide audit found two more in studio-server
(`mediaValidation.ts`, `mediaMetadata.ts`) — their current callers pass
absolute paths, so they were defence-in-depth rather than live bugs, but
the exhaustiveness claim should be true rather than narrowed.

Eleven sites total, all terminated.

Adds a SOURCE-level contract test, which is the gap that let this
happen twice. #2740 fixed one of ten sites and shipped a regression
asserting the argv of that single site, so CI reported the class closed
while nine invocations still parsed `-intro.mp4` as an option. A
per-site unit test has the same blind spot for site twelve; scanning the
tree does not. The test also asserts its own coverage list has not
shrunk.

Verification: engine 1300, lint 511, core 1431, studio-server 398, cli
init/webmAlphaCheck/whisper 146, producer utils 51, audioPadTrim 18.
Removing any single terminator fails the contract test by name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 02:23:34 -07:00
Vance Ingalls df8b9604fe chore: release v0.7.92 2026-08-04 01:49:05 -07:00
Vance Ingalls 8c6cf90ff9 chore: release v0.7.91 2026-08-03 22:48:00 -07:00
Vance Ingalls 71fd96bbf1 Merge pull request #2854 from heygen-com/feat/canary-rollouts
feat(core): percentage-based canary rollouts + calibration experiment
2026-08-03 22:35:37 -07:00
Miguel Ángel 4e7fcf7f2a fix(core): resolve sub-composition sibling asset paths everywhere (#2994)
Extends the studio-preview fix to the render path and the asset-discovery
utilities, which share the same resolver and had the same defect.

`rewriteAssetPath` takes an optional `assetExists` probe. A plain relative ref
authored in a sub-composition (`_shared.css`, `clip.mp4`) is re-pointed at the
composition's own directory when that sibling exists on disk; project-root refs
with no sibling (the registry's `assets/logo.png` convention) stay as authored.
Callers that can see the filesystem supply the probe, so the module stays free
of node:fs.

Also fixes a second defect in the inliner: `<head>` <link> hrefs and external
script srcs are hoisted into the root document but never went through the
rewrite at all, so even the documented `../` form escaped the project and 404'd
at render time.

Wired into the preview bundler, the producer compiler, the studio preview
builder, the HEVC preview lint, the project lint's asset scans, publish proxy
baking, and media-treatment source resolution.
2026-08-03 21:13:35 -07:00
Miguel Ángel cbd8a77d16 fix(studio): resolve sibling asset paths in sub-composition previews (#2983)
Fixes #2956

## What

A composition in a subdirectory that references a **sibling** file (`<link rel="stylesheet" href="_shared.css">`, not `../_shared.css`) is now resolved against the composition's own directory when building the standalone sub-composition preview page.

## Why

The preview page borrows the project-root `<base href="/api/projects/:id/preview/">`, but the path rewriter only rewrote `../`-prefixed paths. So `design/styleframes/frame-01.html` referencing `_shared.css` was served unrewritten and the browser requested `/preview/_shared.css` → **404**.

With its stylesheet missing, the frame renders unstyled: `body` has no background, and the thumbnail generator's transparent-body fallback paints it `#1c2028`. Result: dark navy thumbnail with unreadable dark text on every styleframe in the Board view.

The report attributed this to project scale (~21 sibling files). It is not scale-related: a 2-file project reproduces identically, and the same file moved to the project root renders correctly. The trigger is **composition-in-a-subdirectory + relative sibling asset ref**.

Reproduced before the fix (single `curl` against the thumbnail endpoint, plus a direct headless capture of the preview URL):

```
HTTP 404 http://localhost:5190/api/projects/big/preview/_shared.css
body bg: rgb(28, 32, 40)
```

## How

`resolvePreviewAssetPath` in `packages/studio-server/src/helpers/subComposition.ts`, applied through the single rewrite pass all three dispatch branches (template / full-doc / fragment) already share, so `src`, `href`, inline `style` urls, and `<style>` blocks all get the same rule:

1. `../` paths keep resolving against the composition dir (unchanged, shared with the producer's inliner so preview and render agree).
2. Any other relative path is re-pointed at the composition's directory **only when that sibling file exists on disk**.

The disk check is what keeps the two conventions apart: registry blocks are installed into a subdirectory but reference project-root assets (`assets/logo.png`), which are already correct under the root base and have no sibling on disk, so they are left untouched.

Not changed: the `#1c2028` transparent-body fallback in the thumbnail generator. It is correct for genuinely transparent compositions; the illegibility was a downstream symptom of the 404.

## Test plan

- [x] Unit tests added/updated — two tests in `subComposition.test.ts`: a red-first regression guard for the sibling `<link>` / `<img>` / `url()` case, and a guard that project-root-relative refs with no sibling on disk stay untouched.
- [x] Manual testing performed — reproduced the dark thumbnail on a generated project (21-file and 2-file variants both reproduce), then confirmed the same URL renders the white-to-lavender gradient with legible text after the fix, with no 404 in the network log.
- [x] `packages/studio-server` suite green: 29 files / 402 tests. Lint, format, typecheck clean.
- [ ] Documentation updated — n/a
2026-08-04 04:09:50 +02:00
James Russo 1e51eaec2c chore: release v0.7.90 (#2958) 2026-08-02 21:57:19 -07:00
Miguel Ángel 411ada0d90 chore: release v0.7.89 (#2953) 2026-08-02 20:25:14 +02:00
Miguel Ángel 59fe32b633 Merge pull request #2952 from heygen-com/fix/2951-ignore-generated-cache-watcher
fix(cli): ignore generated caches in project watcher
2026-08-02 19:51:43 +02:00
Miguel Ángel 1d01b9f2cf fix(studio): exclude generated caches from project metadata 2026-08-02 17:37:51 +00:00
Vance IngallsandClaude Opus 5 6f0df2640b fix(cli,studio,core): close five R5 telemetry and canary findings
- A long-lived preview cached its telemetry posture in two places
  (readConfig and shouldTrack). Running `telemetry disable` in another
  terminal left it resolving canaries and injecting the CLI id for hours.
  Both caches are now dropped together at a request boundary.
- Studio minted and shipped a telemetry id for every render regardless of
  the browser profile's opt-out, and the server emitted the outcome under
  CLI policy, which cannot see localStorage or DNT. The browser now sends
  an explicit telemetryOptOut, distinct from an old client's omission.
- Any non-empty HYPERFRAMES_PREVIEW_HOST disabled the DNS-rebinding guard,
  so even a loopback bind accepted a hostile Host. The guard now holds for
  loopback binds and, on a LAN bind, admits only names this machine
  answers on.
- sunsetAfter had no reader of the current date. A scheduled workflow runs
  scripts/check-canary-sunset.ts weekly, so a failure lands on the
  rollout's owner rather than on an unrelated PR author.
- The install-state seed memo outlived `rm -rf ~/.hyperframes`,
  resurrecting a cleared cohort. Removed; it only saved a read on a
  readConfig cache miss.

Docs updated for the Host rule and the 100% exclusion carve-out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 17:21:41 -07:00
Vance Ingalls 74fadf69c4 chore: release v0.7.88 2026-08-01 16:53:08 -07:00
Miguel Ángel 89e970fbd7 chore: release v0.7.87 (#2935) 2026-07-31 22:36:46 +02:00
Somansh ReddyandClaude Opus 4.8 5a6e4b1a8f refactor(lint): consolidate asset-src placeholder skip into a shared predicate (#2894)
* refactor(lint): route asset-src skips through a shared isUnresolvedAssetPlaceholder predicate

Follow-up to the templating-token fix. The __UPPER__ + templating-token skip was
copy-pasted across the asset-src sites and had drifted: two non-lint sites carried only
the __UPPER__ half, and htmlCompiler's comment still claimed it "matches lint's skip"
after lint's skip became a superset. Extract one isUnresolvedAssetPlaceholder(rawSrc) in
@hyperframes/parsers/asset-resolution (both placeholder shapes, checked on the raw value)
and route every site through it: the four project.ts lint sites, hevcPreviewLint, and the
two previously-missed post-substitution sites (studio-server mediaCodecMap, producer
htmlCompiler). Remote/inline handling stays per-site (audio uses a narrower check).
Behavior-preserving for the lint sites (full suite green); the two non-lint sites are
post-substitution so they don't false-positive today, but now share one definition and
can't drift again. Adds unit tests for the predicate.

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

* docs(parsers): refresh hasUnresolvedTemplatingToken aside for the shared predicate

The parenthetical said the __UPPER__ shape keeps its own inline check at each call
site; this branch folded it into isUnresolvedAssetPlaceholder, so point there instead.
Addresses review nit.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 14:52:10 -07:00
Vance Ingalls 12ee861123 chore: release v0.7.85 2026-07-30 10:38:10 -07:00
Vance Ingalls 065293ecf3 chore: release v0.7.84 2026-07-30 01:39:54 -07:00
Vance Ingalls 5244dde5f1 chore: release v0.7.83 2026-07-29 18:37:58 -07:00
Miguel Ángel b904343949 chore: release v0.7.82 2026-07-29 11:25:04 +00:00
James 0d42d65525 chore: release v0.7.81 2026-07-29 06:11:11 +00:00
Miguel Ángel 5829515932 chore: release v0.7.80 2026-07-29 02:22:54 +00:00
Miguel Ángel a68729bcf1 chore: release v0.7.79 2026-07-28 23:06:45 +00:00
Vance Ingalls c691869e22 chore: release v0.7.78 2026-07-28 05:27:29 -07:00
Miguel Ángel 3c857d768b chore: release v0.7.77 2026-07-28 02:12:41 +00:00
Miguel Ángel 07e63b3758 Merge pull request #2791 from heygen-com/codex/studio-timeline-b-expanded-lanes-v2
feat(studio): wire expanded keyframe timeline lanes
2026-07-28 02:37:23 +02:00
Miguel Angel Simon Sierra d57039882f fix(studio): harden keyframe editing semantics 2026-07-28 00:13:46 +02:00
James 5cad2bc312 chore: release v0.7.76 2026-07-27 05:08:40 +00:00
James 45b458c007 chore: release v0.7.75 2026-07-27 03:38:16 +00:00
James Russo 0cc78c1d42 chore: release v0.7.74 (#2818) 2026-07-26 21:57:54 -04:00
James 527751c871 chore: release v0.7.73 2026-07-26 21:25:03 +00:00
James Russo 72ae39e3d0 chore: release v0.7.72 (#2800) 2026-07-26 02:01:10 -04:00
James d07d1874b6 chore: release v0.7.71 2026-07-24 19:03:16 +00:00