cozyclay 2.1.1 → 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 (577) hide show
  1. package/CHANGELOG.md +60 -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 +3 -3
  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-msLI71g1.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-ChUvB_zL.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/sw.js +3 -6
  40. package/dist/workflow/index.html +5 -4
  41. package/mcp/LIVE-PROTOCOL.md +7 -47
  42. package/mcp/README.md +16 -26
  43. package/mcp/live-hub.mjs +6 -10
  44. package/mcp/package.json +1 -3
  45. package/mcp/tool-handlers.mjs +4 -168
  46. package/package.json +3 -22
  47. package/src/App.jsx +150 -2624
  48. package/src/agent-progress.js +79 -0
  49. package/src/agent-turn-mark.js +30 -0
  50. package/src/analytics.js +1 -19
  51. package/src/app-context.js +5 -0
  52. package/src/app-stage.jsx +2 -34
  53. package/src/asset-pane.jsx +6 -5
  54. package/src/camera-block.js +21 -0
  55. package/src/camera-direction.js +120 -0
  56. package/src/character-kind.js +3 -9
  57. package/src/command-bus.js +8 -6
  58. package/src/commands/cast.js +5 -34
  59. package/src/commands/elements/character.js +1 -1
  60. package/src/commands/elements/motion.js +1 -6
  61. package/src/commands/elements/object.js +6 -2
  62. package/src/commands/elements/shot.js +4 -1
  63. package/src/commands/elements.js +38 -14
  64. package/src/commands/index.js +3 -1
  65. package/src/commands/kits.js +37 -0
  66. package/src/commands/motion-timing.js +57 -0
  67. package/src/commands/motion.js +9 -152
  68. package/src/commands/motion3-compile.js +65 -0
  69. package/src/commands/motion3.js +207 -0
  70. package/src/commands/objects.js +6 -4
  71. package/src/commands/shot.js +49 -11
  72. package/src/commands/stage.js +14 -1
  73. package/src/commands/view.js +4 -1
  74. package/src/document-mirrors.js +42 -0
  75. package/src/document-store.js +4 -0
  76. package/src/domains/cast.js +19 -194
  77. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  78. package/src/domains/motion/take-source.js +51 -0
  79. package/src/domains/motion.js +60 -1838
  80. package/src/domains/scenes.js +12 -55
  81. package/src/domains/shots.js +2 -2
  82. package/src/domains/stage.js +4 -2
  83. package/src/draft-ghost.js +46 -0
  84. package/src/draft-ghost.jsx +129 -0
  85. package/src/dualview.jsx +4 -3
  86. package/src/effects/EffectsLayer.jsx +112 -0
  87. package/src/effects/effect-meshes.js +232 -0
  88. package/src/effects/effect-record.js +103 -0
  89. package/src/effects/emitter.js +59 -0
  90. package/src/effects/simulate.js +261 -0
  91. package/src/effects/state.js +59 -0
  92. package/src/execution-telemetry.js +2 -3
  93. package/src/first-success-guide.jsx +5 -5
  94. package/src/ghost-set.js +257 -0
  95. package/src/held-object-edit.jsx +113 -0
  96. package/src/held-object-pose.js +175 -0
  97. package/src/hierarchy-model.js +1 -1
  98. package/src/hierarchy-panel.jsx +1 -1
  99. package/src/imported-mesh.jsx +103 -0
  100. package/src/kits/held.js +84 -0
  101. package/src/kits/index.js +70 -0
  102. package/src/kits/smoking.js +57 -0
  103. package/src/light-blockers.js +93 -0
  104. package/src/motion/beat-board-model.js +84 -0
  105. package/src/motion/beat-board.css +124 -0
  106. package/src/motion/beat-board.jsx +59 -0
  107. package/src/motion/center-of-mass.js +66 -0
  108. package/src/motion/fix-collisions.js +850 -0
  109. package/src/motion/physics-review.js +118 -0
  110. package/src/{ardy → motion}/physics-support.js +1 -76
  111. package/src/motion/rig-snapshot.js +22 -0
  112. package/src/motion/take-segments.jsx +140 -0
  113. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  114. package/src/{ardy → motion}/timeline.css +1 -2
  115. package/src/{ardy → motion}/timeline.jsx +42 -413
  116. package/src/motion/waypoints.js +76 -0
  117. package/src/motion3/beats.js +83 -0
  118. package/src/motion3/body.js +158 -0
  119. package/src/motion3/bones.js +47 -0
  120. package/src/motion3/carried.js +47 -0
  121. package/src/motion3/compile-job.js +75 -0
  122. package/src/motion3/compile-worker.js +10 -0
  123. package/src/motion3/compiler/anchors.js +105 -0
  124. package/src/motion3/compiler/arm-orient.js +128 -0
  125. package/src/motion3/compiler/channels.js +125 -0
  126. package/src/motion3/compiler/compile.js +205 -0
  127. package/src/motion3/compiler/gait-rhythm.js +97 -0
  128. package/src/motion3/compiler/grip.js +171 -0
  129. package/src/motion3/compiler/head-steady.js +48 -0
  130. package/src/motion3/compiler/hold.js +134 -0
  131. package/src/motion3/compiler/idle.js +113 -0
  132. package/src/motion3/compiler/limbs.js +170 -0
  133. package/src/motion3/compiler/math.js +72 -0
  134. package/src/motion3/compiler/pose.js +162 -0
  135. package/src/motion3/compiler/quat.js +49 -0
  136. package/src/motion3/compiler/skeleton.js +74 -0
  137. package/src/motion3/compiler/validate-common.js +32 -0
  138. package/src/motion3/compiler/validate-goals.js +118 -0
  139. package/src/motion3/compiler/validate-objects.js +143 -0
  140. package/src/motion3/compiler/validate-parts.js +116 -0
  141. package/src/motion3/compiler/validate.js +122 -0
  142. package/src/motion3/compiler/vocab.js +73 -0
  143. package/src/motion3/compiler/walk-pelvis.js +275 -0
  144. package/src/motion3/compiler/walk.js +207 -0
  145. package/src/motion3/compiler/wrist-clamp.js +32 -0
  146. package/src/motion3/delta.js +47 -0
  147. package/src/motion3/describe.js +174 -0
  148. package/src/motion3/docs.js +27 -0
  149. package/src/motion3/edit-carry.js +21 -0
  150. package/src/motion3/fix/ease.js +16 -0
  151. package/src/motion3/fix/ground.js +37 -0
  152. package/src/motion3/fix/index.js +52 -0
  153. package/src/motion3/fix/push.js +71 -0
  154. package/src/motion3/fix/reach.js +46 -0
  155. package/src/motion3/fix/retime.js +79 -0
  156. package/src/motion3/fix/self.js +211 -0
  157. package/src/motion3/fix/slide.js +70 -0
  158. package/src/motion3/fix/turn.js +51 -0
  159. package/src/motion3/follow.js +39 -0
  160. package/src/motion3/free-motion.js +173 -0
  161. package/src/motion3/grip-choice.js +130 -0
  162. package/src/motion3/hold-plan.js +157 -0
  163. package/src/motion3/json-patch.js +77 -0
  164. package/src/motion3/key-aim.js +105 -0
  165. package/src/motion3/key-edit.js +82 -0
  166. package/src/motion3/lenient.js +127 -0
  167. package/src/motion3/object-keys.js +236 -0
  168. package/src/motion3/object-pose.js +182 -0
  169. package/src/motion3/object-tracks.js +12 -0
  170. package/src/motion3/pipeline.js +102 -0
  171. package/src/motion3/playback.js +51 -0
  172. package/src/motion3/probes.js +91 -0
  173. package/src/motion3/qa/body-checks.js +34 -0
  174. package/src/motion3/qa/check-names.js +14 -0
  175. package/src/motion3/qa/checks.js +121 -0
  176. package/src/motion3/qa/contact-checks.js +51 -0
  177. package/src/motion3/qa/explain.js +80 -0
  178. package/src/motion3/qa/fmt.js +5 -0
  179. package/src/motion3/qa/foot-checks.js +72 -0
  180. package/src/motion3/qa/geometry.js +77 -0
  181. package/src/motion3/qa/goals.js +191 -0
  182. package/src/motion3/qa/ground.js +60 -0
  183. package/src/motion3/qa/hints-contact.js +179 -0
  184. package/src/motion3/qa/hints-goal.js +160 -0
  185. package/src/motion3/qa/hints-knee.js +24 -0
  186. package/src/motion3/qa/hints-motion.js +209 -0
  187. package/src/motion3/qa/hints-object.js +91 -0
  188. package/src/motion3/qa/index.js +42 -0
  189. package/src/motion3/qa/key-checks.js +82 -0
  190. package/src/motion3/qa/near.js +96 -0
  191. package/src/motion3/qa/object-checks.js +135 -0
  192. package/src/motion3/qa/patch.js +161 -0
  193. package/src/motion3/qa/severity.js +38 -0
  194. package/src/motion3/qa/spec-checks.js +65 -0
  195. package/src/motion3/qa/style.js +218 -0
  196. package/src/motion3/qa/wrist-checks.js +69 -0
  197. package/src/motion3/qa/wrist-obs.js +60 -0
  198. package/src/motion3/refs.js +120 -0
  199. package/src/motion3/repair-tune.js +121 -0
  200. package/src/motion3/repair.js +83 -0
  201. package/src/motion3/report.js +67 -0
  202. package/src/motion3/rigs/index.js +14 -0
  203. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  204. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  205. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  207. package/src/motion3/scene.js +60 -0
  208. package/src/motion3/schema/brief.js +89 -0
  209. package/src/motion3/schema/fields.js +44 -0
  210. package/src/motion3/schema/spec-examples.js +46 -0
  211. package/src/motion3/schema/topics.js +111 -0
  212. package/src/motion3/schema/types.js +147 -0
  213. package/src/motion3/search.js +117 -0
  214. package/src/motion3/second-hand.js +112 -0
  215. package/src/motion3/skills/basic.js +82 -0
  216. package/src/motion3/skills/book.js +76 -0
  217. package/src/motion3/skills/car.js +145 -0
  218. package/src/motion3/skills/frames-auto.js +29 -0
  219. package/src/motion3/skills/index.js +83 -0
  220. package/src/motion3/skills/info.js +65 -0
  221. package/src/motion3/skills/smoke-kit.js +109 -0
  222. package/src/motion3/skills/smoke.js +160 -0
  223. package/src/motion3/spec-store.js +59 -0
  224. package/src/motion3/take-edit.js +155 -0
  225. package/src/motion3/take.js +63 -0
  226. package/src/motion3/timeline.js +127 -0
  227. package/src/motion3/views.js +121 -0
  228. package/src/motion3/walk-plan.js +119 -0
  229. package/src/object-catalog.jsx +7 -1
  230. package/src/object-colors.js +98 -0
  231. package/src/object-gizmo-parts.js +115 -0
  232. package/src/object-gizmo.jsx +37 -122
  233. package/src/object-glow.js +139 -0
  234. package/src/object-hidden.js +33 -0
  235. package/src/object-look.js +196 -0
  236. package/src/object-size.js +10 -0
  237. package/src/object-travel.js +71 -6
  238. package/src/panels/CameraPanel.jsx +7 -7
  239. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  240. package/src/panels/LightPanel.jsx +25 -4
  241. package/src/panels/ObjectLookPanel.jsx +88 -0
  242. package/src/panels/PosePanel.jsx +2 -17
  243. package/src/panels/RigControlPanel.jsx +5 -53
  244. package/src/panels/SubjectBox.jsx +4 -1
  245. package/src/panels/details.css +14 -0
  246. package/src/posestudio.jsx +4 -27
  247. package/src/project-browser.jsx +3 -4
  248. package/src/project-resources.js +18 -31
  249. package/src/project.js +13 -104
  250. package/src/props.jsx +25 -280
  251. package/src/rail-dot-drag.js +23 -0
  252. package/src/render-passes.js +10 -0
  253. package/src/resource-status.jsx +1 -0
  254. package/src/result-modal.jsx +4 -19
  255. package/src/room.jsx +20 -12
  256. package/src/scene-assets.js +8 -6
  257. package/src/scene-light-place.js +72 -0
  258. package/src/scene-lights.jsx +148 -0
  259. package/src/scene-objects.js +31 -139
  260. package/src/scenes.js +15 -105
  261. package/src/semantic-edit.js +2 -5
  262. package/src/set-pieces.jsx +183 -0
  263. package/src/settings-menu.jsx +3 -11
  264. package/src/shell/BottomDock.jsx +9 -51
  265. package/src/shell/DetailsSlot.jsx +16 -200
  266. package/src/shell/GhostsMenuGroup.jsx +74 -0
  267. package/src/shell/LibrarySlot.jsx +1 -3
  268. package/src/shell/MenuBar.jsx +3 -3
  269. package/src/shell/PreferencesDialog.jsx +15 -264
  270. package/src/shell/PreferencesSlot.jsx +2 -8
  271. package/src/shell/TopBar.jsx +5 -84
  272. package/src/shell/ViewportToolbar.jsx +10 -70
  273. package/src/shell/agent-glass.css +19 -6
  274. package/src/shell/details-selection.js +42 -0
  275. package/src/shell/ghosts-menu.css +22 -0
  276. package/src/shell/glass-regions.css +29 -3
  277. package/src/shell/preferences.css +6 -1
  278. package/src/shot-authoring.js +4 -7
  279. package/src/soft-shadows.js +37 -0
  280. package/src/stage-attach-frames.js +12 -12
  281. package/src/stage-canvas-rigs.jsx +3 -3
  282. package/src/stage-capture.jsx +42 -8
  283. package/src/stage-characters.jsx +22 -216
  284. package/src/stage-editor-gizmos.jsx +6 -6
  285. package/src/stage-environment.js +93 -0
  286. package/src/stage-hdr-builtins.js +107 -0
  287. package/src/stage-hdr-image.js +116 -0
  288. package/src/stage-hdr-library.js +70 -0
  289. package/src/stage-hdr-map.js +94 -0
  290. package/src/stage-hdr.js +130 -0
  291. package/src/stage-lighting.js +149 -0
  292. package/src/stage-occlusion.js +210 -0
  293. package/src/stage-sky.jsx +159 -0
  294. package/src/stage-startup-defaults.js +5 -9
  295. package/src/studio-actions.js +12 -14
  296. package/src/studio-agent-commands.js +19 -16
  297. package/src/studio-agent-motion.js +9 -9
  298. package/src/studio-agent-protocol.js +41 -71
  299. package/src/studio-app-binding.js +25 -45
  300. package/src/studio-asset-list.js +17 -0
  301. package/src/studio-contact-sheet.js +37 -5
  302. package/src/studio-elements.js +27 -7
  303. package/src/studio-entity-bounds.js +18 -0
  304. package/src/studio-error-codes.js +8 -0
  305. package/src/studio-export-framing.js +152 -0
  306. package/src/studio-lighting-check.js +103 -0
  307. package/src/studio-motion-visual.js +150 -0
  308. package/src/studio-schema-errors.js +80 -0
  309. package/src/studio-support-gap.js +74 -0
  310. package/src/styles.css +1 -19
  311. package/src/timeline-defaults.js +5 -0
  312. package/src/timeline-extent.js +8 -20
  313. package/src/tutorial-walk.js +17 -0
  314. package/src/ui-scrub.js +1 -1
  315. package/src/use-shot-camera-tracks.js +12 -0
  316. package/src/view-ghosts.js +97 -0
  317. package/src/view-ghosts.jsx +49 -0
  318. package/src/workflow/AgentPanel.jsx +47 -166
  319. package/src/workflow/AgentProgressCard.jsx +107 -0
  320. package/src/workflow/AgentReplyText.jsx +37 -0
  321. package/src/workflow/AgentTurnSteps.jsx +84 -0
  322. package/src/workflow/LoginPanel.jsx +140 -0
  323. package/src/workflow/WorkflowBuilder.jsx +7 -23
  324. package/src/workflow/agent-client.js +54 -51
  325. package/src/workflow/agent-panel.css +71 -1
  326. package/src/workflow/agent-progress-card.css +167 -0
  327. package/src/workflow/agent-progress-view.js +47 -0
  328. package/src/workflow/agent-turn-steps.css +113 -0
  329. package/src/workflow/agent-turn.js +191 -0
  330. package/src/workflow/login-client.js +34 -0
  331. package/src/workflow/motion-input.js +18 -31
  332. package/src/workflow/node-schema.js +1 -1
  333. package/src/workflow/reply-markdown.js +69 -0
  334. package/src/workflow/scene-asset-sync.js +8 -8
  335. package/tools/agent-session-stats.mjs +61 -0
  336. package/tools/dev-full.mjs +5 -56
  337. package/tools/eslint-baseline.json +12 -13
  338. package/tools/knip-baseline.json +0 -179
  339. package/tools/loc-baseline.json +31 -81
  340. package/tools/motion3/extract-body.mjs +103 -0
  341. package/tools/motion3/extract-rig.mjs +85 -0
  342. package/tools/process-supervisor.mjs +0 -88
  343. package/tools/run-tests.mjs +8 -124
  344. package/bin/agent/motion-runtime.mjs +0 -76
  345. package/dist/ardy/cskel27-rest.json +0 -557
  346. package/dist/assets/analytics-C9pMpW7s.js +0 -1
  347. package/dist/assets/app-B_2XB9rw.css +0 -1
  348. package/dist/assets/app-DAuvKPP8.js +0 -4920
  349. package/dist/assets/config-Bx7pYsD9.js +0 -1
  350. package/dist/assets/config-DeoFTYzb.css +0 -1
  351. package/dist/assets/demo-CvKuBgy5.js +0 -1
  352. package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
  353. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  354. package/dist/assets/ticket-BPk2Ni95.js +0 -1
  355. package/dist/assets/workflow-jJRBR94Z.js +0 -179
  356. package/dist/demo/walk-then-stop.npz +0 -0
  357. package/mcp/ardy-prompts.mjs +0 -218
  358. package/src/ardy/auto-fix-panel.css +0 -108
  359. package/src/ardy/auto-physics.js +0 -1194
  360. package/src/ardy/client.js +0 -153
  361. package/src/ardy/convert.js +0 -313
  362. package/src/ardy/cskel27-neutral.js +0 -39
  363. package/src/ardy/cskel27.js +0 -77
  364. package/src/ardy/export.js +0 -165
  365. package/src/ardy/fix-collisions.js +0 -2257
  366. package/src/ardy/foot-lock.js +0 -230
  367. package/src/ardy/motion-calibration.js +0 -118
  368. package/src/ardy/motion-edit.js +0 -213
  369. package/src/ardy/motion-url.js +0 -55
  370. package/src/ardy/mp4-duration.js +0 -102
  371. package/src/ardy/npz.js +0 -595
  372. package/src/ardy/physics-panel.css +0 -21
  373. package/src/ardy/physics-panel.jsx +0 -89
  374. package/src/ardy/physics-refine.js +0 -111
  375. package/src/ardy/physics-review.js +0 -567
  376. package/src/ardy/physics-temporal.js +0 -54
  377. package/src/ardy/platform-fit-panel.css +0 -64
  378. package/src/ardy/platform-fit-panel.jsx +0 -41
  379. package/src/ardy/platform-fit.js +0 -326
  380. package/src/ardy/playback.js +0 -711
  381. package/src/ardy/pose-pin.js +0 -97
  382. package/src/ardy/prompt-clips.js +0 -30
  383. package/src/ardy/retime.js +0 -211
  384. package/src/ardy/root-drop.js +0 -369
  385. package/src/ardy/timeline-resize.js +0 -13
  386. package/src/ardy/to-cskel27.js +0 -225
  387. package/src/ardy/trim.js +0 -33
  388. package/src/ardy/waypoints.js +0 -369
  389. package/src/ardy-bridge-contract.js +0 -281
  390. package/src/domains/motion/motion-video-capture.js +0 -664
  391. package/src/i2v-motion-client.js +0 -77
  392. package/src/i2v-motion-studio.jsx +0 -180
  393. package/src/line-edit.js +0 -1348
  394. package/src/motion/generation.js +0 -105
  395. package/src/motion-readiness-ui.jsx +0 -67
  396. package/src/motion-readiness.js +0 -40
  397. package/src/motion-resources.js +0 -177
  398. package/src/motion-store.js +0 -65
  399. package/src/motion-trail.js +0 -601
  400. package/src/multimodel-ingest.js +0 -394
  401. package/src/panels/PromptBlocksPanel.jsx +0 -444
  402. package/src/panels/VideoCapturePanel.jsx +0 -181
  403. package/src/pose-extract/browser.js +0 -42
  404. package/src/pose-extract/detector.js +0 -100
  405. package/src/pose-extract/fit.js +0 -227
  406. package/src/pose-extract/image-frame.js +0 -51
  407. package/src/pose-extract/index.js +0 -58
  408. package/src/pose-extract/keyframes.js +0 -74
  409. package/src/pose-extract/landmarks.js +0 -162
  410. package/src/pose-extract/mirror.js +0 -153
  411. package/src/pose-extract/take.js +0 -131
  412. package/src/pose-extract/video-frames.js +0 -91
  413. package/src/take-recipe.js +0 -165
  414. package/src/trail-key-conflicts.js +0 -39
  415. package/src/trail-pick.js +0 -59
  416. package/src/use-i2v-capture-modal.js +0 -15
  417. package/tools/ardy/BRIDGE.md +0 -37
  418. package/tools/ardy/GVHMR-NPZ.md +0 -68
  419. package/tools/ardy/README.md +0 -124
  420. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  421. package/tools/ardy/artifacts.mjs +0 -34
  422. package/tools/ardy/bridge.mjs +0 -1732
  423. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  424. package/tools/ardy/cclay_constrained_generate.py +0 -561
  425. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  426. package/tools/ardy/cclay_motion_edit.py +0 -510
  427. package/tools/ardy/cclay_sequence_generate.py +0 -705
  428. package/tools/ardy/cclay_worker.py +0 -304
  429. package/tools/ardy/dump-npz.py +0 -205
  430. package/tools/ardy/extract-rest.mjs +0 -299
  431. package/tools/ardy/extract.mjs +0 -507
  432. package/tools/ardy/footage.mjs +0 -467
  433. package/tools/ardy/gvhmr-floor.mjs +0 -102
  434. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  435. package/tools/ardy/gvhmr_fastpath.py +0 -168
  436. package/tools/ardy/gvhmr_trajectory.py +0 -240
  437. package/tools/ardy/merge-text-encoder.py +0 -160
  438. package/tools/ardy/mocap-metrics.mjs +0 -200
  439. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  440. package/tools/ardy/motion-stabilize.mjs +0 -348
  441. package/tools/ardy/npz.mjs +0 -414
  442. package/tools/ardy/pose-to-npz.mjs +0 -106
  443. package/tools/ardy/prompt-limits.mjs +0 -5
  444. package/tools/ardy/run-edit-on-box.sh +0 -73
  445. package/tools/ardy/run-local.mjs +0 -233
  446. package/tools/ardy/run-on-box.sh +0 -593
  447. package/tools/ardy/run-sequence-on-box.sh +0 -177
  448. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  449. package/tools/ardy/runners/index.mjs +0 -18
  450. package/tools/ardy/runners/local.mjs +0 -455
  451. package/tools/ardy/runners/proc.mjs +0 -125
  452. package/tools/ardy/runners/remote.mjs +0 -252
  453. package/tools/ardy/setup-local.mjs +0 -189
  454. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  455. package/tools/ardy/setup-text-encoder.py +0 -306
  456. package/tools/ardy/smpl-cskel27.mjs +0 -178
  457. package/tools/ardy/visual-qa.mjs +0 -196
  458. package/tools/ardy/vq-car.mjs +0 -52
  459. package/tools/bench/EXP3.md +0 -61
  460. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  461. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  462. package/tools/bench/cclay_bench_runner.py +0 -42
  463. package/tools/bench/cube-contact.mjs +0 -162
  464. package/tools/bench/exp3.mjs +0 -152
  465. package/tools/bench/extract-bench-lib.mjs +0 -216
  466. package/tools/bench/extract-bench.mjs +0 -302
  467. package/tools/bench/fal-generate.mjs +0 -88
  468. package/tools/bench/fit/README.md +0 -189
  469. package/tools/bench/fit/camera.mjs +0 -59
  470. package/tools/bench/fit/contact.mjs +0 -419
  471. package/tools/bench/fit/footlock.mjs +0 -234
  472. package/tools/bench/fit/motion.mjs +0 -85
  473. package/tools/bench/fit/pin.mjs +0 -27
  474. package/tools/bench/fit/remote.mjs +0 -105
  475. package/tools/bench/fit-bench.mjs +0 -126
  476. package/tools/bench/fit-sanity.mjs +0 -45
  477. package/tools/bench/metrics.mjs +0 -313
  478. package/tools/bench/obs/depth.mjs +0 -258
  479. package/tools/bench/obs/extrinsics.mjs +0 -175
  480. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  481. package/tools/bench/obs/ground.mjs +0 -294
  482. package/tools/bench/obs/heading.mjs +0 -151
  483. package/tools/bench/obs/ladder.mjs +0 -555
  484. package/tools/bench/obs/mannequin-betas.json +0 -124
  485. package/tools/bench/obs/remote.mjs +0 -83
  486. package/tools/bench/obs/rest_joints.py +0 -52
  487. package/tools/bench/obs/ybot-targets.mjs +0 -49
  488. package/tools/bench/obs-bench.mjs +0 -520
  489. package/tools/bench/score.mjs +0 -343
  490. package/tools/bench/summarize.mjs +0 -86
  491. package/tools/demo-worker/README.md +0 -137
  492. package/tools/demo-worker/api-client.mjs +0 -233
  493. package/tools/demo-worker/generate.mjs +0 -301
  494. package/tools/demo-worker/index.mjs +0 -466
  495. package/tools/demo-worker/sign-core.mjs +0 -112
  496. package/tools/demo-worker/sign.mjs +0 -161
  497. package/tools/gt-render/browser.mjs +0 -182
  498. package/tools/gt-render/camera-math.mjs +0 -276
  499. package/tools/gt-render/page.mjs +0 -267
  500. package/tools/gt-render/render.mjs +0 -514
  501. package/tools/gt-render/scene-box.mjs +0 -31
  502. package/tools/gt-render/take-transform.mjs +0 -87
  503. package/tools/kimodo/compare-seam.mjs +0 -108
  504. package/tools/kimodo/constraints.mjs +0 -244
  505. package/tools/kimodo/edit.mjs +0 -259
  506. package/tools/kimodo/effector-constraints.mjs +0 -200
  507. package/tools/kimodo/generate.mjs +0 -646
  508. package/tools/kimodo/local-output.mjs +0 -265
  509. package/tools/kimodo/measure-pose.mjs +0 -65
  510. package/tools/kimodo/measure-preserve.mjs +0 -899
  511. package/tools/kimodo/measure-waypoints.mjs +0 -55
  512. package/tools/kimodo/mlx-generate.py +0 -111
  513. package/tools/kimodo/pose-constraints.mjs +0 -248
  514. package/tools/kimodo/preserve-mask.mjs +0 -543
  515. package/tools/kimodo/read-npz.mjs +0 -189
  516. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  517. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  518. package/tools/kimodo/runner.mjs +0 -284
  519. package/tools/kimodo/seam-batch.mjs +0 -77
  520. package/tools/kimodo/setup-local.mjs +0 -30
  521. package/tools/kimodo/setup-on-box.sh +0 -339
  522. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  523. package/tools/morphgs/assets/gen_truth.py +0 -39
  524. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  525. package/tools/morphgs/assets/playback-check.mjs +0 -30
  526. package/tools/morphgs/demo-gate.sh +0 -26
  527. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  528. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  529. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  530. package/tools/morphgs/setup-on-cluster.sh +0 -126
  531. package/tools/projflow/driver.py +0 -1069
  532. package/tools/projflow/generate.mjs +0 -805
  533. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  534. package/tools/projflow/line-edit-job.mjs +0 -547
  535. package/tools/projflow/replay.mjs +0 -452
  536. package/tools/projflow/runner.mjs +0 -269
  537. package/tools/projflow/service.mjs +0 -742
  538. package/tools/track/DESIGN.md +0 -513
  539. package/tools/track/backfill-provenance.mjs +0 -116
  540. package/tools/track/budget.mjs +0 -8
  541. package/tools/track/check-rig.mjs +0 -209
  542. package/tools/track/diagnostics.schema.json +0 -60
  543. package/tools/track/export-rig.mjs +0 -256
  544. package/tools/track/fallback.mjs +0 -7
  545. package/tools/track/fk-parity-fixture.mjs +0 -161
  546. package/tools/track/gate.mjs +0 -349
  547. package/tools/track/masks.mjs +0 -318
  548. package/tools/track/metrics.mjs +0 -201
  549. package/tools/track/publish-obs.mjs +0 -108
  550. package/tools/track/py/check_env.py +0 -61
  551. package/tools/track/py/eval_lr.py +0 -229
  552. package/tools/track/py/lr_viterbi.py +0 -294
  553. package/tools/track/py/masks.py +0 -405
  554. package/tools/track/py/objective.py +0 -382
  555. package/tools/track/py/rig.py +0 -508
  556. package/tools/track/py/scene.py +0 -326
  557. package/tools/track/py/test_joint_indices.py +0 -123
  558. package/tools/track/py/test_lr_viterbi.py +0 -195
  559. package/tools/track/py/test_masks.py +0 -229
  560. package/tools/track/py/test_rig.py +0 -288
  561. package/tools/track/py/test_scene.py +0 -354
  562. package/tools/track/py/test_track.py +0 -328
  563. package/tools/track/py/track.py +0 -411
  564. package/tools/track/remote.mjs +0 -140
  565. package/tools/track/rig-dump.mjs +0 -227
  566. package/tools/track/run-box-tests.mjs +0 -14
  567. package/tools/track/run-box.mjs +0 -168
  568. package/tools/track/setup-box.sh +0 -59
  569. package/tools/track/study-2d.mjs +0 -728
  570. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  571. /package/src/{ardy → motion}/ground.js +0 -0
  572. /package/src/{ardy → motion}/ik-drag.js +0 -0
  573. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  574. /package/src/{ardy → motion}/ik.js +0 -0
  575. /package/src/{ardy → motion}/physics-surface.js +0 -0
  576. /package/src/{ardy → motion}/playback-clock.js +0 -0
  577. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -0,0 +1,107 @@
1
+ // The live progress card in the agent panel (agent-progress.js). While the agent works on motion it shows what
2
+ // the agent actually produced so far: how many drafts it compiled and their QA trend, the latest draft's goals
3
+ // (met or not, value against need) and heaviest issues, whether that draft is installed, and the images the
4
+ // agent asked to see (click to enlarge, over the whole window). Nothing here is invented progress. A new turn
5
+ // starts it over.
6
+ import { useEffect, useRef, useState, useSyncExternalStore } from "react";
7
+ import { createPortal } from "react-dom";
8
+ import { FiCheck, FiX } from "react-icons/fi";
9
+ import { clearProgress, draftsOf, latestDraft, progressSheets, progressVersion, subscribeProgress } from "../agent-progress.js";
10
+ import { ko } from "../locale.js";
11
+ import { goalRows, issueRows, trendPoints, turnCount } from "./agent-progress-view.js";
12
+ import "./agent-progress-card.css";
13
+
14
+ const W = 220, H = 40;
15
+
16
+ function Trend({ totals }) {
17
+ const { points, first, last } = trendPoints(totals, { width: W, height: H });
18
+ if (!points.length) return null;
19
+ const [lx, ly] = points[points.length - 1];
20
+ const label = ko(`QA issues by draft: ${totals.join(", ")}`, `초안별 QA 문제 수: ${totals.join(", ")}`);
21
+ return <div className="agent-progress-trend">
22
+ <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={label} preserveAspectRatio="none">
23
+ <line className="base" x1={4} y1={H - 4} x2={W - 4} y2={H - 4} />
24
+ {points.length > 1 && <polyline className="line" points={points.map(p => p.join(",")).join(" ")} />}
25
+ {points.slice(0, -1).map(([x, y], i) => <circle key={i} className="dot" cx={x} cy={y} r={1.8} />)}
26
+ <circle className={`end ${last === 0 ? "ok" : "issue"}`} cx={lx} cy={ly} r={3.4} />
27
+ </svg>
28
+ <span className="agent-progress-trend-text">{totals.length > 1 ? `${first} → ${last}` : `${last}`}</span>
29
+ </div>;
30
+ }
31
+
32
+ // The panel's own lightbox stays inside the panel; a contact sheet needs the window.
33
+ function SheetViewer({ sheet, onClose }) {
34
+ useEffect(() => {
35
+ const onKey = event => { if (event.key === "Escape") onClose(); };
36
+ window.addEventListener("keydown", onKey);
37
+ return () => window.removeEventListener("keydown", onKey);
38
+ }, [onClose]);
39
+ return createPortal(<button type="button" className="agent-progress-viewer" aria-label={ko("Close image", "이미지 닫기")} onClick={onClose}>
40
+ <img src={sheet.dataUrl} alt={sheet.caption || ko("Image the agent checked", "에이전트가 확인한 이미지")} />
41
+ {sheet.caption && <span className="agent-progress-viewer-caption">{sheet.caption}</span>}
42
+ </button>, document.body);
43
+ }
44
+
45
+ /** `since`: when the latest turn started (null: no turn yet). Drafts and images from before it are not this turn's
46
+ * work, such as the take the camera tutorial compiles at startup. `live`: that turn is still running. */
47
+ export function AgentProgressCard({ items, since = null, live = false }) {
48
+ useSyncExternalStore(subscribeProgress, progressVersion);
49
+ const [open, setOpen] = useState(null);
50
+ // The goal and issue lists fold to one line each: the panel is short and the live steps sit below the card.
51
+ const [shown, setShown] = useState({});
52
+ const toggle = key => setShown(now => ({ ...now, [key]: !now[key] }));
53
+ // a new turn (not a steer of the running one) starts the card over
54
+ const turns = turnCount(items), seen = useRef(turns);
55
+ useEffect(() => {
56
+ if (turns > seen.current) clearProgress();
57
+ seen.current = turns;
58
+ }, [turns]);
59
+ const ours = record => since !== null && record.at >= since;
60
+ const newest = latestDraft(), latest = newest && ours(newest) ? newest : null, sheets = progressSheets().filter(ours);
61
+ const viewing = open && sheets.find(sheet => sheet.seq === open);
62
+ if (!latest && !sheets.length) return null;
63
+ const drafts = latest ? draftsOf(latest.characterId).filter(ours) : [];
64
+ const goals = goalRows(latest?.qa.goals), issues = latest && !latest.qa.ok ? issueRows(latest.qa.groups) : [];
65
+ return <div className="agent-row">
66
+ <div className="agent-card agent-progress-card" data-agent-card="progress" data-drafts={drafts.length} data-installed={latest?.installed ? "true" : "false"} role="status" aria-live="polite">
67
+ {latest && <div className="agent-progress-head">
68
+ <span className="agent-progress-title">{ko("Draft", "초안")} {latest.n} · QA {latest.qa.total}</span>
69
+ <span className={`agent-progress-chip ${latest.installed ? "installed" : "draft"}`}>{latest.installed ? ko("Installed", "설치됨") : live ? ko("Not installed yet", "아직 설치 전") : ko("Not installed", "설치 안 됨")}</span>
70
+ </div>}
71
+ {latest?.label && <p className="agent-progress-label">{latest.label}</p>}
72
+ {drafts.length > 0 && <section className="agent-progress-section">
73
+ <h4>{ko(`QA issues across ${drafts.length} draft${drafts.length === 1 ? "" : "s"}`, `초안 ${drafts.length}개의 QA 문제 수`)}</h4>
74
+ <Trend totals={drafts.map(d => d.qa.total)} />
75
+ </section>}
76
+ {goals.length > 0 && <section className="agent-progress-section">
77
+ <button type="button" className="agent-progress-fold" aria-expanded={!!shown.goals} onClick={() => toggle("goals")}>
78
+ <h4>{ko("Goals", "목표")}</h4><span>{ko(`${goals.filter(g => g.ok).length} of ${goals.length} met`, `${goals.length}개 중 ${goals.filter(g => g.ok).length}개 충족`)}</span>
79
+ </button>
80
+ {shown.goals && <ul className="agent-progress-goals">
81
+ {goals.map((g, i) => <li key={i} data-goal-ok={g.ok ? "true" : "false"}>
82
+ <span className={`agent-progress-mark ${g.ok ? "ok" : "miss"}`} aria-label={g.ok ? ko("met", "충족") : ko("missed", "미충족")}>{g.ok ? <FiCheck size={11} /> : <FiX size={11} />}</span>
83
+ <span className="agent-progress-goal-name">{g.name}</span>
84
+ <span className="agent-progress-goal-value">{g.value}{g.need && <span className="need"> / {g.need}</span>}</span>
85
+ </li>)}
86
+ </ul>}
87
+ </section>}
88
+ {issues.length > 0 && <section className="agent-progress-section">
89
+ <button type="button" className="agent-progress-fold" aria-expanded={!!shown.issues} onClick={() => toggle("issues")}>
90
+ <h4>{ko("Largest issues", "가장 큰 문제")}</h4><span>{issues[0].kind} {issues[0].subject}</span>
91
+ </button>
92
+ {shown.issues && <ul className="agent-progress-issues">
93
+ {issues.map((row, i) => <li key={i}><span className="kind">{row.kind}</span> {row.subject} <span className="meta">×{row.count}{row.frames && ` · ${row.frames}`}</span></li>)}
94
+ </ul>}
95
+ </section>}
96
+ {sheets.length > 0 && <section className="agent-progress-section">
97
+ <h4>{ko("What the agent looked at", "에이전트가 본 화면")}</h4>
98
+ <div className="agent-progress-sheets">
99
+ {sheets.map(sheet => <button type="button" key={sheet.seq} className="agent-progress-sheet" title={sheet.caption} onClick={() => setOpen(sheet.seq)}>
100
+ <img src={sheet.dataUrl} alt={sheet.caption || ko("Image the agent checked", "에이전트가 확인한 이미지")} />
101
+ </button>)}
102
+ </div>
103
+ </section>}
104
+ </div>
105
+ {viewing && <SheetViewer sheet={viewing} onClose={() => setOpen(null)} />}
106
+ </div>;
107
+ }
@@ -0,0 +1,37 @@
1
+ // The agent's reply as formatted text (reply-markdown.js): paragraphs, headings, lists, bold, code and tables,
2
+ // never the raw marks. `tail` (the streaming caret) sits at the end of the last line.
3
+ import { Fragment, useMemo } from "react";
4
+ import { replyBlocks } from "./reply-markdown.js";
5
+
6
+ function Spans({ spans }) {
7
+ return spans.map((span, index) => {
8
+ if (span.code) return <code key={index}>{span.text}</code>;
9
+ if (span.bold) return <strong key={index}>{span.text}</strong>;
10
+ return <Fragment key={index}>{span.text}</Fragment>;
11
+ });
12
+ }
13
+
14
+ function Block({ block, tail }) {
15
+ if (block.type === "h") return <p className="agent-reply-heading"><Spans spans={block.spans} />{tail}</p>;
16
+ if (block.type === "code") return <pre><code>{block.text}</code>{tail}</pre>;
17
+ if (block.type === "ul" || block.type === "ol") {
18
+ const List = block.type;
19
+ return <List start={block.start}>
20
+ {block.items.map((spans, index) => <li key={index}><Spans spans={spans} />{index === block.items.length - 1 && tail}</li>)}
21
+ </List>;
22
+ }
23
+ if (block.type === "table") return <>
24
+ <div className="agent-reply-table"><table>
25
+ <thead><tr>{block.head.map((spans, index) => <th key={index}><Spans spans={spans} /></th>)}</tr></thead>
26
+ <tbody>{block.rows.map((row, rowIndex) => <tr key={rowIndex}>{row.map((spans, index) => <td key={index}><Spans spans={spans} /></td>)}</tr>)}</tbody>
27
+ </table></div>
28
+ {tail}
29
+ </>;
30
+ return <p><Spans spans={block.spans} />{tail}</p>;
31
+ }
32
+
33
+ export function ReplyText({ text, tail = null }) {
34
+ const blocks = useMemo(() => replyBlocks(text), [text]);
35
+ if (!blocks.length) return tail || null;
36
+ return blocks.map((block, index) => <Block key={index} block={block} tail={index === blocks.length - 1 ? tail : null} />);
37
+ }
@@ -0,0 +1,84 @@
1
+ // One turn's work in the agent panel (agent-turn.js turnLayout): a header that says how the turn stands (working,
2
+ // done, stopped, failed), how many steps and how long, and one compact row per step: the tool call with a plain
3
+ // label, what it was about and its one-line result; the reasoning the model streamed between calls; the call it is
4
+ // writing now. Open while the turn runs, folded to its header when the turn ends (a failed or stopped turn stays
5
+ // open, and a stopped one says what it left in the scene).
6
+ import { useState } from "react";
7
+ import { FiCheck, FiChevronRight, FiSquare, FiX } from "react-icons/fi";
8
+ import { ko } from "../locale.js";
9
+ import { scriptNow, stepLabel, stepResult, stepsHeader, stoppedNote, thinkingLine } from "./agent-turn.js";
10
+ import "./agent-turn-steps.css";
11
+
12
+ /** Step time: "0.6 s", "12 s", "1 m 05 s". */
13
+ function stepClock(ms) {
14
+ if (!Number.isFinite(ms) || ms < 0) return "";
15
+ const s = ms / 1000;
16
+ if (s < 10) return `${s.toFixed(1)} s`;
17
+ if (s < 60) return `${Math.round(s)} s`;
18
+ return `${Math.floor(s / 60)} m ${String(Math.round(s % 60)).padStart(2, "0")} s`;
19
+ }
20
+
21
+ function StepIcon({ state }) {
22
+ if (state === "running") return <span className="agent-step-spin" aria-label={ko("running", "진행 중")} />;
23
+ if (state === "failed") return <FiX className="agent-step-icon alert" size={11} aria-label={ko("failed", "실패")} />;
24
+ if (state === "cancelled") return <FiSquare className="agent-step-icon warn" size={9} aria-label={ko("stopped", "중지됨")} />;
25
+ if (state === "note") return <span className="agent-step-dot" aria-hidden="true" />;
26
+ return <FiCheck className="agent-step-icon ok" size={11} aria-label={ko("done", "완료")} />;
27
+ }
28
+
29
+ function Step({ step, live, isLast, now, toolLabels }) {
30
+ const [open, setOpen] = useState(false);
31
+ if (step.kind === "narration") return <li className="agent-step narration" data-step-kind="narration"><StepIcon state="note" /><span className="agent-step-text">{step.text}</span></li>;
32
+ if (step.kind === "thinking") {
33
+ const running = live && isLast;
34
+ const line = thinkingLine(step.text);
35
+ // from its first words until the next step began; a thought that arrived at once shows no time
36
+ const took = (running ? now : step.endedAt ?? step.updatedAt) - step.startedAt;
37
+ return <li className="agent-step thinking" data-step-kind="thinking">
38
+ <button type="button" className="agent-step-row" aria-expanded={open} onClick={() => setOpen(value => !value)}>
39
+ <StepIcon state={running ? "running" : "note"} />
40
+ <span className="agent-step-label">{ko("Thinking", "생각")}</span>
41
+ <span className="agent-step-subject">{open ? "" : line}</span>
42
+ <span className="agent-step-time">{took >= 1000 ? stepClock(took) : ""}</span>
43
+ </button>
44
+ {open && <p className="agent-step-thought">{step.text}</p>}
45
+ </li>;
46
+ }
47
+ const running = step.kind === "compose" || step.status === "running";
48
+ const label = step.kind === "compose" ? ko(`Preparing ${stepLabel(step, toolLabels)}`, `${stepLabel(step, toolLabels)} 준비 중`) : stepLabel(step, toolLabels);
49
+ const result = running ? (step.progress ? `${scriptNow(step)}${step.progress.summary ? ` — ${step.progress.summary}` : ""}` : "") : stepResult(step);
50
+ const time = running ? now - (step.startedAt ?? now) : step.elapsedMs;
51
+ return <li className={`agent-step ${step.kind}${step.status === "failed" ? " failed" : ""}`} data-step-kind={step.kind} data-tool-name={step.name} data-tool-status={step.kind === "compose" ? "composing" : step.status} data-action={step.action}>
52
+ <div className="agent-step-row">
53
+ <StepIcon state={running ? "running" : step.status} />
54
+ <span className="agent-step-label">{label}</span>
55
+ {step.subject && <span className="agent-step-subject">{step.subject}</span>}
56
+ <span className="agent-step-time">{stepClock(time)}</span>
57
+ </div>
58
+ {result && <p className="agent-step-result">{result}</p>}
59
+ </li>;
60
+ }
61
+
62
+ export function TurnSteps({ block, now, toolLabels, formatClock, onRetry }) {
63
+ const [chosen, setChosen] = useState(null);
64
+ const failed = block.end?.status === "failed" || block.steps.some(step => step.failure);
65
+ const stopped = block.end?.status === "stopped";
66
+ const open = chosen ?? (block.live || failed || stopped);
67
+ const header = stepsHeader(block, now, formatClock);
68
+ const failure = block.steps.find(step => step.failure)?.failure;
69
+ return <div className="agent-card agent-steps" data-agent-card="steps" data-steps-state={block.live ? "live" : block.end?.status ?? "done"} data-open={open ? "true" : "false"}>
70
+ <button type="button" className="agent-steps-head" aria-expanded={open} onClick={() => setChosen(!open)}>
71
+ <span className={`agent-status-dot ${header.tone}`} aria-hidden="true" />
72
+ <span className="agent-steps-title">{header.text}</span>
73
+ <FiChevronRight className="agent-steps-chevron" size={12} aria-hidden="true" />
74
+ </button>
75
+ {open && <ol className="agent-steps-list">
76
+ {block.steps.map((step, index) => <Step key={step.id} step={step} live={block.live} isLast={index === block.steps.length - 1} now={now} toolLabels={toolLabels} />)}
77
+ </ol>}
78
+ {stopped && <p className="agent-steps-note">{stoppedNote(block)}</p>}
79
+ {failure && <div className="agent-error" role="alert">
80
+ <span>{failure.message || ko("The turn failed.", "턴이 실패했어요.")}</span>
81
+ <div className="agent-error-actions"><button type="button" className="agent-ghost-button agent-error-retry" onClick={onRetry}>{ko("Retry", "다시 시도")}</button></div>
82
+ </div>}
83
+ </div>;
84
+ }
@@ -0,0 +1,140 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { FiX } from "react-icons/fi";
3
+ import { answerLogin, cancelLogin, fetchLogins, logoutProvider, readLogin, startLogin } from "./login-client.js";
4
+
5
+ // "Connect a model": pi's own login (the coding agent's /login) inside the agent panel (#pi-login). The list
6
+ // is what pi ships: each provider with its subscription sign-in and/or api-key login; picking one runs pi's
7
+ // flow in the sidecar and this panel only shows what pi asks (a key, a server address, a pasted code, a
8
+ // choice) and tells (a sign-in page to open, a device code, progress). ChatGPT keeps CozyClay's own sign-in.
9
+
10
+ const GROUPS = [
11
+ ["Connected", (entry) => entry.connected],
12
+ ["Subscriptions", (entry) => !entry.connected && entry.methods.some((method) => method.subscription)],
13
+ ["API keys", (entry) => !entry.connected && !entry.methods.some((method) => method.subscription)],
14
+ ];
15
+
16
+ function PromptForm({ prompt, onSubmit }) {
17
+ const [value, setValue] = useState("");
18
+ const ask = prompt.prompt;
19
+ if (ask.type === "select") {
20
+ return <div className="agent-login-choices" role="group" aria-label={ask.message}>
21
+ <p className="agent-login-ask">{ask.message}</p>
22
+ {(ask.options ?? []).map((option) => <button key={option.id} type="button" className="agent-ghost-button" onClick={() => onSubmit(option.id)}>{option.label}</button>)}
23
+ </div>;
24
+ }
25
+ const inputId = `agent-login-${prompt.promptId}`;
26
+ return <form className="agent-login-form" onSubmit={(event) => { event.preventDefault(); onSubmit(value); setValue(""); }}>
27
+ <label className="agent-login-ask" htmlFor={inputId}>{ask.message}</label>
28
+ <div className="agent-key-controls">
29
+ <input id={inputId} className="agent-key-input" type={ask.type === "secret" ? "password" : "text"} autoComplete="off" spellCheck="false"
30
+ placeholder={ask.placeholder ?? ""} value={value} autoFocus onChange={(event) => setValue(event.target.value)} />
31
+ <button type="submit" className="agent-key-save" disabled={ask.type === "secret" && !value.trim()}>Continue</button>
32
+ </div>
33
+ </form>;
34
+ }
35
+
36
+ function FlowLog({ entries }) {
37
+ return <ul className="agent-login-log">
38
+ {entries.map((entry, index) => {
39
+ const event = entry.event;
40
+ if (event.type === "auth_url") return <li key={index}><a className="agent-primary-button agent-login-open" href={event.url} target="_blank" rel="noopener noreferrer">Open the sign-in page</a>{event.instructions && <span className="agent-key-note">{event.instructions}</span>}</li>;
41
+ if (event.type === "device_code") return <li key={index}><span className="agent-key-note">Enter this code at </span><a href={event.verificationUri} target="_blank" rel="noopener noreferrer">{event.verificationUri}</a><code className="agent-login-code">{event.userCode}</code></li>;
42
+ return <li key={index} className="agent-key-note">{event.message}{(event.links ?? []).map((link) => <a key={link.url} href={link.url} target="_blank" rel="noopener noreferrer"> {link.label || link.url}</a>)}</li>;
43
+ })}
44
+ </ul>;
45
+ }
46
+
47
+ export function LoginPanel({ onClose, onChatGPT, onChanged }) {
48
+ const [providers, setProviders] = useState(null);
49
+ const [error, setError] = useState("");
50
+ const [query, setQuery] = useState("");
51
+ const [flow, setFlow] = useState(null);
52
+ const live = useRef(true);
53
+ const load = useCallback(async () => {
54
+ try { setProviders(await fetchLogins()); setError(""); } catch (failure) { setError(failure?.message || "The login list is unavailable."); }
55
+ }, []);
56
+ useEffect(() => { live.current = true; load(); return () => { live.current = false; }; }, [load]);
57
+ // Read the running flow until pi finishes it.
58
+ useEffect(() => {
59
+ if (!flow?.flowId || flow.done) return undefined;
60
+ let stopped = false;
61
+ (async () => {
62
+ let after = 0;
63
+ while (!stopped && live.current) {
64
+ let page;
65
+ try { page = await readLogin(flow.flowId, after); } catch (failure) { if (!stopped) setFlow((current) => current && { ...current, done: true, ok: false, message: failure?.message || "The login stopped." }); return; }
66
+ if (stopped) return;
67
+ after = page.next;
68
+ for (const entry of page.events) {
69
+ setFlow((current) => {
70
+ if (!current || current.flowId !== flow.flowId) return current;
71
+ if (entry.type === "prompt") return { ...current, prompt: entry };
72
+ if (entry.type === "prompt_closed") return current.prompt?.promptId === entry.promptId ? { ...current, prompt: null } : current;
73
+ if (entry.type === "event") return { ...current, log: [...current.log, entry] };
74
+ return { ...current, prompt: null, done: true, ok: entry.ok, message: entry.message ?? "" };
75
+ });
76
+ if (entry.type === "done" && entry.ok) { await onChanged?.(); load(); }
77
+ }
78
+ if (page.done) return;
79
+ }
80
+ })();
81
+ return () => { stopped = true; };
82
+ }, [flow?.flowId, flow?.done, load, onChanged]);
83
+
84
+ const begin = async (provider, method) => {
85
+ setError("");
86
+ // ChatGPT's own sign-in shows its waiting card in the panel body, so the list steps aside.
87
+ if (method.type === "chatgpt") { onChatGPT?.(); onClose?.(); return; }
88
+ try {
89
+ const flowId = await startLogin(provider.id, method.type);
90
+ setFlow({ flowId, name: provider.name, label: method.label, log: [], prompt: null, done: false });
91
+ } catch (failure) { setError(failure?.message || "That login could not start."); }
92
+ };
93
+ const answer = async (value) => {
94
+ const prompt = flow?.prompt;
95
+ if (!prompt) return;
96
+ setFlow((current) => current && { ...current, prompt: null });
97
+ try { await answerLogin(flow.flowId, prompt.promptId, value); } catch (failure) { setError(failure?.message || "That answer was not taken."); }
98
+ };
99
+ const cancel = async () => {
100
+ if (flow && !flow.done) await cancelLogin(flow.flowId).catch(() => {});
101
+ setFlow(null);
102
+ };
103
+ const logout = async (provider) => {
104
+ try { await logoutProvider(provider.id); await onChanged?.(); load(); } catch (failure) { setError(failure?.message || "Logout failed."); }
105
+ };
106
+
107
+ const shown = (providers ?? []).filter((entry) => !query.trim() || entry.name.toLowerCase().includes(query.trim().toLowerCase()));
108
+ return <section className="agent-keys agent-login" aria-label="Connect a model" data-agent-keys="true">
109
+ <div className="agent-keys-head">
110
+ <h3 className="agent-keys-title">Connect a model</h3>
111
+ <button type="button" className="agent-icon-button agent-keys-close" aria-label="Close" onClick={async () => { await cancel(); onClose?.(); }}><FiX size={11} aria-hidden="true" /></button>
112
+ </div>
113
+ {error && <p className="agent-key-error" role="alert">{error}</p>}
114
+ {flow ? <div className="agent-login-flow" data-login-flow={flow.done ? (flow.ok ? "connected" : "failed") : "running"}>
115
+ <p className="agent-key-label">{flow.name} · {flow.label}</p>
116
+ <FlowLog entries={flow.log} />
117
+ {flow.prompt && <PromptForm key={flow.prompt.promptId} prompt={flow.prompt} onSubmit={answer} />}
118
+ {!flow.done && !flow.prompt && <p className="agent-key-note">Waiting for {flow.name}…</p>}
119
+ {flow.done && <p className={flow.ok ? "agent-key-note" : "agent-key-error"} role={flow.ok ? "status" : "alert"}>{flow.ok ? `Connected to ${flow.name}.` : flow.message}</p>}
120
+ <div className="agent-key-controls"><button type="button" className="agent-ghost-button" onClick={cancel}>{flow.done ? "Back to the list" : "Cancel"}</button></div>
121
+ </div> : <>
122
+ <p className="agent-keys-hint">A subscription signs in on its own page. A key or server address you type stays with the local CozyClay service and is never shown again.</p>
123
+ <input className="agent-key-input" type="search" aria-label="Search providers" placeholder="Search providers" value={query} onChange={(event) => setQuery(event.target.value)} />
124
+ {providers === null && !error && <p className="agent-keys-note">Loading providers…</p>}
125
+ {GROUPS.map(([title, test]) => {
126
+ const rows = shown.filter(test);
127
+ return rows.length ? <div key={title} className="agent-login-group"><p className="agent-key-endpoint-label">{title}</p>
128
+ {rows.map((provider) => <div key={provider.id} className="agent-key-row" data-provider={provider.id} data-provider-signed-in={provider.connected ? "true" : "false"}>
129
+ <div className="agent-key-head"><span className="agent-key-label">{provider.name}</span><span className="agent-key-source">{provider.connected ? (provider.via === "oauth" ? "signed in" : provider.source || "key") : ""}</span></div>
130
+ <div className="agent-key-controls">
131
+ {provider.connected && provider.id !== "openai-codex"
132
+ ? <button type="button" className="agent-ghost-button agent-key-remove" onClick={() => logout(provider)}>Log out</button>
133
+ : provider.methods.map((method) => <button key={method.type} type="button" className={method.subscription ? "agent-key-save" : "agent-ghost-button"} onClick={() => begin(provider, method)}>{method.label}</button>)}
134
+ </div>
135
+ </div>)}
136
+ </div> : null;
137
+ })}
138
+ </>}
139
+ </section>;
140
+ }
@@ -15,7 +15,7 @@ import { loadWorkflowGraph, normalizeWorkflowGraph, storeWorkflowGraph, WORKFLOW
15
15
  import AgentPanel from "./AgentPanel.jsx";
16
16
  import CozySceneNode from "./CozySceneNode.jsx";
17
17
  import { applyCozyScenePatch, normalizeCozySceneData, sceneConnectionAllowed } from "./cozy-scene-node.js";
18
- import { activeSceneCharacters, characterHandleId, characterIdFromHandle, normalizeMotionInputData, motionInputOutput } from "./motion-input.js";
18
+ import { activeSceneCharacters, characterHandleId, characterIdFromHandle, normalizeMotionInputData, motionInputOutput, parseMotionSpec } from "./motion-input.js";
19
19
  import { DEFAULT_NODE_SCHEMAS, defaultFormValues, schemaCategoryForType, schemaModelEntries, schemaProperties } from "./node-schema.js";
20
20
  import { executeLocalWorkflowGraph } from "./local-workflow.js";
21
21
  import { createLiveControl } from "../live-control.js";
@@ -24,7 +24,6 @@ import { startWorkflowExecution } from "../execution-telemetry.js";
24
24
  import { createCanvasCommands } from "./canvas-commands.js";
25
25
  import { applyMotionToActiveScene, importImageIntoActiveScene, readStoredSceneDocument } from "./scene-asset-sync.js";
26
26
  import { createHttpTransport } from "./agent-client.js";
27
- import { fetchVideoOutputBlob, requestBridgeExtract } from "../multimodel-ingest.js";
28
27
  import { canvasTakesPaste, fileToDataUrl, imageFileFromTransfer, pastedImageNodeData } from "./clipboard-image.js";
29
28
  import { appendVersion, compareIndex, pinnedInputs, selectVersion, versionLabel, versionState } from "./image-versions.js";
30
29
  import { normalizeVideoForm, videoFormContract } from "./video-contract.js";
@@ -214,10 +213,8 @@ function VideoNode({ id, data }) {
214
213
  <label>Motion prompt</label><textarea className="workflow-textarea" value={form.prompt ?? data.prompt ?? ""} onChange={(event) => update("prompt", event.target.value)} placeholder="Motion prompt" />
215
214
  <label>Duration (seconds)</label><input type="number" min={contract.minDuration} max={contract.maxDuration} value={form.duration_seconds} onChange={(event) => update("duration_seconds", Number(event.target.value))} />
216
215
  <label>Aspect</label>{contract.aspectFromImage ? <><div className="workflow-hint" data-testid="video-source-aspect">Matches input image · frame the full body in the source image</div><div className="workflow-warning" role="status" data-testid="mocap-framing-warning">Before generating: confirm the captured frame shows the head, hands, feet, and any needed objects. Cropped parts can break mocap alignment.</div></> : <select value={form.aspect} onChange={(event) => update("aspect", event.target.value)}>{contract.aspects.map((aspect) => <option key={aspect}>{aspect}</option>)}</select>}
217
- <label className="workflow-schema-check"><input type="checkbox" checked={Boolean(form.extract_mocap)} onChange={(event) => update("extract_mocap", event.target.checked)} />Extract GVHMR motion into connected Motion Input</label>
218
216
  </>}
219
217
  {data.videoUrl && <video controls className="workflow-video-preview" src={data.videoUrl} />}
220
- {generated && data.motionExtraction?.motionUrl && <div className="workflow-hint" data-testid="mocap-extraction-receipt">✓ GVHMR motion extracted{Number.isFinite(data.motionExtraction.frames) ? ` · ${data.motionExtraction.frames} frames` : ""}</div>}
221
218
  {generated && data.preservation?.pass && <div className="workflow-hint workflow-preservation-ok" data-testid="h3-preservation-receipt">✓ H3 scene/camera lock verified{h3VerificationMetrics(data.preservation)}</div>}
222
219
  {generated && data.preservation && !data.preservation.pass && <div className="workflow-error workflow-preservation-failed" data-testid="h3-preservation-failed">✕ H3 output rejected: background/camera drift{h3VerificationMetrics(data.preservation)}</div>}
223
220
  {generated && data.isLoading && <div className="workflow-hint">Generating video…</div>}{generated && data.errorMsg && <div className="workflow-error">{data.errorMsg}</div>}
@@ -246,9 +243,8 @@ function MotionInputNode({ id, data }) {
246
243
  {options.map((character) => <option key={character.id} value={character.id}>{character.name || character.subject || character.id}</option>)}
247
244
  </select>
248
245
  <input aria-label="Character id" value={normalized.characterId} onChange={(event) => update({ characterId: event.target.value.trim() })} placeholder="character id (e.g. char-a)" />
249
- <label>Same-origin motion URL</label>
250
- <input aria-label="Motion URL" value={normalized.objectUrl ? "" : normalized.url} onChange={(event) => update({ url: event.target.value, objectUrl: null, status: event.target.value ? "ready" : "idle" })} placeholder="/ardy/motions/take.npz" />
251
- <label className="workflow-upload workflow-motion-upload"><FiUpload size={18} /><span>{normalized.fileName || "Choose local .npz motion"}</span><input type="file" accept=".npz,application/octet-stream,.json" onChange={(event) => { const file = event.target.files?.[0]; if (!file) return; const objectUrl = URL.createObjectURL(file); update({ objectUrl, url: objectUrl, fileName: file.name, mimeType: file.type || "application/octet-stream", status: "ready" }); }} /></label>
246
+ <label>Motion spec (cozyclay.motion-spec.v1)</label>
247
+ <label className="workflow-upload workflow-motion-upload"><FiUpload size={18} /><span>{normalized.fileName || "Choose a motion spec .json"}</span><input type="file" accept=".json,application/json" onChange={(event) => { const file = event.target.files?.[0]; if (!file) return; file.text().then((body) => { const spec = parseMotionSpec(body); if (!spec) toast.error("Not a cozyclay.motion-spec.v1 file"); update(spec ? { spec, fileName: file.name, status: "ready" } : { spec: null, fileName: file.name, status: "error" }); }); }} /></label>
252
248
  <div className="workflow-node-foot"><span>{normalized.status === "ready" ? `${handleId} ready` : "Connect to a character handle"}</span><span>{motionInputOutput(normalized).frames ? `${motionInputOutput(normalized).frames}f` : ""}</span></div>
253
249
  </NodeShell>;
254
250
  }
@@ -539,20 +535,8 @@ export default function WorkflowBuilder() {
539
535
  const videoForm = normalizeVideoForm(provider, { ...form, duration_seconds: form.duration_seconds ?? current.data.duration_seconds, aspect: form.aspect || current.data.aspect }, falModel);
540
536
  const output = await createHttpTransport().video({ provider, prompt: motionPrompt, imageDataUrl: frame, ...(lastFrameDataUrl ? { lastFrameDataUrl } : {}), durationSeconds: videoForm.duration_seconds, aspect: videoForm.aspect });
541
537
  const videoUrl = output.dataUrl || output.url;
542
- let motionExtraction = null;
543
- const motionTargets = graph.edges.filter((edge) => edge.source === id).map((edge) => result.nodes.find((node) => node.id === edge.target)).filter((node) => node?.type === "motion-input");
544
- if (videoForm.extract_mocap && motionTargets.length) {
545
- const done = await requestBridgeExtract(await fetchVideoOutputBlob(videoUrl));
546
- motionExtraction = done;
547
- for (const target of motionTargets) {
548
- const motionData = normalizeMotionInputData({ ...target.data, url: done.motionUrl, objectUrl: null, status: "ready", frames: done.frames, fps: done.fps, motionRef: done.motionRef || null });
549
- const motionOutput = motionInputOutput(motionData);
550
- values.set(target.id, motionOutput);
551
- patchNode(target.id, { ...motionData, outputs: [{ value: motionOutput }] });
552
- }
553
- }
554
538
  values.set(id, videoUrl);
555
- patchNode(id, { isLoading: false, status: "complete", videoUrl, resultUrl: videoUrl, outputs: [{ value: videoUrl }], preservation: output.preservation || null, motionExtraction, errorMsg: null });
539
+ patchNode(id, { isLoading: false, status: "complete", videoUrl, resultUrl: videoUrl, outputs: [{ value: videoUrl }], preservation: output.preservation || null, errorMsg: null });
556
540
  if (videoUrl) execution.apply();
557
541
  }
558
542
  catch (error) {
@@ -592,7 +576,7 @@ export default function WorkflowBuilder() {
592
576
  const source = resultById.get(assignment.source);
593
577
  if (source?.type !== "motion-input" || !assignment.characterId) continue;
594
578
  const motion = motionInputOutput({ ...source.data, ...(patches.get(source.id) || {}) });
595
- if (motion.url) applyMotionToActiveScene(assignment.characterId, motion);
579
+ if (motion.spec) applyMotionToActiveScene(assignment.characterId, motion);
596
580
  }
597
581
  }
598
582
  setRunState("complete");
@@ -672,8 +656,8 @@ export default function WorkflowBuilder() {
672
656
  const onConnect = useCallback((params) => {
673
657
  const target = nodes.find((node) => node.id === params.target);
674
658
  const source = nodes.find((node) => node.id === params.source);
675
- if (target?.type === "motion-input" && source?.type !== "video") {
676
- toast.error("Motion Input accepts generated Video nodes only");
659
+ if (target?.type === "motion-input") {
660
+ toast.error("Motion Input takes a motion spec file, not a node");
677
661
  return;
678
662
  }
679
663
  if (target?.type === "scene" && !sceneConnectionAllowed(source, params.targetHandle, source?.data)) {