Skip to content

Integrate current upstream into fork while preserving Flow API fixes - #5

Draft
Bl0ck154 wants to merge 98 commits into
mainfrom
integration/upstream-20261027
Draft

Bl0ck154 wants to merge 98 commits into
mainfrom
integration/upstream-20261027

Conversation

@Bl0ck154

@Bl0ck154 Bl0ck154 commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Goal

Bring the fork's main up to upstream crisng95/flowkit@19cb87d without losing the fork's existing 39 commits and its self-hosted Flow API work.

This is an integration candidate, not a production deployment. Merge commit efc953c has both upstream 19cb87d and fork 7be6093 as parents.

Integration and compatibility fixes

  • Applied the complete updated upstream tree and preserved non-conflicting fork changes; resolved 35 overlapping paths with upstream as the default, then reviewed/repaired semantic regressions.
  • Restored the fork's native 1080p video export path (p0UkFb) and the /api/flow/export-video / check-export-status APIs.
  • Fixed FlowClient active-project fallback and missing/overridden generation hold/cooldown metadata.
  • Fixed credit inspection API (get_credits(refresh=...)) and ensured an unavailable account balance is not reported as zero.
  • Removed stale duplicate Omni functions and legacy REST branching that broke imports after upstream removed USE_BATCH_RPC.
  • Fixed early video validation in contact sheet generation.
  • Preserved upstream FUNDING metadata and reconciled misleading README migration/upscale statements.
  • Added integration smoke tests and docs/FORK_SYNC.md to describe safe separation from production.
  • Restored the fork's optional Playwright native-composer and CDP transport (FLOW_BROWSER_BACKEND=playwright_ui), with fresh-upload picker caching. Standard extension transport stays the default.
  • Fixed four lost Flow API imports affecting browser status, session recovery, account inspection, and image model capabilities. Added 12 offline regression tests for those paths and transport selection.
  • Brought native export and Omni polling into alignment with upstream PR Restore native 1080p export on migrated Flow API crisng95/flowkit#40: one shared flow_poll.py for both; POST /api/flow/check-status supports mode=export; the old check-export-status and check-upscale-status routes are retained as compatibility aliases. Project-less exports now resolve the leased session project.

