cozyclay 2.1.0 → 3.0.0

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 (583) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +40 -113
  4. package/bin/agent/agent-runner.mjs +9 -10
  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/studio-motion-reference.mjs +23 -0
  14. package/bin/agent/studio-prompt.mjs +1 -1
  15. package/bin/agent/studio-rebase-text.mjs +32 -0
  16. package/bin/agent/studio-rebase.mjs +26 -39
  17. package/bin/agent/studio-tools.mjs +9 -28
  18. package/bin/agent/tool-arguments.mjs +49 -0
  19. package/bin/agent/turn-frames.mjs +66 -0
  20. package/bin/codex-auth.mjs +9 -2
  21. package/bin/cozyclay.mjs +10 -116
  22. package/bin/live/cli.mjs +22 -6
  23. package/bin/mcp-runtime.mjs +2 -12
  24. package/bin/package-signature.mjs +0 -4
  25. package/dist/app/index.html +7 -6
  26. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  27. package/dist/assets/analytics-C9sUnndd.js +1 -0
  28. package/dist/assets/app-CCh--1Hc.js +5087 -0
  29. package/dist/assets/app-f-fkV5uP.css +1 -0
  30. package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
  31. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  32. package/dist/assets/{first-shot-handoff-BlT33CGf.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-BSesrCpL.js → landing-DjHD8u7k.js} +1 -1
  34. package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
  35. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  36. package/dist/assets/workflow-C4yKH1p4.js +179 -0
  37. package/dist/cozyclay-package.json +1 -1
  38. package/dist/index.html +3 -3
  39. package/dist/llms.txt +14 -0
  40. package/dist/sw.js +3 -6
  41. package/dist/workflow/index.html +5 -4
  42. package/mcp/LIVE-PROTOCOL.md +7 -47
  43. package/mcp/README.md +16 -26
  44. package/mcp/live-hub.mjs +6 -10
  45. package/mcp/package.json +1 -3
  46. package/mcp/tool-handlers.mjs +4 -168
  47. package/package.json +3 -22
  48. package/src/App.jsx +174 -2639
  49. package/src/agent-progress.js +79 -0
  50. package/src/agent-turn-mark.js +30 -0
  51. package/src/analytics.js +1 -19
  52. package/src/app-context.js +5 -0
  53. package/src/app-stage.jsx +2 -34
  54. package/src/asset-pane.jsx +6 -5
  55. package/src/camera-block.js +21 -0
  56. package/src/camera-direction.js +120 -0
  57. package/src/character-kind.js +3 -9
  58. package/src/command-bus.js +8 -6
  59. package/src/commands/cast.js +5 -34
  60. package/src/commands/elements/character.js +1 -1
  61. package/src/commands/elements/motion.js +1 -6
  62. package/src/commands/elements/object.js +6 -2
  63. package/src/commands/elements/shot.js +4 -1
  64. package/src/commands/elements.js +38 -14
  65. package/src/commands/index.js +3 -1
  66. package/src/commands/kits.js +37 -0
  67. package/src/commands/motion-timing.js +57 -0
  68. package/src/commands/motion.js +9 -152
  69. package/src/commands/motion3-compile.js +65 -0
  70. package/src/commands/motion3.js +207 -0
  71. package/src/commands/objects.js +6 -4
  72. package/src/commands/shot.js +49 -11
  73. package/src/commands/stage.js +14 -1
  74. package/src/commands/view.js +4 -1
  75. package/src/document-mirrors.js +42 -0
  76. package/src/document-store.js +4 -0
  77. package/src/domains/cast.js +19 -194
  78. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  79. package/src/domains/motion/take-source.js +51 -0
  80. package/src/domains/motion.js +60 -1838
  81. package/src/domains/scenes.js +12 -55
  82. package/src/domains/shots.js +2 -2
  83. package/src/domains/stage.js +4 -2
  84. package/src/draft-ghost.js +46 -0
  85. package/src/draft-ghost.jsx +129 -0
  86. package/src/dualview.jsx +4 -3
  87. package/src/effects/EffectsLayer.jsx +112 -0
  88. package/src/effects/effect-meshes.js +232 -0
  89. package/src/effects/effect-record.js +103 -0
  90. package/src/effects/emitter.js +59 -0
  91. package/src/effects/simulate.js +261 -0
  92. package/src/effects/state.js +59 -0
  93. package/src/execution-telemetry.js +2 -3
  94. package/src/first-success-guide.jsx +5 -5
  95. package/src/ghost-set.js +257 -0
  96. package/src/held-object-edit.jsx +113 -0
  97. package/src/held-object-pose.js +175 -0
  98. package/src/hierarchy-model.js +1 -1
  99. package/src/hierarchy-panel.jsx +1 -1
  100. package/src/imported-mesh.jsx +103 -0
  101. package/src/kits/held.js +84 -0
  102. package/src/kits/index.js +70 -0
  103. package/src/kits/smoking.js +57 -0
  104. package/src/light-blockers.js +93 -0
  105. package/src/motion/beat-board-model.js +84 -0
  106. package/src/motion/beat-board.css +124 -0
  107. package/src/motion/beat-board.jsx +59 -0
  108. package/src/motion/center-of-mass.js +66 -0
  109. package/src/motion/fix-collisions.js +850 -0
  110. package/src/motion/physics-review.js +118 -0
  111. package/src/{ardy → motion}/physics-support.js +1 -76
  112. package/src/motion/rig-snapshot.js +22 -0
  113. package/src/motion/take-segments.jsx +140 -0
  114. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  115. package/src/{ardy → motion}/timeline.css +1 -2
  116. package/src/{ardy → motion}/timeline.jsx +48 -419
  117. package/src/motion/waypoints.js +76 -0
  118. package/src/motion3/beats.js +83 -0
  119. package/src/motion3/body.js +158 -0
  120. package/src/motion3/bones.js +47 -0
  121. package/src/motion3/carried.js +47 -0
  122. package/src/motion3/compile-job.js +75 -0
  123. package/src/motion3/compile-worker.js +10 -0
  124. package/src/motion3/compiler/anchors.js +105 -0
  125. package/src/motion3/compiler/arm-orient.js +128 -0
  126. package/src/motion3/compiler/channels.js +125 -0
  127. package/src/motion3/compiler/compile.js +205 -0
  128. package/src/motion3/compiler/gait-rhythm.js +97 -0
  129. package/src/motion3/compiler/grip.js +171 -0
  130. package/src/motion3/compiler/head-steady.js +48 -0
  131. package/src/motion3/compiler/hold.js +134 -0
  132. package/src/motion3/compiler/idle.js +113 -0
  133. package/src/motion3/compiler/limbs.js +170 -0
  134. package/src/motion3/compiler/math.js +72 -0
  135. package/src/motion3/compiler/pose.js +162 -0
  136. package/src/motion3/compiler/quat.js +49 -0
  137. package/src/motion3/compiler/skeleton.js +74 -0
  138. package/src/motion3/compiler/validate-common.js +32 -0
  139. package/src/motion3/compiler/validate-goals.js +118 -0
  140. package/src/motion3/compiler/validate-objects.js +143 -0
  141. package/src/motion3/compiler/validate-parts.js +116 -0
  142. package/src/motion3/compiler/validate.js +122 -0
  143. package/src/motion3/compiler/vocab.js +73 -0
  144. package/src/motion3/compiler/walk-pelvis.js +275 -0
  145. package/src/motion3/compiler/walk.js +207 -0
  146. package/src/motion3/compiler/wrist-clamp.js +32 -0
  147. package/src/motion3/delta.js +47 -0
  148. package/src/motion3/describe.js +174 -0
  149. package/src/motion3/docs.js +27 -0
  150. package/src/motion3/edit-carry.js +21 -0
  151. package/src/motion3/fix/ease.js +16 -0
  152. package/src/motion3/fix/ground.js +37 -0
  153. package/src/motion3/fix/index.js +52 -0
  154. package/src/motion3/fix/push.js +71 -0
  155. package/src/motion3/fix/reach.js +46 -0
  156. package/src/motion3/fix/retime.js +79 -0
  157. package/src/motion3/fix/self.js +211 -0
  158. package/src/motion3/fix/slide.js +70 -0
  159. package/src/motion3/fix/turn.js +51 -0
  160. package/src/motion3/follow.js +39 -0
  161. package/src/motion3/free-motion.js +173 -0
  162. package/src/motion3/grip-choice.js +130 -0
  163. package/src/motion3/hold-plan.js +157 -0
  164. package/src/motion3/json-patch.js +77 -0
  165. package/src/motion3/key-aim.js +105 -0
  166. package/src/motion3/key-edit.js +82 -0
  167. package/src/motion3/lenient.js +127 -0
  168. package/src/motion3/object-keys.js +236 -0
  169. package/src/motion3/object-pose.js +182 -0
  170. package/src/motion3/object-tracks.js +12 -0
  171. package/src/motion3/pipeline.js +102 -0
  172. package/src/motion3/playback.js +51 -0
  173. package/src/motion3/probes.js +91 -0
  174. package/src/motion3/qa/body-checks.js +34 -0
  175. package/src/motion3/qa/check-names.js +14 -0
  176. package/src/motion3/qa/checks.js +121 -0
  177. package/src/motion3/qa/contact-checks.js +51 -0
  178. package/src/motion3/qa/explain.js +80 -0
  179. package/src/motion3/qa/fmt.js +5 -0
  180. package/src/motion3/qa/foot-checks.js +72 -0
  181. package/src/motion3/qa/geometry.js +77 -0
  182. package/src/motion3/qa/goals.js +191 -0
  183. package/src/motion3/qa/ground.js +60 -0
  184. package/src/motion3/qa/hints-contact.js +179 -0
  185. package/src/motion3/qa/hints-goal.js +160 -0
  186. package/src/motion3/qa/hints-knee.js +24 -0
  187. package/src/motion3/qa/hints-motion.js +209 -0
  188. package/src/motion3/qa/hints-object.js +91 -0
  189. package/src/motion3/qa/index.js +42 -0
  190. package/src/motion3/qa/key-checks.js +82 -0
  191. package/src/motion3/qa/near.js +96 -0
  192. package/src/motion3/qa/object-checks.js +135 -0
  193. package/src/motion3/qa/patch.js +161 -0
  194. package/src/motion3/qa/severity.js +38 -0
  195. package/src/motion3/qa/spec-checks.js +65 -0
  196. package/src/motion3/qa/style.js +218 -0
  197. package/src/motion3/qa/wrist-checks.js +69 -0
  198. package/src/motion3/qa/wrist-obs.js +60 -0
  199. package/src/motion3/refs.js +120 -0
  200. package/src/motion3/repair-tune.js +121 -0
  201. package/src/motion3/repair.js +83 -0
  202. package/src/motion3/report.js +67 -0
  203. package/src/motion3/rigs/index.js +14 -0
  204. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  205. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  207. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  208. package/src/motion3/scene.js +60 -0
  209. package/src/motion3/schema/brief.js +89 -0
  210. package/src/motion3/schema/fields.js +44 -0
  211. package/src/motion3/schema/spec-examples.js +46 -0
  212. package/src/motion3/schema/topics.js +111 -0
  213. package/src/motion3/schema/types.js +147 -0
  214. package/src/motion3/search.js +117 -0
  215. package/src/motion3/second-hand.js +112 -0
  216. package/src/motion3/skills/basic.js +82 -0
  217. package/src/motion3/skills/book.js +76 -0
  218. package/src/motion3/skills/car.js +145 -0
  219. package/src/motion3/skills/frames-auto.js +29 -0
  220. package/src/motion3/skills/index.js +83 -0
  221. package/src/motion3/skills/info.js +65 -0
  222. package/src/motion3/skills/smoke-kit.js +109 -0
  223. package/src/motion3/skills/smoke.js +160 -0
  224. package/src/motion3/spec-store.js +59 -0
  225. package/src/motion3/take-edit.js +155 -0
  226. package/src/motion3/take.js +63 -0
  227. package/src/motion3/timeline.js +127 -0
  228. package/src/motion3/views.js +121 -0
  229. package/src/motion3/walk-plan.js +119 -0
  230. package/src/object-catalog.jsx +7 -1
  231. package/src/object-colors.js +98 -0
  232. package/src/object-gizmo-parts.js +115 -0
  233. package/src/object-gizmo.jsx +37 -122
  234. package/src/object-glow.js +139 -0
  235. package/src/object-hidden.js +33 -0
  236. package/src/object-look.js +196 -0
  237. package/src/object-size.js +10 -0
  238. package/src/object-travel.js +71 -6
  239. package/src/panels/CameraPanel.jsx +7 -7
  240. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  241. package/src/panels/LightPanel.jsx +25 -4
  242. package/src/panels/ObjectLookPanel.jsx +88 -0
  243. package/src/panels/PoseImagePanel.jsx +1 -1
  244. package/src/panels/PosePanel.jsx +2 -17
  245. package/src/panels/RigControlPanel.jsx +5 -53
  246. package/src/panels/SubjectBox.jsx +4 -1
  247. package/src/panels/details.css +14 -0
  248. package/src/posestudio.jsx +4 -27
  249. package/src/project-browser.jsx +3 -4
  250. package/src/project-resources.js +18 -31
  251. package/src/project.js +13 -104
  252. package/src/props.jsx +25 -280
  253. package/src/rail-dot-drag.js +23 -0
  254. package/src/render-passes.js +10 -0
  255. package/src/resource-status.jsx +1 -0
  256. package/src/result-modal.jsx +4 -19
  257. package/src/room.jsx +20 -12
  258. package/src/scene-assets.js +8 -6
  259. package/src/scene-light-place.js +72 -0
  260. package/src/scene-lights.jsx +148 -0
  261. package/src/scene-objects.js +31 -139
  262. package/src/scenes.js +15 -105
  263. package/src/semantic-edit.js +2 -5
  264. package/src/set-pieces.jsx +183 -0
  265. package/src/settings-menu.jsx +3 -11
  266. package/src/shell/BottomDock.jsx +11 -53
  267. package/src/shell/CompositionGuideButton.jsx +20 -0
  268. package/src/shell/DetailsSlot.jsx +16 -200
  269. package/src/shell/GhostsMenuGroup.jsx +74 -0
  270. package/src/shell/LibrarySlot.jsx +1 -3
  271. package/src/shell/MenuBar.jsx +3 -3
  272. package/src/shell/PreferencesDialog.jsx +15 -264
  273. package/src/shell/PreferencesSlot.jsx +2 -8
  274. package/src/shell/ShotPreviewHeader.jsx +17 -0
  275. package/src/shell/TopBar.jsx +5 -83
  276. package/src/shell/ViewportToolbar.jsx +23 -93
  277. package/src/shell/agent-glass.css +19 -6
  278. package/src/shell/details-selection.js +42 -0
  279. package/src/shell/ghosts-menu.css +22 -0
  280. package/src/shell/glass-regions.css +29 -3
  281. package/src/shell/preferences.css +6 -1
  282. package/src/shell/viewport.css +12 -0
  283. package/src/shot-authoring.js +4 -7
  284. package/src/soft-shadows.js +37 -0
  285. package/src/stage-attach-frames.js +12 -12
  286. package/src/stage-canvas-rigs.jsx +3 -3
  287. package/src/stage-capture.jsx +42 -8
  288. package/src/stage-characters.jsx +22 -216
  289. package/src/stage-editor-gizmos.jsx +6 -6
  290. package/src/stage-environment.js +93 -0
  291. package/src/stage-hdr-builtins.js +107 -0
  292. package/src/stage-hdr-image.js +116 -0
  293. package/src/stage-hdr-library.js +70 -0
  294. package/src/stage-hdr-map.js +94 -0
  295. package/src/stage-hdr.js +130 -0
  296. package/src/stage-lighting.js +149 -0
  297. package/src/stage-occlusion.js +210 -0
  298. package/src/stage-sky.jsx +159 -0
  299. package/src/stage-startup-defaults.js +5 -9
  300. package/src/studio-actions.js +12 -14
  301. package/src/studio-agent-commands.js +46 -16
  302. package/src/studio-agent-motion.js +9 -9
  303. package/src/studio-agent-protocol.js +46 -73
  304. package/src/studio-app-binding.js +29 -45
  305. package/src/studio-asset-list.js +17 -0
  306. package/src/studio-contact-sheet.js +37 -5
  307. package/src/studio-elements.js +27 -7
  308. package/src/studio-entity-bounds.js +18 -0
  309. package/src/studio-error-codes.js +8 -0
  310. package/src/studio-export-framing.js +152 -0
  311. package/src/studio-lighting-check.js +103 -0
  312. package/src/studio-motion-visual.js +150 -0
  313. package/src/studio-schema-errors.js +80 -0
  314. package/src/studio-support-gap.js +74 -0
  315. package/src/styles.css +1 -19
  316. package/src/timeline-defaults.js +5 -0
  317. package/src/timeline-extent.js +8 -20
  318. package/src/tutorial-walk.js +17 -0
  319. package/src/ui-scrub.js +1 -1
  320. package/src/use-shot-camera-tracks.js +12 -0
  321. package/src/view-ghosts.js +97 -0
  322. package/src/view-ghosts.jsx +49 -0
  323. package/src/workflow/AgentPanel.jsx +47 -166
  324. package/src/workflow/AgentProgressCard.jsx +107 -0
  325. package/src/workflow/AgentReplyText.jsx +37 -0
  326. package/src/workflow/AgentTurnSteps.jsx +84 -0
  327. package/src/workflow/LoginPanel.jsx +140 -0
  328. package/src/workflow/WorkflowBuilder.jsx +7 -23
  329. package/src/workflow/agent-client.js +54 -51
  330. package/src/workflow/agent-panel.css +71 -1
  331. package/src/workflow/agent-progress-card.css +167 -0
  332. package/src/workflow/agent-progress-view.js +47 -0
  333. package/src/workflow/agent-turn-steps.css +113 -0
  334. package/src/workflow/agent-turn.js +191 -0
  335. package/src/workflow/login-client.js +34 -0
  336. package/src/workflow/motion-input.js +18 -31
  337. package/src/workflow/node-schema.js +1 -1
  338. package/src/workflow/reply-markdown.js +69 -0
  339. package/src/workflow/scene-asset-sync.js +8 -8
  340. package/tools/agent-session-stats.mjs +61 -0
  341. package/tools/dev-full.mjs +5 -56
  342. package/tools/eslint-baseline.json +12 -13
  343. package/tools/headless-studio.mjs +85 -0
  344. package/tools/knip-baseline.json +0 -179
  345. package/tools/loc-baseline.json +33 -83
  346. package/tools/motion3/extract-body.mjs +103 -0
  347. package/tools/motion3/extract-rig.mjs +85 -0
  348. package/tools/process-supervisor.mjs +0 -88
  349. package/tools/run-tests.mjs +9 -124
  350. package/bin/agent/motion-runtime.mjs +0 -76
  351. package/dist/ardy/cskel27-rest.json +0 -557
  352. package/dist/assets/analytics-B41FmPYI.js +0 -1
  353. package/dist/assets/app-Do-Og5Pu.css +0 -1
  354. package/dist/assets/app-DwP-tl2x.js +0 -4920
  355. package/dist/assets/config-Bx7pYsD9.js +0 -1
  356. package/dist/assets/config-DeoFTYzb.css +0 -1
  357. package/dist/assets/demo-Dt0qZfS7.js +0 -1
  358. package/dist/assets/scene-asset-sync-BCWDJB1d.js +0 -19
  359. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  360. package/dist/assets/ticket-jqMhqL88.js +0 -1
  361. package/dist/assets/workflow-Br-K7Srs.js +0 -179
  362. package/dist/demo/walk-then-stop.npz +0 -0
  363. package/mcp/ardy-prompts.mjs +0 -218
  364. package/src/ardy/auto-fix-panel.css +0 -108
  365. package/src/ardy/auto-physics.js +0 -1194
  366. package/src/ardy/client.js +0 -153
  367. package/src/ardy/convert.js +0 -313
  368. package/src/ardy/cskel27-neutral.js +0 -39
  369. package/src/ardy/cskel27.js +0 -77
  370. package/src/ardy/export.js +0 -165
  371. package/src/ardy/fix-collisions.js +0 -2257
  372. package/src/ardy/foot-lock.js +0 -230
  373. package/src/ardy/motion-calibration.js +0 -118
  374. package/src/ardy/motion-edit.js +0 -213
  375. package/src/ardy/motion-url.js +0 -55
  376. package/src/ardy/mp4-duration.js +0 -102
  377. package/src/ardy/npz.js +0 -595
  378. package/src/ardy/physics-panel.css +0 -21
  379. package/src/ardy/physics-panel.jsx +0 -89
  380. package/src/ardy/physics-refine.js +0 -111
  381. package/src/ardy/physics-review.js +0 -567
  382. package/src/ardy/physics-temporal.js +0 -54
  383. package/src/ardy/platform-fit-panel.css +0 -64
  384. package/src/ardy/platform-fit-panel.jsx +0 -41
  385. package/src/ardy/platform-fit.js +0 -326
  386. package/src/ardy/playback.js +0 -711
  387. package/src/ardy/pose-pin.js +0 -97
  388. package/src/ardy/prompt-clips.js +0 -30
  389. package/src/ardy/retime.js +0 -211
  390. package/src/ardy/root-drop.js +0 -369
  391. package/src/ardy/timeline-resize.js +0 -13
  392. package/src/ardy/to-cskel27.js +0 -225
  393. package/src/ardy/trim.js +0 -33
  394. package/src/ardy/waypoints.js +0 -369
  395. package/src/ardy-bridge-contract.js +0 -281
  396. package/src/domains/motion/motion-video-capture.js +0 -664
  397. package/src/i2v-motion-client.js +0 -77
  398. package/src/i2v-motion-studio.jsx +0 -180
  399. package/src/line-edit.js +0 -1348
  400. package/src/motion/generation.js +0 -105
  401. package/src/motion-readiness-ui.jsx +0 -67
  402. package/src/motion-readiness.js +0 -40
  403. package/src/motion-resources.js +0 -177
  404. package/src/motion-store.js +0 -65
  405. package/src/motion-trail.js +0 -601
  406. package/src/multimodel-ingest.js +0 -394
  407. package/src/panels/PromptBlocksPanel.jsx +0 -444
  408. package/src/panels/VideoCapturePanel.jsx +0 -181
  409. package/src/pose-extract/browser.js +0 -42
  410. package/src/pose-extract/detector.js +0 -100
  411. package/src/pose-extract/fit.js +0 -227
  412. package/src/pose-extract/image-frame.js +0 -51
  413. package/src/pose-extract/index.js +0 -58
  414. package/src/pose-extract/keyframes.js +0 -74
  415. package/src/pose-extract/landmarks.js +0 -162
  416. package/src/pose-extract/mirror.js +0 -153
  417. package/src/pose-extract/take.js +0 -131
  418. package/src/pose-extract/video-frames.js +0 -91
  419. package/src/take-recipe.js +0 -165
  420. package/src/trail-key-conflicts.js +0 -39
  421. package/src/trail-pick.js +0 -59
  422. package/src/use-i2v-capture-modal.js +0 -15
  423. package/tools/ardy/BRIDGE.md +0 -37
  424. package/tools/ardy/GVHMR-NPZ.md +0 -68
  425. package/tools/ardy/README.md +0 -124
  426. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  427. package/tools/ardy/artifacts.mjs +0 -34
  428. package/tools/ardy/bridge.mjs +0 -1732
  429. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  430. package/tools/ardy/cclay_constrained_generate.py +0 -561
  431. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  432. package/tools/ardy/cclay_motion_edit.py +0 -510
  433. package/tools/ardy/cclay_sequence_generate.py +0 -705
  434. package/tools/ardy/cclay_worker.py +0 -304
  435. package/tools/ardy/dump-npz.py +0 -205
  436. package/tools/ardy/extract-rest.mjs +0 -299
  437. package/tools/ardy/extract.mjs +0 -507
  438. package/tools/ardy/footage.mjs +0 -467
  439. package/tools/ardy/gvhmr-floor.mjs +0 -102
  440. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  441. package/tools/ardy/gvhmr_fastpath.py +0 -168
  442. package/tools/ardy/gvhmr_trajectory.py +0 -240
  443. package/tools/ardy/merge-text-encoder.py +0 -160
  444. package/tools/ardy/mocap-metrics.mjs +0 -200
  445. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  446. package/tools/ardy/motion-stabilize.mjs +0 -348
  447. package/tools/ardy/npz.mjs +0 -414
  448. package/tools/ardy/pose-to-npz.mjs +0 -106
  449. package/tools/ardy/prompt-limits.mjs +0 -5
  450. package/tools/ardy/run-edit-on-box.sh +0 -73
  451. package/tools/ardy/run-local.mjs +0 -233
  452. package/tools/ardy/run-on-box.sh +0 -593
  453. package/tools/ardy/run-sequence-on-box.sh +0 -177
  454. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  455. package/tools/ardy/runners/index.mjs +0 -18
  456. package/tools/ardy/runners/local.mjs +0 -455
  457. package/tools/ardy/runners/proc.mjs +0 -125
  458. package/tools/ardy/runners/remote.mjs +0 -252
  459. package/tools/ardy/setup-local.mjs +0 -189
  460. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  461. package/tools/ardy/setup-text-encoder.py +0 -306
  462. package/tools/ardy/smpl-cskel27.mjs +0 -178
  463. package/tools/ardy/visual-qa.mjs +0 -196
  464. package/tools/ardy/vq-car.mjs +0 -52
  465. package/tools/bench/EXP3.md +0 -61
  466. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  467. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  468. package/tools/bench/cclay_bench_runner.py +0 -42
  469. package/tools/bench/cube-contact.mjs +0 -162
  470. package/tools/bench/exp3.mjs +0 -152
  471. package/tools/bench/extract-bench-lib.mjs +0 -216
  472. package/tools/bench/extract-bench.mjs +0 -302
  473. package/tools/bench/fal-generate.mjs +0 -88
  474. package/tools/bench/fit/README.md +0 -189
  475. package/tools/bench/fit/camera.mjs +0 -59
  476. package/tools/bench/fit/contact.mjs +0 -419
  477. package/tools/bench/fit/footlock.mjs +0 -234
  478. package/tools/bench/fit/motion.mjs +0 -85
  479. package/tools/bench/fit/pin.mjs +0 -27
  480. package/tools/bench/fit/remote.mjs +0 -105
  481. package/tools/bench/fit-bench.mjs +0 -126
  482. package/tools/bench/fit-sanity.mjs +0 -45
  483. package/tools/bench/metrics.mjs +0 -313
  484. package/tools/bench/obs/depth.mjs +0 -258
  485. package/tools/bench/obs/extrinsics.mjs +0 -175
  486. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  487. package/tools/bench/obs/ground.mjs +0 -294
  488. package/tools/bench/obs/heading.mjs +0 -151
  489. package/tools/bench/obs/ladder.mjs +0 -555
  490. package/tools/bench/obs/mannequin-betas.json +0 -124
  491. package/tools/bench/obs/remote.mjs +0 -83
  492. package/tools/bench/obs/rest_joints.py +0 -52
  493. package/tools/bench/obs/ybot-targets.mjs +0 -49
  494. package/tools/bench/obs-bench.mjs +0 -520
  495. package/tools/bench/score.mjs +0 -343
  496. package/tools/bench/summarize.mjs +0 -86
  497. package/tools/demo-worker/README.md +0 -137
  498. package/tools/demo-worker/api-client.mjs +0 -233
  499. package/tools/demo-worker/generate.mjs +0 -301
  500. package/tools/demo-worker/index.mjs +0 -466
  501. package/tools/demo-worker/sign-core.mjs +0 -112
  502. package/tools/demo-worker/sign.mjs +0 -161
  503. package/tools/gt-render/browser.mjs +0 -159
  504. package/tools/gt-render/camera-math.mjs +0 -276
  505. package/tools/gt-render/page.mjs +0 -267
  506. package/tools/gt-render/render.mjs +0 -514
  507. package/tools/gt-render/scene-box.mjs +0 -31
  508. package/tools/gt-render/take-transform.mjs +0 -87
  509. package/tools/kimodo/compare-seam.mjs +0 -108
  510. package/tools/kimodo/constraints.mjs +0 -244
  511. package/tools/kimodo/edit.mjs +0 -259
  512. package/tools/kimodo/effector-constraints.mjs +0 -200
  513. package/tools/kimodo/generate.mjs +0 -646
  514. package/tools/kimodo/local-output.mjs +0 -265
  515. package/tools/kimodo/measure-pose.mjs +0 -65
  516. package/tools/kimodo/measure-preserve.mjs +0 -899
  517. package/tools/kimodo/measure-waypoints.mjs +0 -55
  518. package/tools/kimodo/mlx-generate.py +0 -111
  519. package/tools/kimodo/pose-constraints.mjs +0 -248
  520. package/tools/kimodo/preserve-mask.mjs +0 -543
  521. package/tools/kimodo/read-npz.mjs +0 -189
  522. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  523. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  524. package/tools/kimodo/runner.mjs +0 -284
  525. package/tools/kimodo/seam-batch.mjs +0 -77
  526. package/tools/kimodo/setup-local.mjs +0 -30
  527. package/tools/kimodo/setup-on-box.sh +0 -339
  528. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  529. package/tools/morphgs/assets/gen_truth.py +0 -39
  530. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  531. package/tools/morphgs/assets/playback-check.mjs +0 -30
  532. package/tools/morphgs/demo-gate.sh +0 -26
  533. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  534. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  535. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  536. package/tools/morphgs/setup-on-cluster.sh +0 -126
  537. package/tools/projflow/driver.py +0 -1069
  538. package/tools/projflow/generate.mjs +0 -805
  539. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  540. package/tools/projflow/line-edit-job.mjs +0 -547
  541. package/tools/projflow/replay.mjs +0 -452
  542. package/tools/projflow/runner.mjs +0 -269
  543. package/tools/projflow/service.mjs +0 -742
  544. package/tools/track/DESIGN.md +0 -513
  545. package/tools/track/backfill-provenance.mjs +0 -116
  546. package/tools/track/budget.mjs +0 -8
  547. package/tools/track/check-rig.mjs +0 -209
  548. package/tools/track/diagnostics.schema.json +0 -60
  549. package/tools/track/export-rig.mjs +0 -256
  550. package/tools/track/fallback.mjs +0 -7
  551. package/tools/track/fk-parity-fixture.mjs +0 -161
  552. package/tools/track/gate.mjs +0 -349
  553. package/tools/track/masks.mjs +0 -318
  554. package/tools/track/metrics.mjs +0 -201
  555. package/tools/track/publish-obs.mjs +0 -108
  556. package/tools/track/py/check_env.py +0 -61
  557. package/tools/track/py/eval_lr.py +0 -229
  558. package/tools/track/py/lr_viterbi.py +0 -294
  559. package/tools/track/py/masks.py +0 -405
  560. package/tools/track/py/objective.py +0 -382
  561. package/tools/track/py/rig.py +0 -508
  562. package/tools/track/py/scene.py +0 -326
  563. package/tools/track/py/test_joint_indices.py +0 -123
  564. package/tools/track/py/test_lr_viterbi.py +0 -195
  565. package/tools/track/py/test_masks.py +0 -229
  566. package/tools/track/py/test_rig.py +0 -288
  567. package/tools/track/py/test_scene.py +0 -354
  568. package/tools/track/py/test_track.py +0 -328
  569. package/tools/track/py/track.py +0 -411
  570. package/tools/track/remote.mjs +0 -140
  571. package/tools/track/rig-dump.mjs +0 -227
  572. package/tools/track/run-box-tests.mjs +0 -14
  573. package/tools/track/run-box.mjs +0 -168
  574. package/tools/track/setup-box.sh +0 -59
  575. package/tools/track/study-2d.mjs +0 -728
  576. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  577. /package/src/{ardy → motion}/ground.js +0 -0
  578. /package/src/{ardy → motion}/ik-drag.js +0 -0
  579. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  580. /package/src/{ardy → motion}/ik.js +0 -0
  581. /package/src/{ardy → motion}/physics-surface.js +0 -0
  582. /package/src/{ardy → motion}/playback-clock.js +0 -0
  583. /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
- }