Repository navigation
fix(cli): a credit limit the person names holds on every paid media call - #7145
Conversation
There was a problem hiding this comment.
Verdict: REQUEST CHANGES · 4 blocker · 1 important · 0 nits · head 89452339
-
blocker (PR body): this is a fix PR, but it has no
fails without the fix:line naming a test. "Two of the three newrunJobtests fail" doesn't say which tests. Add afails without the fix:line that names at least one by file and title. -
blocker:
packages/cli/src/heygen/v3.ts:272-273: a resume can wipe out a live job's reservation.creditsSpentkeeps the last credits each key logged, and a resumed key skipswithinLimit. AnyRefusedon that resume then logscredits: 0for the key. That includes theKEEPS_KEYstatuses (401/403/408/409/425/429), where the original job may still be running and charging. I reproduced it with limit 100:k1for 100 loses its answer (socket hang up), so the job may have started and the reservation stays.- The same command is re-run and gets 429. The ledger total drops to 0.
- A new
k2for 100 is sent. That is 200 against a limit of 100.
Fix: release the reservation only when this call made it and
!KEEPS_KEY.has(err.status). -
blocker (Rule 2):
skills/media-use/audio/scripts/lib/tts.mjs:315(synthesizeHeygen) still POSTs/voices/speechwith the HyperFrames credential, and no limit check is on that path. It is reached from the documented audio engine (audio/scripts/audio.mjs:153,references/audio.md) and fromheygen-tts.mjs. This PR treatsresolve --type voiceas a paid HeyGen voice and refuses it under a limit, so this path needs the same refusal. Otherwise the limit still doesn't hold "on every paid media call". -
blocker (CI): the required Windows lanes fail because preflight lint fails:
✗ media-use/SKILL.md is 5282 B, over its 5120 B budget. The rewritten Credits paragraph needs to lose about 160 B. -
important: the optional Comments ratchet fails.
packages/cli/src/heygen/v3.tscomment share went from 6.4% to 6.8% (+4 lines), and the job reports 3 problems in total.
What I checked
- Tests at head:
v3.test.ts27/27 andcredit-limit.test.mjs3/3. - No double-counting within one process:
withinLimitruns afterawait jobSender(), and its read/check/append is synchronous. - Failures that keep the reservation: a client-side timeout or a 5xx comes back as a plain
Fix, notRefused, so its reservation correctly stays. - Callers:
generate.ts:680andtranscribe.ts:80both passlog.projectDir, so therunJobcheck covers both. - Engine guards:
voice-provider.mjsandheygen-video-provider.mjsrefuse before runningheygen. They fall back toprocess.cwd()whenctx.projectDiris absent.
There was a problem hiding this comment.
Verdict: REQUEST CHANGES · 4 resolved, 0 open, 1 new · 89452339 → 0b58b73b
- blocker (new, required
Typecheck):packages/cli/src/heygen/v3.ts:225still declareswithinLimit(...): void, but it now returnstrue.tsc --noEmit -p packages/clifails withv3.ts(245,3) TS2322: Type 'boolean' is not assignable to type 'void'andv3.ts(271,9) TS1345: An expression of type 'void' cannot be tested for truthiness. Change the return type toboolean, and have the early exitsreturn false.
The four earlier blockers are all fixed: the body names the tests, a resume keeps its reservation, synthesizeHeygen refuses under a limit, and SKILL.md is 5104 B. Tests at this head: v3.test.ts 30/30, tts.test.mjs plus copy-parity 18/18. The optional Comments check still reports 4 problems.
A limit was only a per-call --max-credits flag that each caller had to remember, so kit tools, voice resolve and transcribe could spend past it. The limit now lives in the project's credit-limit file and runJob, which every paid call goes through, refuses a new job past what is left (unpriced calls included). Voice and avatar video resolved through the heygen CLI are refused under a limit, since they cannot be priced there.
…he audio engine's voice A resumed job refused with a status that says nothing about the job released its reservation while the job may still be running. Only the call that reserved releases, and only for a refusal that rules the job out. The audio engine's HeyGen speech call now refuses under a limit too.
0b58b73 to
db47a53
Compare
There was a problem hiding this comment.
Verdict: APPROVE · 1 resolved, 0 open, 1 new (important) · 0b58b73b → db47a539 (rebased)
- important (optional
Fallow audit):packages/cli/src/heygen/v3.ts:249:runJobhas cognitive complexity 18, over the threshold of 15, after thereservedrelease condition was added. Either pull the catch-block bookkeeping into a helper, or use the// fallow-ignore-next-line complexitymarker already oncall. The optional Comments check is also still red. Neither is a required check.
What I checked
- Rebase:
git range-diffshows the first two commits unchanged apart from the regeneratedskills-manifest.jsonhunks. After those come1048760b(withinLimit(): boolean, early exits returnfalse) anddb47a539(manifest regenerated, which picks up #7144'sstyle-samplinghash). - Typecheck:
tsc --noEmit -p packages/clireports nothing inv3.ts. The only remaining errors come from the unbuilt@hyperframes/inspectorin my local sandbox. - Tests:
src/heygen118/118,credit-limit.test.mjs3/3, andtts.test.mjs+ copy-parity + core-skills 28/28.skills/media-use/SKILL.mdis 5104 B, under its 5120 B budget. - CI when I posted:
preview-regression(which includes preflight),regression,Player perfandPR captureswere green. TheCIworkflow, includingTypecheck, and the Windows lanes were still running.
There was a problem hiding this comment.
Verdict: APPROVE · 1 resolved, 0 open, 0 new · db47a539 → 18bddeb4. runJob now carries the same fallow-ignore-next-line complexity marker as call. The refused-call branch is restructured with the same logic: the reservation is released only when this call made it and the status is outside KEEPS_KEY. .hyperframes/credit-limit.json is added to the citation checker's project-file allowlist. v3.test.ts passes 32/32, with no tsc errors in v3.ts. The optional Comments ratchet is still red: v3.ts is 6.4% against 6.2% at the base, +3 lines. This does not block.
What
A credit limit the person names now holds on every paid media call, not only the calls that remember
--max-credits.Why
Today the limit is a per-call flag, checked in the
media-use generatecommand, so anything else that spends skips it: kit tools that callgeneratewithout the flag,transcribe, and a HeyGen voice or avatar video made throughresolve. One run stayed under its limit only because the agent edited its own copy of a kit tool.Related work
None open on this.
How
.hyperframes/credit-limit.jsonas{"credits": N}. The skills tell the agent to write it once when the person names a limit.runJob(packages/cli/src/heygen/v3.ts) is the one function every paid HeyGen job goes through (generateandtranscribe). Before sending, it reads the file and what earlier jobs in the project committed (from.hyperframes/heygen-jobs.jsonl, which now records each job's credits, reserved before sending) and refuses a new job that does not fit. A call that cannot be priced ahead (transcribe) is refused under a limit, same as--max-creditsalready did. A resumed job (same key) is not charged twice; a refused call gives its credits back.synthesizeHeygen) refuses under a limit too.--max-creditsstays as a tighter per-call limit.resolve --type voiceand avatar video shell out to theheygenCLI, which has no price here, so under a limit they refuse and point atmedia-use generate, which is priced and counted.credit-limit.mjs) used by both sides.Not covered:
reverse starttalks to a different service and is not a media call.Search first: no existing run-level limit, budget or spend ledger in
packages/cliorskills(grep forbudget,max-credits,creditbefore this change found only the per-call flag).Test plan
v3.test.ts(refuses a call over what is left with no--max-credits, refuses an unpriced call, a resumed job passes, a refused call's credits come back) andcredit-limit.test.mjs(file parsing, spend read-back, voice refused before theheygenCLI runs). fails without the fix:v3.test.ts"refuses a call over what is left, before sending, with no --max-credits given" and "refuses a call that cannot be priced ahead" (check inrunJobremoved); "keeps a lost job's reservation through a resume refused with 422" and "keeps the reservation of a fresh call refused with 429" (release rule changed).Ran on a remote Linux box:
src/heygenandsrc/commands/media-usevitest, 6 files, 119 passed; lint, format and skills-manifest checks clean.Size
Over 100 lines because the limit reader, the check in
runJob, the engine guard and their tests have to land together; any one alone leaves a paid path unguarded.