Validation

  • 539 passed, 2 skipped across the available tests/unit suite (excluding test_youtube.py; optional googleapiclient is missing from the production host's existing Python venv).
  • Latest targeted run: 88 passed, including shared polling, native video export, and compatibility API regression tests.
  • Imported agent.main.app, generated OpenAPI successfully, and verified 101 API paths, including export, credits, upscale and upstream routes.
  • Python syntax, node --check for extension JS, and git diff --check passed.
  • GitHub Actions on commit 64adb1d: Python 3.10 and 3.13 both passed (run 40726928013). The runner workflow now skips preinstalled ffmpeg/fonts and bounds apt setup; same correction was proposed upstream in PR #82.
  • No live Google generation or video export was attempted in the integration candidate.

Deployment safety / deliberately deferred

  • Do not update the production /opt/flowkit checkout directly; it has 12 modified tracked files and local untracked source/runtime data.
  • The separate remote Flow worker and its browser extension are backed up on the host but not part of this PR. They need a dedicated security and compatibility audit before any public commit.
  • Do not merge/deploy until browser session routing, Flow account safety holds, and representative image/video/export flows are validated outside production or in a controlled smoke test with rollback.
  • This does not modify upstream PR Restore native 1080p export on migrated Flow API crisng95/flowkit#40.

hieuh0 and others added 30 commits August 28, 2026 12:05
Port the jHPbke (create) and QI2zvc (delete) RPCs captured off the Flow UI:
- flow_batch: RPC_CREATE_PROJECT/RPC_DELETE_PROJECT, request builders, and
  read_created_project_id (slot 0, uuid fullmatch, raises otherwise).
- flow_client.create_project mints a project via jHPbke when unpinned (pin still
  reused first); rejects non-default tool_name on batch; delete_project via QI2zvc.
- projects DELETE route: preflight get_project (404 before any RPC), remote-first
  delete of every project except the pinned/shared one, local row kept on failure.
Verified: 100 unit tests + a live create/delete round trip on Flow.
A pinned or request-supplied Flow project id may already have a local
row; the bare INSERT then raised UNIQUE constraint failed and surfaced
as a 500. Return the existing row when the id is already tracked.
feat: OmniVoice TTS từ xa + tạo/xoá Flow project trên batchexecute
…ion-deploy

fix(extension): prevent idle Flow tab leaks
…nivoice-tts

Revert "feat: OmniVoice TTS từ xa + tạo/xoá Flow project trên batchexecute"
Port Omni Flash text-to-video to migrated Flow API
The batch builders are about to gain a caller: Omni will submit through the
same generate_video that Veo uses, differing only by a model key. That refactor
can drop a trailing null and still look fine — Flow accepts a malformed payload
and then ignores it, which reads exactly like success.

test_flow_batch.py asserts positions, and positions survive that. So assert the
whole string instead. Checked by flipping SURFACE_ID 22 -> 23: all three goldens
fail, and all 48 position tests pass.

Also adds requirements-dev.txt — the test deps were undeclared, so a fresh
checkout could not run the suite that guards any of this.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DB27wvPQ195KGiQ6VZfCDf
test(flow): lock the wire envelopes before consolidating the Flow API surface
Expand migrated image API: model selection, edits and 2K export
Flow moved to flow.google.com in September 2026 and stopped minting the
`Bearer ya29.…` the aisandbox-pa REST path needed. That path has been
unreachable ever since, kept behind USE_BATCH_RPC=0 as a post-mortem tool.

Keeping it cost more than it was worth. A dead implementation sitting beside
a live one is a template: the Omni module was written against it, so when the
migration landed on FlowClient it missed Omni entirely, and Omni frame and
reference generation have been returning UNSUPPORTED_ON_BATCH_API since.
Deleting the template is what stops the next copy.

Removed: the ten _legacy_* methods, the twelve `if not USE_BATCH_RPC` branches
in FlowClient (ten dispatching to those methods, one returning an inline note
from refresh_project_urls, and upscale_image's, which arrived with the migrated
image API this rebased onto), _build_url/_client_context, ENDPOINTS,
GOOGLE_FLOW_API, GOOGLE_API_KEY, the browser-fingerprint pools and
agent/services/headers.py that fed them, the tRPC polling branch in omni_flash,
and omni_flash's _annotate_polling, whose only callers were the REST submits.
RECAPTCHA_SITE_KEY went too: it had no Python caller, and the extension
hardcodes its own copy in injected.js, so the env var was a knob that changed
nothing. Git history has all of it if a payload is ever needed.

One deliberate behavior change, in the unported Omni modes. Previously the
capability check ran first, so a bad duration on frame-to-video answered 502
UNSUPPORTED_ON_BATCH_API. Now validation runs first, so it answers 400
"duration 5s is unsupported". A caller who can fix the duration should hear
about the duration; the gap it cannot fix comes after. No caller exists today
either way — the pipeline uses model_family=veo.

The extension keeps its api_request and trpc_request handlers, so an extension
updated ahead of its agent still serves an older one. To be clear about what
that is worth: on any profile that postdates the migration nothing in the
extension reaches aisandbox-pa either — sendTelemetry early-returns without a
flowKey that is no longer minted. So the host permission is held by the
backward-compat proxy alone, and the two now share one lifetime: when the
oldest agent in the wild speaks batch_rpc, the handlers and manifest.json's
aisandbox-pa entry go together. That is a manifest bump and a user-visible
reload, so it is its own change.

Tests: test_omni_flash.py was pinned to the legacy path by a module-level
autouse fixture setting USE_BATCH_RPC=False, so all 34 of its tests would have
errored on the missing attribute. 10 asserted REST or tRPC contracts for code
this commit deletes and went with it; the remaining 24 — the model-key mapping,
the input validation, the batch-path tests — were kept, and one new test pins
the seven-reference boundary that a deleted test used to cover.

Two tests added for the retry branch in generate_scene_video. It used to be
gated on `bare_uuid and not USE_BATCH_RPC`, so it was dead on the only
transport that exists and had no coverage — and it is the one branch where a
wrong answer abandons a render Flow is already paying for and starts a second.
Checked by forcing the branch off: the test fails. A comment in operations.py
that described that resubmit was removed too; left in place it reads as an
instruction to reintroduce it.

  agent/services/flow_client.py   1290 -> 921
  agent/services/omni_flash.py     672 -> 350
  agent/config.py                  158 -> 111

211 passed, 3 failed — the 3 are pre-existing on main (test_processor.py
patches processor._retry_state, which does not exist). Golden envelope tests
pass, so the Veo submit payload is unchanged byte-for-byte.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DB27wvPQ195KGiQ6VZfCDf
Six tests failed for reasons unrelated to the code under test, and one of
them hung the interpreter.

test_processor.py patched `agent.worker.processor._retry_state`, a module
global that no longer exists: retry state became the instance attribute
`self._retry_after` plus an explicit `retry_after` parameter on
_handle_failure. mock.patch refuses a missing attribute without
create=True, so all three tests raised AttributeError before their bodies
ran. The assertions were always fine — _handle_failure(rid, req, result)
still matches the signature — so the patch line just goes.

test_result_handler.py mocked crud.update_scene but not crud.get_scene,
which c071cb8 added at result_handler.py:61 for the chain cascade. Since
April those three tests hit a real database with no schema, hence "no such
table: scene". They now mock get_scene returning None — the no-parent case,
which keeps update_scene's assert_awaited_once true.

That accidental connection was also why `pytest tests/unit` never exited:
aiosqlite services each connection on a non-daemon worker thread, and this
one was never closed, so the thread outlived the session. Piping the run
hid it, because the summary sat in the pipe buffer while the process
refused to die.

266 passed, and the process exits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5BJVhNgerhFTUgmfnKTSj
A live 2K export failed with `SPrCad: NO_INJECTION_RESULT` and the taxonomy
had no entry for it, so diagnosing it meant reading background.js from
scratch — the one thing this skill exists to prevent.

The string comes from extension/background.js:540, where executeScript
resolved with no frame result: the Flow tab was gone or still booting, so
the envelope never ran. It landed on the first captcha-bearing RPC after an
agent restart and cleared on retry.

The row records what the evidence ruled out, because all three are easy to
reach for and wrong: a failed captcha mint reports CAPTCHA_FAILED, an
oversized payload is not it (MAX_RPC_TEXT is 32 MB), and a structured-clone
failure surfaces as 500 rather than 502. Auto-handling is the generic
branch in processor.py — retried with backoff, counted against MAX_RETRIES
— not the captcha branch's ten free attempts.

Also added to the auto-invoke trigger list, so the next one routes here
instead of being guessed at.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5BJVhNgerhFTUgmfnKTSj
43b0406 changed background.js and injected.js without bumping the manifest,
so /health reported 0.3.1 for both the pre- and post-merge build. There was
no way to tell a stale extension from a current one — which is the first
question you ask when an RPC misbehaves. background.js:229 reports
getManifest().version, so the bump makes it answerable; reload the
extension once and /health says 0.3.2.

README gets the status at the top, where someone landing on the repo sees
it before anything else: the batchexecute transport is verified end to end
against the live API — image generation, 2K export and i2v — and the four
unported capabilities fail loudly rather than guessing.

That list is taken from the guards themselves, not from the docs, because
the docs disagreed. flow_client.py blocks start+end chaining, r2v and video
upscale; omni_flash.py:33 blocks Omni frame/reference. CLAUDE.md calls the
third one "4K upscale", which reads as the image export — and that one in
fact works, verified at 1536x2752 from a 768x1376 source. CAPTURE.md lists
a fourth item the image API merge has since ported. The Architecture
callout's v0.3.0+ was aligned to v0.3.2+ so the two do not contradict.

Also ignore extension/_metadata/, which Chrome generates when it loads the
unpacked extension.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5BJVhNgerhFTUgmfnKTSj
…batch API

Closes the gap the REST removal documented: Omni's image-conditioned modes were
written against the pre-migration REST endpoints, so the migration to
`FlowClient` missed them and they have been answering
`UNSUPPORTED_ON_BATCH_API` since. crisng95#48 captured their batchexecute payloads —
`eb1hJf` (first frame), `nprQif` (first+last), `MZZa6b` (references) — and this
merge lands them.

crisng95#48 was cut from a49831f, before the REST transport was removed, and preserved
the dual-transport shape on purpose: "the legacy REST implementations are kept
below for non-batch deployments." On current main that half does not merely
conflict, it does not import — `agent.config.USE_BATCH_RPC`,
`agent/services/headers.py` and `FlowClient._build_url`/`_client_context` are
all gone. So the resolution keeps crisng95#48's batch submits byte for byte, including
the captured envelopes and the 360p model-key suffixes, and deletes the REST
half rather than trying to revive it:

- dropped the REST bodies of `_submit_omni_frame_video` and
  `generate_omni_flash_video`, the tRPC poller behind `check_omni_flash_status`,
  `_annotate_polling`, `_fetch_project_initial_data` and `_fetch_media_url`
- unwrapped the two surviving `if USE_BATCH_RPC:` blocks and removed the flag's
  five imports and four references
- dropped both `model_key = _load_model_key(...)` assignments: their only
  consumer was the REST body's `videoModelKey`. `_load_model_key` itself stays,
  tested directly, and `models.json` still carries all three modes
- 709 lines to 417; imports are back to main's set

Tests needed the same treatment, and two of them needed judgement rather than
deletion. crisng95#48's suite was pinned to the REST path by a module-level autouse
fixture setting `USE_BATCH_RPC=False`, so several tests that never name the flag
still exercised the old poller. Dropped the seven REST/tRPC wire-contract tests;
converted `..._returns_signed_url_without_buffering_video` and
`..._missing_media_is_pending` onto the batch poller, because what they pin
still matters — the poller must hand back Flow's signed url instead of
buffering the clip, and a media record without a `/video/` url is pending, not
done. Dropped `..._requires_project_id_for_legacy_descriptors`: the batch poller
deliberately resolves a missing project id instead of refusing.

The seven-reference boundary existed on both sides in a form that no longer
applies — main asserted it reached the capability gap, crisng95#48 asserted it through
the REST body — so it is rewritten against the batch submit. Seven is the limit,
not one past it, and with r2v live it has to reach the wire or an off-by-one in
the validator reads as a Flow refusal.

Docs followed the behaviour, since the merge falsifies claims main shipped
yesterday: README's note and capability table, CLAUDE.md, and the
`UNSUPPORTED_ON_BATCH_API: Omni Flash` row in `/fk-doctor`, an error string
nothing can produce any more. The unported set is now three, all on the Veo
path, and Veo chaining/r2v callers get pointed at the Omni modes that cover the
same shot. CLAUDE.md's "4K upscale" was corrected to video upscale while there:
it read as the image export, which works.

272 passed, up from 266, and the suite exits.

Not verified: the captured envelopes themselves. crisng95#48 reports them live-verified
on 2026-09-14 and the tests lock their shape, but proving the wire format needs
a real submit through a signed-in Flow tab, which this merge did not do.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5BJVhNgerhFTUgmfnKTSj
Merge crisng95#48: restore Omni frame/first+last/reference on the batch API
The sponsor block sat between the badges and the project title, above
everything that explains what Flow Kit is — including the note admitting it was
put there because nobody scrolls past the badges. Removed, along with the two QR
images it was the only referrer of; `git log` still has them if it comes back.

`.github/FUNDING.yml` is left alone: `ko_fi: crisnguyen95` drives GitHub's own
Sponsor button on the repo page, which is a separate surface from README copy
and its own decision.

The changelog goes before `## License` rather than at the top, so it does not
push Quick Start down the page. Entries are taken from merge commits and tag
dates, not written from memory, and the September rows are one arc: Flow moved
to flow.google.com, so the transport was rebuilt, the REST path deleted, and the
Omni modes ported back on top of it. Links are relative (`../../pull/N`) because
the badges point at tuannguyenhoangit-droid/google-flow-agent while origin is
crisng95/flowkit — a relative link resolves correctly on either.

Also ignore `.omc/`, the oh-my-claudecode session state. It is tooling output,
not project content, and without this a plain `git add -A` sweeps ~800 lines of
it into the index.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5BJVhNgerhFTUgmfnKTSj
claude and others added 28 commits September 30, 2026 16:07
Flow Kit -> Muse2APIProvider -> muse2api -> muse.ai. A new `muse2api`
MediaProvider renders scene/reference images and i2v video through a
muse2api gateway (https://github.com/crisng95/muse2api):

- agent/services/muse2api_client.py: async HTTP client for
  /v1/images/generations, /v1/videos (+ task polling) and /v1/media,
  Bearer auth, OpenAI error bodies surfaced as "muse2api <code>".
  Input frames are sent inline as data URLs so the gateway can run on
  another host.
- agent/sdk/services/muse2api_provider.py: translates ProviderJobs,
  downloads results into output/_shared/muse2api/ as file:// URLs with
  minted UUID media_ids (Flow-shaped results, pipeline unchanged).
  Chained end frames and r2v fail loudly unless MUSE2API_ALLOW_DEGRADED=1;
  edit/upscale/audio are not offered.
- Registered in ProviderRegistry; available once MUSE2API_URL is set.
- Config, README, fk-provider/fk-doctor and generation skills updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MfvQsdxJhWw15u6Zm51YJM
Spell out that the muse2api gateway is a separate service Flow Kit does
not ship or start: clone crisng95/muse2api, configure the browser driver,
start it, import a muse.ai account, verify /readyz, then point Flow Kit
at it with MUSE2API_URL / MUSE2API_KEY. Adds a pointer from Quick Start
and the env-var table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MfvQsdxJhWw15u6Zm51YJM
…locked")

Two coroutines hitting get_db() before the connection existed both saw
None and both connected; the second overwrote the global, so the first
ran its PRAGMAs (including wal_checkpoint) on the second's connection
while a statement was open on it. That failed intermittently with
"sqlite3.OperationalError: database table is locked" in
test_assistant_provider on CI (Python 3.10, also on main in run crisng95#33) and
leaked a connection whose worker thread can keep the interpreter alive.

Connect under a per-event-loop init lock (separate from _db_lock, which
crud holds while calling get_db), set up a local, and publish it only
when ready. Adds a regression test counting connect() calls under
concurrent first callers; it fails without this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MfvQsdxJhWw15u6Zm51YJM
…-1567a3

Add muse2api media provider for muse.ai rendering
…id-vowel

OmniVoice returns audio that stops at the last voiced sample: zero release
tail, final vowel clipped. Any pipeline that trims video to narrator length
then cuts exactly where the voice is still ringing, which reads as an abrupt
scene change. Fade the last 60 ms and append 0.4 s of silence inside the
save path so every wav ends cleanly and downstream trims get a breath.
A scene whose narration runs 7.6s cannot breathe inside an 8s clip; the
only post fix is a frozen last frame, which reads as a glitch. Pass
scene.duration into job.extra and round it UP to the next supported Omni
Flash step (4/6/8/10) in the provider — rounding down would clip the
voice. Veo ignores the field. Defaults unchanged.
fix: resolve file:// URLs correctly on Windows
Comprehensive System Bug Fixes & Windows Cross-Platform Hardening
…, and production skills

- Add 5 new production skills: fk-scriptwriter, fk-character-bible, fk-action-choreography, fk-cinematic-transitions, fk-sound-design
- Add scripts for film mastering (build_master_film), hardsub burning (generate_subtitles), series manifest (series_manifest), and SFX layering (sfx_layering)
- Add sync_skills and validate_skills for tool parity across Antigravity IDE and Claude Code
- Upgrade fk-concat-fit-narrator with bumper transitions, audio ducking, and hardsubs
- Upgrade fk-gen-narrator with Edge-TTS neural voice profiles and pacing controls
- Upgrade fk-review-video with automatic prompt healing loop
- Fix manifest synchronization in agent/api/projects.py to merge all chapters
- Fix polling timeout guard in agent/worker/processor.py
- Support Antigravity IDE target in setup.py (.agents/skills)
- Clean up git tracking for large scratch dumps and update .gitignore
feat(skills,pipeline): add cinematic series tooling, per-project manifest manager, and production skills
…NO_BANANA_2 nickname

Banana 2 (NARWHAL) answers [5] NOT_FOUND on ogiZ0b in every project since
early October 2026. BELUGA (the UI's beluga_display) is its successor and
generates normally on the batch path.

- IMAGE_MODELS accepts BELUGA and BELUGA_THINKING_{LOW,MED,HIGH}.
- NANO_BANANA_2_1 -> BELUGA; NANO_BANANA_2 now lands on BELUGA too. NARWHAL
  still passes through when named explicitly.
- Default image model is unchanged (NANO_BANANA_PRO / GEM_PIX_2).

Verified live on 2026-10-24: BELUGA 200, NANO_BANANA_2 via the new mapping
200, NARWHAL [5]. BELUGA_THINKING_* not exercised live.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The provider dropped every reference image on the assumption that the
gateway took none. It does: /v1/images/generations accepts `image` as a
data URL or a list of them, and the browser driver attaches them to the
muse.ai chat. Scenes rendered through muse2api therefore lost their
characters.

- Muse2APIClient.generate_image takes `images` and sends them as `image`,
  capped at MAX_IMAGE_REFS (4).
- The provider dedupes the job's refs, keeps the first four (logging the
  rest) and inlines them as data URLs.
- An unreadable or unreachable ref now fails the job instead of rendering
  the scene without its character; httpx errors on input fetch are caught
  like OSError/ValueError (this also covers a video's start frame).
- README and /fk-provider describe the new behaviour and the billing note
  for gateways with wallets enabled.

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

Issue crisng95#67 reports accounts whose manual Flow UI generation was blocked after
FlowKit testing; retrying into the block is a suspected cause.

- The generation hold after PUBLIC_ERROR_UNUSUAL_ACTIVITY is written to
  flow_generation_hold.json and restored on start, so a restart no longer
  lifts it. FLOW_UNUSUAL_ACTIVITY_COOLDOWN_S defaults to 3600 (was 120).
  A shorter hold never replaces a longer one or its reason.
- [HIJACK] extension_hijack_detected was requeued forever without counting;
  it now fails once retry_count reaches 3.
- Any "unusual_activity" spelling is terminal; a bare code no longer falls
  into the 10x reCAPTCHA retry loop.
- POST /api/flow/clear-hijack clears the file too; /api/flow/status reports
  the hold's reason.

Takes the persistent-hold and bounded-retry ideas of crisng95#68 onto the current
batch transport.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fix(flow): keep the unusual-activity hold across restarts, bound hijack retries
feat(image): add Nano Banana 2.1 (BELUGA), retire NARWHAL from the NANO_BANANA_2 nickname
feat(muse2api): send entity reference images with scene images
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
docs: account-safety hold and BELUGA image model
Merge the full upstream history with fork changes, resolving overlapping code in favor of upstream and restoring regression-tested Flow API features. Preserve production remote-worker changes separately.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants