cozyclay 2.1.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (585) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +43 -116
  4. package/bin/agent/agent-runner.mjs +14 -15
  5. package/bin/agent/cliproxy-provider.mjs +135 -0
  6. package/bin/agent/credential-store.mjs +30 -7
  7. package/bin/agent/pi-tools.mjs +4 -1
  8. package/bin/agent/provider-keys.mjs +32 -0
  9. package/bin/agent/provider-login.mjs +110 -0
  10. package/bin/agent/provider-routes.mjs +99 -0
  11. package/bin/agent/providers.mjs +19 -97
  12. package/bin/agent/script-tool.mjs +17 -4
  13. package/bin/agent/session-store.mjs +2 -2
  14. package/bin/agent/studio-context-budget.mjs +93 -0
  15. package/bin/agent/studio-motion-reference.mjs +23 -0
  16. package/bin/agent/studio-prompt.mjs +1 -1
  17. package/bin/agent/studio-rebase-text.mjs +32 -0
  18. package/bin/agent/studio-rebase.mjs +26 -39
  19. package/bin/agent/studio-tools.mjs +9 -28
  20. package/bin/agent/tool-arguments.mjs +49 -0
  21. package/bin/agent/turn-frames.mjs +66 -0
  22. package/bin/codex-auth.mjs +9 -2
  23. package/bin/cozyclay.mjs +10 -116
  24. package/bin/live/cli.mjs +3 -3
  25. package/bin/mcp-runtime.mjs +2 -12
  26. package/bin/package-signature.mjs +0 -4
  27. package/dist/app/index.html +7 -6
  28. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  29. package/dist/assets/analytics-BWCqdgWA.js +1 -0
  30. package/dist/assets/app-DfTCxaKk.js +5087 -0
  31. package/dist/assets/app-f-fkV5uP.css +1 -0
  32. package/dist/assets/compile-worker-BxKXh07s.js +5 -0
  33. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  34. package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-DNO8kv_N.js} +1 -1
  35. package/dist/assets/{landing-ChUvB_zL.js → landing-yfZaJkWc.js} +1 -1
  36. package/dist/assets/scene-asset-sync-U3kS2rvK.js +20 -0
  37. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  38. package/dist/assets/workflow-D78eiKTa.js +179 -0
  39. package/dist/cozyclay-package.json +1 -1
  40. package/dist/index.html +3 -3
  41. package/dist/sw.js +3 -6
  42. package/dist/workflow/index.html +5 -4
  43. package/mcp/LIVE-PROTOCOL.md +7 -47
  44. package/mcp/README.md +16 -26
  45. package/mcp/live-hub.mjs +6 -10
  46. package/mcp/package.json +1 -3
  47. package/mcp/tool-handlers.mjs +4 -168
  48. package/package.json +3 -22
  49. package/src/App.jsx +151 -2625
  50. package/src/agent-progress.js +80 -0
  51. package/src/agent-turn-mark.js +30 -0
  52. package/src/analytics.js +1 -19
  53. package/src/app-context.js +12 -2
  54. package/src/app-stage.jsx +2 -34
  55. package/src/asset-pane.jsx +6 -5
  56. package/src/camera-block.js +21 -0
  57. package/src/camera-direction.js +120 -0
  58. package/src/character-kind.js +3 -9
  59. package/src/command-bus.js +10 -8
  60. package/src/commands/cast.js +5 -34
  61. package/src/commands/elements/character.js +1 -1
  62. package/src/commands/elements/motion.js +1 -6
  63. package/src/commands/elements/object.js +6 -2
  64. package/src/commands/elements/shot.js +4 -1
  65. package/src/commands/elements.js +38 -14
  66. package/src/commands/index.js +3 -1
  67. package/src/commands/kits.js +37 -0
  68. package/src/commands/motion-timing.js +57 -0
  69. package/src/commands/motion.js +9 -152
  70. package/src/commands/motion3-compile.js +65 -0
  71. package/src/commands/motion3.js +214 -0
  72. package/src/commands/objects.js +6 -4
  73. package/src/commands/shot.js +55 -13
  74. package/src/commands/stage.js +14 -1
  75. package/src/commands/view.js +4 -1
  76. package/src/document-mirrors.js +42 -0
  77. package/src/document-store.js +4 -0
  78. package/src/domains/cast.js +19 -194
  79. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  80. package/src/domains/motion/take-source.js +53 -0
  81. package/src/domains/motion.js +60 -1838
  82. package/src/domains/scenes.js +12 -55
  83. package/src/domains/shots.js +2 -2
  84. package/src/domains/stage.js +4 -2
  85. package/src/draft-ghost.js +47 -0
  86. package/src/draft-ghost.jsx +129 -0
  87. package/src/dualview.jsx +4 -3
  88. package/src/effects/EffectsLayer.jsx +112 -0
  89. package/src/effects/effect-meshes.js +232 -0
  90. package/src/effects/effect-record.js +103 -0
  91. package/src/effects/emitter.js +59 -0
  92. package/src/effects/simulate.js +261 -0
  93. package/src/effects/state.js +59 -0
  94. package/src/execution-telemetry.js +2 -3
  95. package/src/first-success-guide.jsx +5 -5
  96. package/src/ghost-set.js +257 -0
  97. package/src/held-object-edit.jsx +113 -0
  98. package/src/held-object-pose.js +175 -0
  99. package/src/hierarchy-model.js +1 -1
  100. package/src/hierarchy-panel.jsx +1 -1
  101. package/src/imported-mesh.jsx +103 -0
  102. package/src/kits/held.js +84 -0
  103. package/src/kits/index.js +70 -0
  104. package/src/kits/smoking.js +57 -0
  105. package/src/light-blockers.js +93 -0
  106. package/src/motion/beat-board-model.js +84 -0
  107. package/src/motion/beat-board.css +124 -0
  108. package/src/motion/beat-board.jsx +60 -0
  109. package/src/motion/center-of-mass.js +66 -0
  110. package/src/motion/fix-collisions.js +850 -0
  111. package/src/motion/physics-review.js +118 -0
  112. package/src/{ardy → motion}/physics-support.js +1 -76
  113. package/src/motion/rig-snapshot.js +22 -0
  114. package/src/motion/take-segments.jsx +140 -0
  115. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  116. package/src/{ardy → motion}/timeline.css +1 -2
  117. package/src/{ardy → motion}/timeline.jsx +42 -413
  118. package/src/motion/waypoints.js +76 -0
  119. package/src/motion3/beats.js +83 -0
  120. package/src/motion3/body.js +158 -0
  121. package/src/motion3/bones.js +47 -0
  122. package/src/motion3/carried.js +47 -0
  123. package/src/motion3/compile-job.js +79 -0
  124. package/src/motion3/compile-worker.js +10 -0
  125. package/src/motion3/compiler/anchors.js +113 -0
  126. package/src/motion3/compiler/arm-orient.js +128 -0
  127. package/src/motion3/compiler/channels.js +125 -0
  128. package/src/motion3/compiler/compile.js +205 -0
  129. package/src/motion3/compiler/gait-rhythm.js +97 -0
  130. package/src/motion3/compiler/grip.js +171 -0
  131. package/src/motion3/compiler/head-steady.js +48 -0
  132. package/src/motion3/compiler/hold.js +134 -0
  133. package/src/motion3/compiler/idle.js +113 -0
  134. package/src/motion3/compiler/limbs.js +197 -0
  135. package/src/motion3/compiler/math.js +72 -0
  136. package/src/motion3/compiler/pose.js +177 -0
  137. package/src/motion3/compiler/quat.js +49 -0
  138. package/src/motion3/compiler/skeleton.js +74 -0
  139. package/src/motion3/compiler/validate-common.js +32 -0
  140. package/src/motion3/compiler/validate-goals.js +118 -0
  141. package/src/motion3/compiler/validate-objects.js +144 -0
  142. package/src/motion3/compiler/validate-parts.js +118 -0
  143. package/src/motion3/compiler/validate.js +122 -0
  144. package/src/motion3/compiler/vocab.js +74 -0
  145. package/src/motion3/compiler/walk-pelvis.js +275 -0
  146. package/src/motion3/compiler/walk.js +207 -0
  147. package/src/motion3/compiler/wrist-clamp.js +32 -0
  148. package/src/motion3/delta.js +47 -0
  149. package/src/motion3/describe.js +177 -0
  150. package/src/motion3/docs.js +27 -0
  151. package/src/motion3/edit-carry.js +21 -0
  152. package/src/motion3/fix/ease.js +16 -0
  153. package/src/motion3/fix/ground.js +37 -0
  154. package/src/motion3/fix/index.js +52 -0
  155. package/src/motion3/fix/push.js +71 -0
  156. package/src/motion3/fix/reach.js +46 -0
  157. package/src/motion3/fix/retime.js +79 -0
  158. package/src/motion3/fix/self.js +211 -0
  159. package/src/motion3/fix/slide.js +70 -0
  160. package/src/motion3/fix/turn.js +51 -0
  161. package/src/motion3/follow.js +39 -0
  162. package/src/motion3/free-motion.js +204 -0
  163. package/src/motion3/grip-choice.js +130 -0
  164. package/src/motion3/hold-plan.js +157 -0
  165. package/src/motion3/json-patch.js +77 -0
  166. package/src/motion3/key-aim.js +105 -0
  167. package/src/motion3/key-edit.js +82 -0
  168. package/src/motion3/lenient.js +127 -0
  169. package/src/motion3/object-keys.js +236 -0
  170. package/src/motion3/object-pose.js +182 -0
  171. package/src/motion3/object-tracks.js +12 -0
  172. package/src/motion3/people.js +82 -0
  173. package/src/motion3/pipeline.js +102 -0
  174. package/src/motion3/playback.js +51 -0
  175. package/src/motion3/probes.js +91 -0
  176. package/src/motion3/qa/body-checks.js +34 -0
  177. package/src/motion3/qa/check-names.js +14 -0
  178. package/src/motion3/qa/checks.js +125 -0
  179. package/src/motion3/qa/contact-checks.js +51 -0
  180. package/src/motion3/qa/explain.js +80 -0
  181. package/src/motion3/qa/fmt.js +5 -0
  182. package/src/motion3/qa/foot-checks.js +72 -0
  183. package/src/motion3/qa/geometry.js +77 -0
  184. package/src/motion3/qa/goals.js +191 -0
  185. package/src/motion3/qa/ground.js +60 -0
  186. package/src/motion3/qa/hints-contact.js +195 -0
  187. package/src/motion3/qa/hints-goal.js +160 -0
  188. package/src/motion3/qa/hints-knee.js +24 -0
  189. package/src/motion3/qa/hints-motion.js +209 -0
  190. package/src/motion3/qa/hints-object.js +91 -0
  191. package/src/motion3/qa/index.js +44 -0
  192. package/src/motion3/qa/key-checks.js +82 -0
  193. package/src/motion3/qa/near.js +96 -0
  194. package/src/motion3/qa/object-checks.js +135 -0
  195. package/src/motion3/qa/patch.js +161 -0
  196. package/src/motion3/qa/people-checks.js +37 -0
  197. package/src/motion3/qa/severity.js +38 -0
  198. package/src/motion3/qa/spec-checks.js +65 -0
  199. package/src/motion3/qa/style.js +228 -0
  200. package/src/motion3/qa/wrist-checks.js +69 -0
  201. package/src/motion3/qa/wrist-obs.js +60 -0
  202. package/src/motion3/refs.js +129 -0
  203. package/src/motion3/repair-tune.js +121 -0
  204. package/src/motion3/repair.js +83 -0
  205. package/src/motion3/report.js +67 -0
  206. package/src/motion3/rigs/index.js +14 -0
  207. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  208. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  209. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  210. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  211. package/src/motion3/scene.js +60 -0
  212. package/src/motion3/schema/brief.js +89 -0
  213. package/src/motion3/schema/fields.js +44 -0
  214. package/src/motion3/schema/spec-examples.js +46 -0
  215. package/src/motion3/schema/topics.js +113 -0
  216. package/src/motion3/schema/types.js +149 -0
  217. package/src/motion3/search.js +117 -0
  218. package/src/motion3/second-hand.js +112 -0
  219. package/src/motion3/settle.js +136 -0
  220. package/src/motion3/skills/basic.js +83 -0
  221. package/src/motion3/skills/book.js +76 -0
  222. package/src/motion3/skills/car.js +145 -0
  223. package/src/motion3/skills/flip.js +200 -0
  224. package/src/motion3/skills/frames-auto.js +29 -0
  225. package/src/motion3/skills/impact.js +109 -0
  226. package/src/motion3/skills/index.js +85 -0
  227. package/src/motion3/skills/info.js +80 -0
  228. package/src/motion3/skills/smoke-kit.js +109 -0
  229. package/src/motion3/skills/smoke.js +160 -0
  230. package/src/motion3/spec-store.js +59 -0
  231. package/src/motion3/take-edit.js +155 -0
  232. package/src/motion3/take.js +63 -0
  233. package/src/motion3/timeline.js +127 -0
  234. package/src/motion3/views.js +121 -0
  235. package/src/motion3/walk-plan.js +119 -0
  236. package/src/object-catalog.jsx +7 -1
  237. package/src/object-colors.js +98 -0
  238. package/src/object-gizmo-parts.js +115 -0
  239. package/src/object-gizmo.jsx +37 -122
  240. package/src/object-glow.js +139 -0
  241. package/src/object-hidden.js +33 -0
  242. package/src/object-look.js +196 -0
  243. package/src/object-size.js +10 -0
  244. package/src/object-travel.js +71 -6
  245. package/src/panels/CameraPanel.jsx +7 -7
  246. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  247. package/src/panels/LightPanel.jsx +25 -4
  248. package/src/panels/ObjectLookPanel.jsx +88 -0
  249. package/src/panels/PosePanel.jsx +2 -17
  250. package/src/panels/RigControlPanel.jsx +5 -53
  251. package/src/panels/SubjectBox.jsx +4 -1
  252. package/src/panels/details.css +14 -0
  253. package/src/posestudio.jsx +4 -27
  254. package/src/project-browser.jsx +3 -4
  255. package/src/project-resources.js +18 -31
  256. package/src/project.js +13 -104
  257. package/src/props.jsx +25 -280
  258. package/src/qa-label.js +25 -0
  259. package/src/rail-dot-drag.js +23 -0
  260. package/src/render-passes.js +10 -0
  261. package/src/resource-status.jsx +1 -0
  262. package/src/result-modal.jsx +4 -19
  263. package/src/room.jsx +20 -12
  264. package/src/scene-assets.js +8 -6
  265. package/src/scene-light-place.js +72 -0
  266. package/src/scene-lights.jsx +148 -0
  267. package/src/scene-objects.js +31 -139
  268. package/src/scenes.js +15 -105
  269. package/src/semantic-edit.js +2 -5
  270. package/src/set-pieces.jsx +183 -0
  271. package/src/settings-menu.jsx +3 -11
  272. package/src/shell/BottomDock.jsx +9 -51
  273. package/src/shell/DetailsSlot.jsx +16 -200
  274. package/src/shell/GhostsMenuGroup.jsx +74 -0
  275. package/src/shell/LibrarySlot.jsx +1 -3
  276. package/src/shell/MenuBar.jsx +3 -3
  277. package/src/shell/PreferencesDialog.jsx +15 -264
  278. package/src/shell/PreferencesSlot.jsx +2 -8
  279. package/src/shell/TopBar.jsx +5 -84
  280. package/src/shell/ViewportToolbar.jsx +10 -70
  281. package/src/shell/agent-glass.css +19 -6
  282. package/src/shell/details-selection.js +42 -0
  283. package/src/shell/ghosts-menu.css +22 -0
  284. package/src/shell/glass-regions.css +29 -3
  285. package/src/shell/preferences.css +6 -1
  286. package/src/shot-authoring.js +4 -7
  287. package/src/soft-shadows.js +37 -0
  288. package/src/stage-attach-frames.js +12 -12
  289. package/src/stage-canvas-rigs.jsx +3 -3
  290. package/src/stage-capture.jsx +42 -8
  291. package/src/stage-characters.jsx +22 -216
  292. package/src/stage-editor-gizmos.jsx +6 -6
  293. package/src/stage-environment.js +93 -0
  294. package/src/stage-hdr-builtins.js +107 -0
  295. package/src/stage-hdr-image.js +116 -0
  296. package/src/stage-hdr-library.js +70 -0
  297. package/src/stage-hdr-map.js +94 -0
  298. package/src/stage-hdr.js +130 -0
  299. package/src/stage-lighting.js +149 -0
  300. package/src/stage-occlusion.js +210 -0
  301. package/src/stage-sky.jsx +159 -0
  302. package/src/stage-startup-defaults.js +5 -9
  303. package/src/studio-actions.js +16 -14
  304. package/src/studio-agent-commands.js +19 -16
  305. package/src/studio-agent-motion.js +9 -9
  306. package/src/studio-agent-protocol.js +47 -77
  307. package/src/studio-app-binding.js +27 -47
  308. package/src/studio-asset-list.js +17 -0
  309. package/src/studio-contact-sheet.js +37 -5
  310. package/src/studio-elements.js +27 -7
  311. package/src/studio-entity-bounds.js +18 -0
  312. package/src/studio-error-codes.js +15 -0
  313. package/src/studio-export-framing.js +152 -0
  314. package/src/studio-lighting-check.js +103 -0
  315. package/src/studio-motion-visual.js +150 -0
  316. package/src/studio-schema-errors.js +80 -0
  317. package/src/studio-support-gap.js +74 -0
  318. package/src/styles.css +1 -19
  319. package/src/timeline-defaults.js +5 -0
  320. package/src/timeline-extent.js +8 -20
  321. package/src/tutorial-walk.js +17 -0
  322. package/src/ui-scrub.js +1 -1
  323. package/src/use-shot-camera-tracks.js +12 -0
  324. package/src/view-ghosts.js +97 -0
  325. package/src/view-ghosts.jsx +49 -0
  326. package/src/workflow/AgentPanel.jsx +47 -166
  327. package/src/workflow/AgentProgressCard.jsx +109 -0
  328. package/src/workflow/AgentReplyText.jsx +37 -0
  329. package/src/workflow/AgentTurnSteps.jsx +84 -0
  330. package/src/workflow/LoginPanel.jsx +140 -0
  331. package/src/workflow/WorkflowBuilder.jsx +7 -23
  332. package/src/workflow/agent-client.js +54 -51
  333. package/src/workflow/agent-panel.css +71 -1
  334. package/src/workflow/agent-progress-card.css +167 -0
  335. package/src/workflow/agent-progress-view.js +47 -0
  336. package/src/workflow/agent-turn-steps.css +113 -0
  337. package/src/workflow/agent-turn.js +191 -0
  338. package/src/workflow/login-client.js +34 -0
  339. package/src/workflow/motion-input.js +18 -31
  340. package/src/workflow/node-schema.js +1 -1
  341. package/src/workflow/reply-markdown.js +69 -0
  342. package/src/workflow/scene-asset-sync.js +8 -8
  343. package/tools/agent-session-stats.mjs +61 -0
  344. package/tools/dev-full.mjs +5 -56
  345. package/tools/eslint-baseline.json +12 -13
  346. package/tools/knip-baseline.json +0 -179
  347. package/tools/loc-baseline.json +31 -81
  348. package/tools/motion3/extract-body.mjs +103 -0
  349. package/tools/motion3/extract-rig.mjs +85 -0
  350. package/tools/process-supervisor.mjs +0 -88
  351. package/tools/run-tests.mjs +9 -125
  352. package/bin/agent/motion-runtime.mjs +0 -76
  353. package/dist/ardy/cskel27-rest.json +0 -557
  354. package/dist/assets/analytics-C9pMpW7s.js +0 -1
  355. package/dist/assets/app-B_2XB9rw.css +0 -1
  356. package/dist/assets/app-DAuvKPP8.js +0 -4920
  357. package/dist/assets/config-Bx7pYsD9.js +0 -1
  358. package/dist/assets/config-DeoFTYzb.css +0 -1
  359. package/dist/assets/demo-CvKuBgy5.js +0 -1
  360. package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
  361. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  362. package/dist/assets/ticket-BPk2Ni95.js +0 -1
  363. package/dist/assets/workflow-jJRBR94Z.js +0 -179
  364. package/dist/demo/walk-then-stop.npz +0 -0
  365. package/mcp/ardy-prompts.mjs +0 -218
  366. package/src/ardy/auto-fix-panel.css +0 -108
  367. package/src/ardy/auto-physics.js +0 -1194
  368. package/src/ardy/client.js +0 -153
  369. package/src/ardy/convert.js +0 -313
  370. package/src/ardy/cskel27-neutral.js +0 -39
  371. package/src/ardy/cskel27.js +0 -77
  372. package/src/ardy/export.js +0 -165
  373. package/src/ardy/fix-collisions.js +0 -2257
  374. package/src/ardy/foot-lock.js +0 -230
  375. package/src/ardy/motion-calibration.js +0 -118
  376. package/src/ardy/motion-edit.js +0 -213
  377. package/src/ardy/motion-url.js +0 -55
  378. package/src/ardy/mp4-duration.js +0 -102
  379. package/src/ardy/npz.js +0 -595
  380. package/src/ardy/physics-panel.css +0 -21
  381. package/src/ardy/physics-panel.jsx +0 -89
  382. package/src/ardy/physics-refine.js +0 -111
  383. package/src/ardy/physics-review.js +0 -567
  384. package/src/ardy/physics-temporal.js +0 -54
  385. package/src/ardy/platform-fit-panel.css +0 -64
  386. package/src/ardy/platform-fit-panel.jsx +0 -41
  387. package/src/ardy/platform-fit.js +0 -326
  388. package/src/ardy/playback.js +0 -711
  389. package/src/ardy/pose-pin.js +0 -97
  390. package/src/ardy/prompt-clips.js +0 -30
  391. package/src/ardy/retime.js +0 -211
  392. package/src/ardy/root-drop.js +0 -369
  393. package/src/ardy/timeline-resize.js +0 -13
  394. package/src/ardy/to-cskel27.js +0 -225
  395. package/src/ardy/trim.js +0 -33
  396. package/src/ardy/waypoints.js +0 -369
  397. package/src/ardy-bridge-contract.js +0 -281
  398. package/src/domains/motion/motion-video-capture.js +0 -664
  399. package/src/i2v-motion-client.js +0 -77
  400. package/src/i2v-motion-studio.jsx +0 -180
  401. package/src/line-edit.js +0 -1348
  402. package/src/motion/generation.js +0 -105
  403. package/src/motion-readiness-ui.jsx +0 -67
  404. package/src/motion-readiness.js +0 -40
  405. package/src/motion-resources.js +0 -177
  406. package/src/motion-store.js +0 -65
  407. package/src/motion-trail.js +0 -601
  408. package/src/multimodel-ingest.js +0 -394
  409. package/src/panels/PromptBlocksPanel.jsx +0 -444
  410. package/src/panels/VideoCapturePanel.jsx +0 -181
  411. package/src/pose-extract/browser.js +0 -42
  412. package/src/pose-extract/detector.js +0 -100
  413. package/src/pose-extract/fit.js +0 -227
  414. package/src/pose-extract/image-frame.js +0 -51
  415. package/src/pose-extract/index.js +0 -58
  416. package/src/pose-extract/keyframes.js +0 -74
  417. package/src/pose-extract/landmarks.js +0 -162
  418. package/src/pose-extract/mirror.js +0 -153
  419. package/src/pose-extract/take.js +0 -131
  420. package/src/pose-extract/video-frames.js +0 -91
  421. package/src/take-recipe.js +0 -165
  422. package/src/trail-key-conflicts.js +0 -39
  423. package/src/trail-pick.js +0 -59
  424. package/src/use-i2v-capture-modal.js +0 -15
  425. package/tools/ardy/BRIDGE.md +0 -37
  426. package/tools/ardy/GVHMR-NPZ.md +0 -68
  427. package/tools/ardy/README.md +0 -124
  428. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  429. package/tools/ardy/artifacts.mjs +0 -34
  430. package/tools/ardy/bridge.mjs +0 -1732
  431. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  432. package/tools/ardy/cclay_constrained_generate.py +0 -561
  433. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  434. package/tools/ardy/cclay_motion_edit.py +0 -510
  435. package/tools/ardy/cclay_sequence_generate.py +0 -705
  436. package/tools/ardy/cclay_worker.py +0 -304
  437. package/tools/ardy/dump-npz.py +0 -205
  438. package/tools/ardy/extract-rest.mjs +0 -299
  439. package/tools/ardy/extract.mjs +0 -507
  440. package/tools/ardy/footage.mjs +0 -467
  441. package/tools/ardy/gvhmr-floor.mjs +0 -102
  442. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  443. package/tools/ardy/gvhmr_fastpath.py +0 -168
  444. package/tools/ardy/gvhmr_trajectory.py +0 -240
  445. package/tools/ardy/merge-text-encoder.py +0 -160
  446. package/tools/ardy/mocap-metrics.mjs +0 -200
  447. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  448. package/tools/ardy/motion-stabilize.mjs +0 -348
  449. package/tools/ardy/npz.mjs +0 -414
  450. package/tools/ardy/pose-to-npz.mjs +0 -106
  451. package/tools/ardy/prompt-limits.mjs +0 -5
  452. package/tools/ardy/run-edit-on-box.sh +0 -73
  453. package/tools/ardy/run-local.mjs +0 -233
  454. package/tools/ardy/run-on-box.sh +0 -593
  455. package/tools/ardy/run-sequence-on-box.sh +0 -177
  456. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  457. package/tools/ardy/runners/index.mjs +0 -18
  458. package/tools/ardy/runners/local.mjs +0 -455
  459. package/tools/ardy/runners/proc.mjs +0 -125
  460. package/tools/ardy/runners/remote.mjs +0 -252
  461. package/tools/ardy/setup-local.mjs +0 -189
  462. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  463. package/tools/ardy/setup-text-encoder.py +0 -306
  464. package/tools/ardy/smpl-cskel27.mjs +0 -178
  465. package/tools/ardy/visual-qa.mjs +0 -196
  466. package/tools/ardy/vq-car.mjs +0 -52
  467. package/tools/bench/EXP3.md +0 -61
  468. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  469. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  470. package/tools/bench/cclay_bench_runner.py +0 -42
  471. package/tools/bench/cube-contact.mjs +0 -162
  472. package/tools/bench/exp3.mjs +0 -152
  473. package/tools/bench/extract-bench-lib.mjs +0 -216
  474. package/tools/bench/extract-bench.mjs +0 -302
  475. package/tools/bench/fal-generate.mjs +0 -88
  476. package/tools/bench/fit/README.md +0 -189
  477. package/tools/bench/fit/camera.mjs +0 -59
  478. package/tools/bench/fit/contact.mjs +0 -419
  479. package/tools/bench/fit/footlock.mjs +0 -234
  480. package/tools/bench/fit/motion.mjs +0 -85
  481. package/tools/bench/fit/pin.mjs +0 -27
  482. package/tools/bench/fit/remote.mjs +0 -105
  483. package/tools/bench/fit-bench.mjs +0 -126
  484. package/tools/bench/fit-sanity.mjs +0 -45
  485. package/tools/bench/metrics.mjs +0 -313
  486. package/tools/bench/obs/depth.mjs +0 -258
  487. package/tools/bench/obs/extrinsics.mjs +0 -175
  488. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  489. package/tools/bench/obs/ground.mjs +0 -294
  490. package/tools/bench/obs/heading.mjs +0 -151
  491. package/tools/bench/obs/ladder.mjs +0 -555
  492. package/tools/bench/obs/mannequin-betas.json +0 -124
  493. package/tools/bench/obs/remote.mjs +0 -83
  494. package/tools/bench/obs/rest_joints.py +0 -52
  495. package/tools/bench/obs/ybot-targets.mjs +0 -49
  496. package/tools/bench/obs-bench.mjs +0 -520
  497. package/tools/bench/score.mjs +0 -343
  498. package/tools/bench/summarize.mjs +0 -86
  499. package/tools/demo-worker/README.md +0 -137
  500. package/tools/demo-worker/api-client.mjs +0 -233
  501. package/tools/demo-worker/generate.mjs +0 -301
  502. package/tools/demo-worker/index.mjs +0 -466
  503. package/tools/demo-worker/sign-core.mjs +0 -112
  504. package/tools/demo-worker/sign.mjs +0 -161
  505. package/tools/gt-render/browser.mjs +0 -182
  506. package/tools/gt-render/camera-math.mjs +0 -276
  507. package/tools/gt-render/page.mjs +0 -267
  508. package/tools/gt-render/render.mjs +0 -514
  509. package/tools/gt-render/scene-box.mjs +0 -31
  510. package/tools/gt-render/take-transform.mjs +0 -87
  511. package/tools/kimodo/compare-seam.mjs +0 -108
  512. package/tools/kimodo/constraints.mjs +0 -244
  513. package/tools/kimodo/edit.mjs +0 -259
  514. package/tools/kimodo/effector-constraints.mjs +0 -200
  515. package/tools/kimodo/generate.mjs +0 -646
  516. package/tools/kimodo/local-output.mjs +0 -265
  517. package/tools/kimodo/measure-pose.mjs +0 -65
  518. package/tools/kimodo/measure-preserve.mjs +0 -899
  519. package/tools/kimodo/measure-waypoints.mjs +0 -55
  520. package/tools/kimodo/mlx-generate.py +0 -111
  521. package/tools/kimodo/pose-constraints.mjs +0 -248
  522. package/tools/kimodo/preserve-mask.mjs +0 -543
  523. package/tools/kimodo/read-npz.mjs +0 -189
  524. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  525. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  526. package/tools/kimodo/runner.mjs +0 -284
  527. package/tools/kimodo/seam-batch.mjs +0 -77
  528. package/tools/kimodo/setup-local.mjs +0 -30
  529. package/tools/kimodo/setup-on-box.sh +0 -339
  530. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  531. package/tools/morphgs/assets/gen_truth.py +0 -39
  532. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  533. package/tools/morphgs/assets/playback-check.mjs +0 -30
  534. package/tools/morphgs/demo-gate.sh +0 -26
  535. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  536. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  537. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  538. package/tools/morphgs/setup-on-cluster.sh +0 -126
  539. package/tools/projflow/driver.py +0 -1069
  540. package/tools/projflow/generate.mjs +0 -805
  541. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  542. package/tools/projflow/line-edit-job.mjs +0 -547
  543. package/tools/projflow/replay.mjs +0 -452
  544. package/tools/projflow/runner.mjs +0 -269
  545. package/tools/projflow/service.mjs +0 -742
  546. package/tools/track/DESIGN.md +0 -513
  547. package/tools/track/backfill-provenance.mjs +0 -116
  548. package/tools/track/budget.mjs +0 -8
  549. package/tools/track/check-rig.mjs +0 -209
  550. package/tools/track/diagnostics.schema.json +0 -60
  551. package/tools/track/export-rig.mjs +0 -256
  552. package/tools/track/fallback.mjs +0 -7
  553. package/tools/track/fk-parity-fixture.mjs +0 -161
  554. package/tools/track/gate.mjs +0 -349
  555. package/tools/track/masks.mjs +0 -318
  556. package/tools/track/metrics.mjs +0 -201
  557. package/tools/track/publish-obs.mjs +0 -108
  558. package/tools/track/py/check_env.py +0 -61
  559. package/tools/track/py/eval_lr.py +0 -229
  560. package/tools/track/py/lr_viterbi.py +0 -294
  561. package/tools/track/py/masks.py +0 -405
  562. package/tools/track/py/objective.py +0 -382
  563. package/tools/track/py/rig.py +0 -508
  564. package/tools/track/py/scene.py +0 -326
  565. package/tools/track/py/test_joint_indices.py +0 -123
  566. package/tools/track/py/test_lr_viterbi.py +0 -195
  567. package/tools/track/py/test_masks.py +0 -229
  568. package/tools/track/py/test_rig.py +0 -288
  569. package/tools/track/py/test_scene.py +0 -354
  570. package/tools/track/py/test_track.py +0 -328
  571. package/tools/track/py/track.py +0 -411
  572. package/tools/track/remote.mjs +0 -140
  573. package/tools/track/rig-dump.mjs +0 -227
  574. package/tools/track/run-box-tests.mjs +0 -14
  575. package/tools/track/run-box.mjs +0 -168
  576. package/tools/track/setup-box.sh +0 -59
  577. package/tools/track/study-2d.mjs +0 -728
  578. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  579. /package/src/{ardy → motion}/ground.js +0 -0
  580. /package/src/{ardy → motion}/ik-drag.js +0 -0
  581. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  582. /package/src/{ardy → motion}/ik.js +0 -0
  583. /package/src/{ardy → motion}/physics-surface.js +0 -0
  584. /package/src/{ardy → motion}/playback-clock.js +0 -0
  585. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -1,452 +0,0 @@
1
- /**
2
- * replay.mjs — recipe replay (contract C10) and the per-field line-edit rules
3
- * both the C6 and the C10 paths validate with.
4
- *
5
- * A take is a RECIPE, not a result: seed + prompt blocks + the line edits the
6
- * artist drew on top. Determinism makes the result reconstructible, so
7
- * regenerating or extending a take no longer has to destroy its refinements —
8
- * the app sends the stored line edits back as `replay` and the bridge re-applies
9
- * them to the freshly generated take.
10
- *
11
- * WHY THIS IS ITS OWN MODULE and not more bridge code: everything here is pure
12
- * arithmetic plus one injectable job runner, so the whole contract — the
13
- * per-entry rules, the boundary warning, the sequential chaining — is testable
14
- * on a laptop with no GPU and no sidecar. bridge.mjs keeps only the wiring:
15
- * which take the chain starts from, where the artifacts go, and what it streams.
16
- *
17
- * THE CHAIN, stated once. Entry i reads entry i-1's OUTPUT; entry 0 reads the
18
- * freshly generated take. Every output lands in the request's artifact dir as
19
- * replay-<i>.npz and only the LAST one is registered — the intermediates are
20
- * evidence, not takes. A failing entry is skipped and the chain continues from
21
- * the last good file: a missing refinement beats a dead run.
22
- */
23
-
24
- import { join } from "node:path";
25
-
26
- import { TRACK_TO_HML22_JOINT, UNMAPPABLE_TRACKS } from "./generate.mjs";
27
-
28
- /** The tracks a line edit (C6) or a replay entry (C10) may name — the wrapper's
29
- * own track -> hml22 joint table, never a copy of it. */
30
- const LINE_EDIT_TRACK_IDS = Object.keys(TRACK_TO_HML22_JOINT);
31
- const LINE_EDIT_POINTS_MIN = 2;
32
- const LINE_EDIT_POINTS_MAX = 64; // matches src/line-edit.js MAX_LINE_POINTS
33
- /** C6's OTHER gesture: 3D pins (`pins3d`), one per scrubbed moment.
34
- *
35
- * A pin is a keyframe — "the hand is HERE at frame 100" — and it is the shape
36
- * ProjFlow is actually evaluated on: its whole spatial-control table is sparse
37
- * keyframes (1, 2, 5, 49, 196 of them) with exact satisfaction and the flow
38
- * prior filling the gaps. A drawn line is the dense end of the same mechanism.
39
- *
40
- * ONE is a legal edit, unlike points2d's two: a line needs two points to have a
41
- * direction, a pin needs none. EIGHT is the cap, and it is a UX number rather
42
- * than a solver one — 3 rows per pin is nothing next to 128 line rows — past
43
- * eight pins in one edit the artist is keyframing by hand and wants the pose
44
- * studio, not a reroute. */
45
- export const LINE_EDIT_PINS_MIN = 1;
46
- export const LINE_EDIT_PINS_MAX = 8;
47
- /** C10's cap. A recipe is an authoring history, not a render queue: 16 chained
48
- * box round trips is already a minute of wall time on top of the generation. */
49
- export const REPLAY_MAX = 16;
50
- /** Mirrors the bridge's own optional-seed cap; the bridge passes its constant in
51
- * so the two can never disagree, and this default only serves direct callers. */
52
- const DEFAULT_SEED_MAX = 2 ** 31 - 1;
53
- /** Frames either side of a block boundary that a replayed edit is warned about.
54
- * See boundaryWarningFor for why the number is small. */
55
- export const BOUNDARY_MARGIN = 5;
56
- /** Fields a replay entry may NOT carry: `replay` cannot be combined with the
57
- * modes that splice or rewrite an existing take, because then "the take the
58
- * replay applies to" has two candidate answers. */
59
- const REPLAY_EXCLUSIVE = ["lineEdit", "motionEdit", "regenerateSegments"];
60
-
61
- /* ------------------------------------------------------------------------- */
62
- /* the per-field rules, shared by C6 and C10 */
63
- /* ------------------------------------------------------------------------- */
64
-
65
- /**
66
- * The field checks a line-edit payload must pass, wherever it arrives from.
67
- *
68
- * `label` is the wire path the message names — "lineEdit" for a C6 request,
69
- * "replay[3]" for the fourth entry of a C10 replay — and it is the ONLY
70
- * difference between the two paths. The strings are otherwise byte-identical to
71
- * the ones the C6 validator has always returned.
72
- *
73
- * What is NOT here, deliberately: `sourceMotion` (C6 requires it, C10 forbids
74
- * it), the exclusivity rules, and `preview` (C6 only). Those belong to whoever
75
- * calls this.
76
- *
77
- * Frame numbers are all on the app's 24 fps clip clock, exactly as sent; the
78
- * 24 -> 20 conversion is line-edit-job.mjs's business and is not duplicated
79
- * here or in the bridge.
80
- *
81
- * Returns an error message naming the offending field, or null when valid.
82
- */
83
- export function validateLineEditFields(lineEdit, clipFrames, label = "lineEdit") {
84
- if (!lineEdit || typeof lineEdit !== "object" || Array.isArray(lineEdit)) {
85
- return `field '${label}' must be an object`;
86
- }
87
- // `chest` is a REAL pose-studio track and a real refusal: cskel27 Spine2 is
88
- // one of the five joints the hml22 skeleton has no source for (S2), so there
89
- // is nothing to constrain. Refused by name, with the alternative, rather than
90
- // silently retargeted onto a neighbouring spine joint — which would put the
91
- // drawn line on a curve the artist did not draw.
92
- if (typeof lineEdit.track === "string" && Object.hasOwn(UNMAPPABLE_TRACKS, lineEdit.track)) {
93
- return (
94
- `field '${label}.track' "${lineEdit.track}" cannot be line-edited: ${UNMAPPABLE_TRACKS[lineEdit.track]}; ` +
95
- "draw on spine or neck instead"
96
- );
97
- }
98
- if (typeof lineEdit.track !== "string" || !Object.hasOwn(TRACK_TO_HML22_JOINT, lineEdit.track)) {
99
- return (
100
- `field '${label}.track' ${JSON.stringify(lineEdit.track)} is not a line-editable IK track id; ` +
101
- `valid ids are ${LINE_EDIT_TRACK_IDS.join(", ")}`
102
- );
103
- }
104
- const range = lineEdit.frameRange;
105
- if (!range || typeof range !== "object" || Array.isArray(range)) {
106
- return `field '${label}.frameRange' must be an object { startFrame, endFrame }`;
107
- }
108
- if (!Number.isInteger(range.startFrame) || !Number.isInteger(range.endFrame)) {
109
- return `field '${label}.frameRange' startFrame and endFrame must be integers`;
110
- }
111
- // Half-open and inside the clip, like every other range that crosses this
112
- // boundary. The clip length comes from `duration`, which the app derives from
113
- // the take's own frame count; the job re-checks against the npz itself, since
114
- // only that file knows how long the take really is.
115
- if (range.startFrame < 0 || range.endFrame <= range.startFrame || range.endFrame > clipFrames) {
116
- return `field '${label}.frameRange' must be a non-empty half-open range inside 0..${clipFrames}`;
117
- }
118
- // Two points need two frames: one constrained frame is a pin, not a line.
119
- if (range.endFrame - range.startFrame < 2) {
120
- return `field '${label}.frameRange' must span at least 2 frames`;
121
- }
122
- /* ---------------------------- pins3d (C6 v2) ---------------------------
123
- * ONE EDIT IS ONE GESTURE. A stroke says "go along this shape" and pins say
124
- * "be exactly here at these moments"; combining them would ask the box to
125
- * satisfy a 2D path and a set of 3D points that were authored through
126
- * different mental models and are only accidentally compatible. So pins3d
127
- * REPLACES points2d and the camera — a pin is already in world metres and
128
- * needs no lens to be read through, which is also why a pinned edit survives
129
- * a camera move that would discard a drawn one.
130
- *
131
- * Checked BEFORE points2d so a pins payload never trips the "needs at least
132
- * 2 points" message about a field it deliberately omitted. */
133
- if (lineEdit.pins3d !== undefined) {
134
- const pins = lineEdit.pins3d;
135
- if (lineEdit.points2d !== undefined) {
136
- return `field '${label}' must carry either points2d (a drawn line) or pins3d (3D pins), not both`;
137
- }
138
- if (!Array.isArray(pins) || pins.length < LINE_EDIT_PINS_MIN) {
139
- return `field '${label}.pins3d' needs at least ${LINE_EDIT_PINS_MIN} pin`;
140
- }
141
- if (pins.length > LINE_EDIT_PINS_MAX) {
142
- return `field '${label}.pins3d' is capped at ${LINE_EDIT_PINS_MAX} pins, got ${pins.length}`;
143
- }
144
- let previousFrame = -1;
145
- for (let index = 0; index < pins.length; index += 1) {
146
- const pin = pins[index];
147
- if (!pin || typeof pin !== "object" || Array.isArray(pin)) {
148
- return `field '${label}.pins3d[${index}]' must be an object { frame, position }`;
149
- }
150
- if (!Number.isInteger(pin.frame)) {
151
- return `field '${label}.pins3d[${index}].frame' must be an integer app-clip frame`;
152
- }
153
- // Inside the range and half-open with it: a pin on endFrame names a
154
- // frame the edit does not own, and the splice would throw it away.
155
- if (pin.frame < range.startFrame || pin.frame >= range.endFrame) {
156
- return `field '${label}.pins3d[${index}].frame' ${pin.frame} is outside frameRange ${range.startFrame}..${range.endFrame}`;
157
- }
158
- // STRICTLY ascending: two pins on one frame are two answers to the
159
- // same question, and out-of-order pins mean the caller lost track of
160
- // which pin it was editing.
161
- if (pin.frame <= previousFrame) {
162
- return `field '${label}.pins3d' frames must be strictly ascending, got ${pin.frame} after ${previousFrame}`;
163
- }
164
- previousFrame = pin.frame;
165
- const position = pin.position;
166
- if (!Array.isArray(position) || position.length !== 3 || !position.every((value) => typeof value === "number" && Number.isFinite(value))) {
167
- return `field '${label}.pins3d[${index}].position' must be [x, y, z] finite numbers in world metres`;
168
- }
169
- }
170
- // The camera is a stroke's business. Refused rather than ignored, for the
171
- // same reason a replay entry's sourceMotion is: silently dropping a field
172
- // lets the caller believe it meant something.
173
- if (lineEdit.camera !== undefined) {
174
- return `field '${label}.camera' must be omitted for a pins3d edit — pins are world-space and need no lens`;
175
- }
176
- if (lineEdit.prompt !== undefined && typeof lineEdit.prompt !== "string") {
177
- return `field '${label}.prompt' must be a string when present`;
178
- }
179
- return null;
180
- }
181
-
182
- const points = lineEdit.points2d;
183
- if (!Array.isArray(points) || points.length < LINE_EDIT_POINTS_MIN) {
184
- return `field '${label}.points2d' needs at least ${LINE_EDIT_POINTS_MIN} points`;
185
- }
186
- // The cap is the app's own, and it is a solver budget: the box builds two
187
- // affine rows per point and factorises an m x m system every ODE step.
188
- if (points.length > LINE_EDIT_POINTS_MAX) {
189
- return `field '${label}.points2d' is capped at ${LINE_EDIT_POINTS_MAX} points, got ${points.length}`;
190
- }
191
- for (let index = 0; index < points.length; index += 1) {
192
- const point = points[index];
193
- if (!Array.isArray(point) || point.length !== 2 || !point.every((value) => typeof value === "number" && Number.isFinite(value))) {
194
- return `field '${label}.points2d[${index}]' must be [u, v] finite numbers`;
195
- }
196
- // Viewport-NORMALISED, and enforced rather than clamped: a pixel
197
- // coordinate here is the single most likely wire mistake and it produces a
198
- // plausible-looking line through the wrong ray.
199
- if (point[0] < 0 || point[0] > 1 || point[1] < 0 || point[1] > 1) {
200
- return `field '${label}.points2d[${index}]' must be viewport-normalized into 0..1, got ${JSON.stringify(point)}`;
201
- }
202
- }
203
- const camera = lineEdit.camera;
204
- if (!camera || typeof camera !== "object" || Array.isArray(camera)) {
205
- return `field '${label}.camera' must be an object { fx, fy, cx, cy, R, t }`;
206
- }
207
- for (const key of ["fx", "fy", "cx", "cy"]) {
208
- if (typeof camera[key] !== "number" || !Number.isFinite(camera[key])) {
209
- return `field '${label}.camera.${key}' must be a finite number`;
210
- }
211
- }
212
- // A non-positive focal length means the uv/NDC flip was applied twice or not
213
- // at all, and every solved position would be mirrored.
214
- if (camera.fx <= 0 || camera.fy <= 0) {
215
- return `field '${label}.camera' has a non-positive focal length — the uv convention is inverted`;
216
- }
217
- // The intrinsics live in the SAME units as points2d. A pixel focal length
218
- // beside normalised points solves cleanly and lands nowhere near the stroke.
219
- if (camera.fx > 50 || camera.fy > 50) {
220
- return `field '${label}.camera' has PIXEL focal lengths (${camera.fx}, ${camera.fy}); normalize them by the viewport size, like points2d`;
221
- }
222
- if (!Array.isArray(camera.R) || camera.R.length !== 9 || !camera.R.every((value) => typeof value === "number" && Number.isFinite(value))) {
223
- return `field '${label}.camera.R' must be 9 finite numbers (3x3 row-major, world-to-camera)`;
224
- }
225
- if (!Array.isArray(camera.t) || camera.t.length !== 3 || !camera.t.every((value) => typeof value === "number" && Number.isFinite(value))) {
226
- return `field '${label}.camera.t' must be 3 finite numbers`;
227
- }
228
- if (lineEdit.prompt !== undefined && typeof lineEdit.prompt !== "string") {
229
- return `field '${label}.prompt' must be a string when present`;
230
- }
231
- return null;
232
- }
233
-
234
- /* ------------------------------------------------------------------------- */
235
- /* the request field (contract C10) */
236
- /* ------------------------------------------------------------------------- */
237
-
238
- /**
239
- * `body.replay`: the line edits to re-apply to the take this request generates.
240
- *
241
- * Each entry is a C6 payload MINUS sourceMotion — the base is the take being
242
- * generated right now, so a stored source id would name a take that is about to
243
- * be superseded. Sending one anyway is refused rather than ignored: silently
244
- * dropping it would let a client believe it chose the base.
245
- *
246
- * Returns an error message naming the offending field, or null when valid.
247
- */
248
- export function validateReplay(body, clipFrames, { seedMax = DEFAULT_SEED_MAX } = {}) {
249
- const replay = body?.replay;
250
- if (!Array.isArray(replay)) {
251
- return "field 'replay' must be an array of line-edit payloads (contract C10)";
252
- }
253
- // Exclusivity first, for the same reason C6 checks it first: a body carrying
254
- // both is two run modes, and guessing which take the replay applies to is not
255
- // the bridge's call. Plain generation, a prompt schedule and preserve all
256
- // produce ONE fresh take, which is exactly what a replay needs.
257
- for (const field of REPLAY_EXCLUSIVE) {
258
- if (body[field] !== undefined) {
259
- return (
260
- `field 'replay' cannot be combined with ${field}: contract C10 replays onto the take this request ` +
261
- "generates, and a spliced or rewritten take leaves the replay base ambiguous"
262
- );
263
- }
264
- }
265
- if (replay.length > REPLAY_MAX) {
266
- return `field 'replay' is capped at ${REPLAY_MAX} entries, got ${replay.length} (contract C10)`;
267
- }
268
- for (let index = 0; index < replay.length; index += 1) {
269
- const entry = replay[index];
270
- const label = `replay[${index}]`;
271
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
272
- return `field '${label}' must be an object`;
273
- }
274
- if (entry.sourceMotion !== undefined) {
275
- return (
276
- `field '${label}.sourceMotion' must be omitted: contract C10 rebinds every replay entry to the take ` +
277
- "this request generates"
278
- );
279
- }
280
- // Preview is a 20-step draft for the interactive loop. A replay is the
281
- // take's history being rebuilt, so it always runs at full quality — an
282
- // entry asking otherwise is a client bug, not a preference.
283
- if (entry.preview !== undefined) {
284
- return `field '${label}.preview' must be omitted: contract C10 replays always run at full quality`;
285
- }
286
- if (entry.seed !== undefined && (!Number.isInteger(entry.seed) || entry.seed < 0 || entry.seed > seedMax)) {
287
- return `field '${label}.seed' must be an integer in 0..${seedMax}`;
288
- }
289
- const error = validateLineEditFields(entry, clipFrames, label);
290
- if (error) return error;
291
- }
292
- return null;
293
- }
294
-
295
- /* ------------------------------------------------------------------------- */
296
- /* the boundary rule */
297
- /* ------------------------------------------------------------------------- */
298
-
299
- /**
300
- * The INTERNAL block boundaries of a generated take: the frame where one
301
- * prompt block's rollout hands over to the next.
302
- *
303
- * Why they matter (the user's catch, verified in pf-chain): block N+1 is
304
- * conditioned on block N's PRE-edit tail. An edit that does not touch a block
305
- * tail leaves that tail byte-identical, so replaying it reproduces the same
306
- * chain exactly; an edit that DOES touch a tail changes what block N+1 was
307
- * conditioned on, and the replayed take can drift after the seam.
308
- *
309
- * The clip end is excluded — there is no block after it to condition — and a
310
- * request with no `segments` field (or with fewer than two blocks in it) is a
311
- * single rollout, so it has no internal boundaries at all.
312
- *
313
- * `segments` entries are contiguous by the time they get here (the bridge
314
- * refuses anything else), so their endFrames ARE the cumulative ones.
315
- */
316
- export function blockBoundaries(segments, clipFrames) {
317
- if (!Array.isArray(segments) || segments.length < 2) return [];
318
- const boundaries = [];
319
- for (const segment of segments) {
320
- const end = segment?.endFrame;
321
- if (!Number.isInteger(end) || end <= 0 || end >= clipFrames) continue;
322
- if (!boundaries.includes(end)) boundaries.push(end);
323
- }
324
- return boundaries;
325
- }
326
-
327
- /**
328
- * Does this half-open app-frame range come close enough to a block boundary to
329
- * be warned about?
330
- *
331
- * The window is [boundary - margin, boundary + margin] INCLUSIVE and the range
332
- * is [startFrame, endFrame) — so the last constrained frame is endFrame - 1 and
333
- * the two intersect iff `startFrame <= boundary + margin` and
334
- * `endFrame - 1 >= boundary - margin`.
335
- *
336
- * The margin is small (5 frames, ~0.2 s) on purpose: the conditioning window the
337
- * next block reads is the tail of the previous one, and a warning that fires on
338
- * half the clip would be ignored. NON-BLOCKING by contract — it is a note on the
339
- * report, never a refusal.
340
- */
341
- export function boundaryWarningFor(frameRange, boundaries, margin = BOUNDARY_MARGIN) {
342
- if (!frameRange || !Array.isArray(boundaries) || boundaries.length === 0) return false;
343
- const { startFrame, endFrame } = frameRange;
344
- if (!Number.isInteger(startFrame) || !Number.isInteger(endFrame)) return false;
345
- return boundaries.some(
346
- (boundary) => startFrame <= boundary + margin && endFrame - 1 >= boundary - margin
347
- );
348
- }
349
-
350
- /* ------------------------------------------------------------------------- */
351
- /* the sequential run */
352
- /* ------------------------------------------------------------------------- */
353
-
354
- /**
355
- * Re-apply a recipe's line edits to a freshly generated take, in order.
356
- *
357
- * @param {object} options
358
- * @param {object[]} options.entries validated C10 entries, application order.
359
- * @param {string} options.takePath the fresh take entry 0 edits.
360
- * @param {string} options.artifactDir where replay-<i>.npz files are written.
361
- * @param {number[]} [options.boundaries] internal block boundaries (app frames).
362
- * @param {number} [options.boundaryMargin=5]
363
- * @param {number} [options.appFps=24]
364
- * @param {Function} options.runJob runLineEditJob, injected so the sequencing
365
- * can be tested without a GPU — the chaining and the failure policy are the
366
- * only logic here, and they are where the bugs would be.
367
- * @param {(line: string) => void} [options.onStatus]
368
- * @returns {Promise<{entries: object[], takePath: string}>} the per-entry report
369
- * (C10 shape) and the path of the LAST GOOD take — what the caller registers.
370
- */
371
- export async function runReplay({
372
- entries,
373
- takePath,
374
- artifactDir,
375
- boundaries = [],
376
- boundaryMargin = BOUNDARY_MARGIN,
377
- appFps = 24,
378
- runJob,
379
- onStatus,
380
- } = {}) {
381
- if (!Array.isArray(entries)) throw new Error("runReplay: entries must be an array");
382
- if (!takePath) throw new Error("runReplay: takePath is required");
383
- if (!artifactDir) throw new Error("runReplay: artifactDir is required");
384
- if (typeof runJob !== "function") throw new Error("runReplay: runJob is required");
385
- const status = typeof onStatus === "function" ? onStatus : () => {};
386
- const report = [];
387
- // The chain cursor. It only ever moves onto a file a job actually wrote, so a
388
- // failed entry leaves it pointing at the last take that exists on disk.
389
- let current = takePath;
390
- for (let index = 0; index < entries.length; index += 1) {
391
- const entry = entries[index];
392
- const outputPath = join(artifactDir, `replay-${index}.npz`);
393
- const boundaryWarning = boundaryWarningFor(entry.frameRange, boundaries, boundaryMargin);
394
- status(
395
- `[bridge] replay ${index + 1}/${entries.length}: ${entry.track} over frames ` +
396
- `${entry.frameRange.startFrame}..${entry.frameRange.endFrame - 1}`
397
- );
398
- if (boundaryWarning) {
399
- status(
400
- `[bridge] replay ${index + 1}/${entries.length} WARNING: frames ` +
401
- `${entry.frameRange.startFrame}..${entry.frameRange.endFrame - 1} sit within ${boundaryMargin} frames of a ` +
402
- `block boundary (${boundaries.join(", ")}); the next block was conditioned on the UNEDITED tail, ` +
403
- "so this edit may not replay exactly"
404
- );
405
- }
406
- let ok = true;
407
- let error;
408
- let seamStartDelta = null;
409
- let seamEndDelta = null;
410
- try {
411
- const meta = await runJob({
412
- lineEdit: entry,
413
- takePath: current,
414
- outputPath,
415
- appFps,
416
- ...(entry.seed === undefined ? {} : { seed: entry.seed }),
417
- onStatus: status,
418
- });
419
- // The job's own output path, not the one we asked for: they are the same
420
- // file, and reading it back from the meta keeps this loop honest about
421
- // what actually got written.
422
- current = meta?.output || outputPath;
423
- seamStartDelta = Number.isFinite(meta?.seamStartDelta) ? meta.seamStartDelta : null;
424
- seamEndDelta = Number.isFinite(meta?.seamEndDelta) ? meta.seamEndDelta : null;
425
- status(
426
- `[bridge] replay ${index + 1}/${entries.length} applied: seams ` +
427
- `${seamStartDelta === null ? "n/a" : seamStartDelta.toFixed(4)} / ` +
428
- `${seamEndDelta === null ? "n/a" : seamEndDelta.toFixed(4)} m`
429
- );
430
- } catch (err) {
431
- // LOUD, and then on with the run: the take the artist asked for exists,
432
- // and losing one refinement is recoverable by drawing it again. Losing
433
- // the whole generation is not.
434
- ok = false;
435
- error = err?.message || String(err);
436
- status(
437
- `[bridge] replay ${index + 1}/${entries.length} FAILED (${entry.track}): ${error} — ` +
438
- "continuing from the last good take"
439
- );
440
- }
441
- report.push({
442
- index,
443
- track: entry.track,
444
- ok,
445
- boundaryWarning,
446
- seamStartDelta,
447
- seamEndDelta,
448
- ...(ok ? {} : { error }),
449
- });
450
- }
451
- return { entries: report, takePath: current };
452
- }