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,79 @@
1
+ // What the agent's motion work looks like while it runs, for the user's eyes. Every motion.compile publishes its
2
+ // draft here, installed or not: the take it baked, its QA (total, groups, goals) and its beats (timeline). The
3
+ // images the agent asks to see (verify_result) are published here too. The viewport plays the latest draft as a
4
+ // ghost, the timeline lays out its beats, and the agent panel draws the QA trend, the goals and the images.
5
+ // In-page only: nothing here is saved with the project, undone, or sent to the agent. Each character keeps its
6
+ // last KEEP drafts; the images keep the last KEEP. A new agent turn starts clean (clearProgress).
7
+
8
+ const KEEP = 12;
9
+ const drafts = new Map(), sheets = [], listeners = new Set();
10
+ let version = 0, counter = 0;
11
+
12
+ function emit() {
13
+ version++;
14
+ for (const listener of [...listeners]) {
15
+ try { listener(); } catch { /* a broken view never stops the agent */ }
16
+ }
17
+ }
18
+
19
+ const pick = (row, keys) => Object.fromEntries(keys.filter(k => row?.[k] !== undefined).map(k => [k, row[k]]));
20
+
21
+ /**
22
+ * publishDraft(characterId, { take, installed, specId, label, qa, timeline }) -> the stored draft:
23
+ * { n (1, 2, ... per character), at (ms), characterId, take, frames, installed, specId, label,
24
+ * qa: { ok, total, groups: [{ kind, subject, count, first, last, worst }], goals: [{ name, kind, ok, value, need, at }] },
25
+ * timeline: the compile's beats (pipeline.js timelineOf) }.
26
+ */
27
+ export function publishDraft(characterId, { take, installed = false, specId = null, label = null, qa = null, timeline = [] }) {
28
+ const list = drafts.get(characterId) ?? [];
29
+ const draft = Object.freeze({
30
+ n: (list.at(-1)?.n ?? 0) + 1, at: Date.now(), seq: ++counter, characterId, take, frames: take?.frameCount ?? 0,
31
+ installed: !!installed, specId, label,
32
+ qa: { ok: !!qa?.ok, total: qa?.total ?? 0,
33
+ groups: (qa?.groups ?? []).map(g => pick(g, ["kind", "subject", "count", "first", "last", "worst"])),
34
+ goals: (qa?.goals ?? []).map(g => pick(g, ["name", "kind", "ok", "value", "need", "at"])) },
35
+ timeline: timeline ?? [],
36
+ });
37
+ drafts.set(characterId, [...list, draft].slice(-KEEP));
38
+ emit();
39
+ return draft;
40
+ }
41
+
42
+ /** The drafts of a character, oldest first (at most KEEP). */
43
+ export const draftsOf = characterId => drafts.get(characterId) ?? [];
44
+
45
+ /** The newest draft of a character, or of any character when none is named; null when there is none. */
46
+ export function latestDraft(characterId) {
47
+ if (characterId !== undefined) return draftsOf(characterId).at(-1) ?? null;
48
+ let best = null;
49
+ for (const list of drafts.values()) if (list.length && (!best || list.at(-1).seq > best.seq)) best = list.at(-1);
50
+ return best;
51
+ }
52
+
53
+ /** publishSheet({ characterId, dataUrl, width, height, caption, frames }) -> the stored image record. */
54
+ export function publishSheet({ characterId = null, dataUrl, width = 0, height = 0, caption = "", frames = [] }) {
55
+ const sheet = Object.freeze({ at: Date.now(), seq: ++counter, characterId, dataUrl, width, height, caption, frames });
56
+ sheets.push(sheet);
57
+ sheets.splice(0, Math.max(0, sheets.length - KEEP));
58
+ emit();
59
+ return sheet;
60
+ }
61
+
62
+ /** The published images, oldest first (at most KEEP). */
63
+ export const progressSheets = () => [...sheets];
64
+
65
+ /** Forget every draft and image (a new agent turn). */
66
+ export function clearProgress() {
67
+ drafts.clear();
68
+ sheets.length = 0;
69
+ emit();
70
+ }
71
+
72
+ /** subscribeProgress(listener) -> unsubscribe; with progressVersion it fits React's useSyncExternalStore. */
73
+ export function subscribeProgress(listener) {
74
+ listeners.add(listener);
75
+ return () => { listeners.delete(listener); };
76
+ }
77
+
78
+ /** A number that changes on every publish or clear. */
79
+ export const progressVersion = () => version;
@@ -0,0 +1,30 @@
1
+ // The agent turn's controls, and its mark. One user message is one editor transaction
2
+ // (#702, #715): every edit of the turn lands in one composed session, so the message is one
3
+ // undo step. A script runs inside that turn, after edits the same message already made (an
4
+ // installed take, a placed set). When the script fails, only its own edits roll back:
5
+ // agent.turn.mark records where it starts, and agent.turn.rollback restores exactly what came
6
+ // after the mark and leaves the turn open, so the earlier edits still land with the turn.
7
+ import { StudioProtocolError } from './studio-agent-protocol.js';
8
+
9
+ const TURN_CONTROLS = ['begin', 'mark', 'rollback', 'finish', 'cancel'];
10
+ /** The wire schema of each agent.turn.* control: the owning turn's id, nothing else. */
11
+ export const agentTurnControls = (object, identifier) => Object.fromEntries(TURN_CONTROLS.map(verb => [`agent.turn.${verb}`, object({ turnId: identifier })]));
12
+
13
+ /**
14
+ * agent.turn.mark or agent.turn.rollback on the open turn. Answers the transaction its receipt
15
+ * describes: a mark affects nothing, a rollback names the ids the rolled-back edits touched.
16
+ * The bus adds each later edit's ids to `turn.mark.since`.
17
+ */
18
+ export function markAgentTurn(id, turn) {
19
+ if (id === 'agent.turn.mark') {
20
+ turn.mark = { point: turn.session?.savepoint() ?? null, affectedIds: new Set(turn.affectedIds), since: new Set() };
21
+ return { txId: turn.txId, affectedIds: [], toasts: [] };
22
+ }
23
+ const { mark } = turn;
24
+ if (!mark) throw new StudioProtocolError('STALE_TARGET', 'The agent turn has no mark to roll back to.');
25
+ turn.mark = null;
26
+ // No session at the mark: everything the turn authored came after it.
27
+ turn.session?.rollback(mark.point ?? undefined);
28
+ turn.affectedIds = mark.affectedIds;
29
+ return { txId: turn.txId, affectedIds: [...mark.since], toasts: [] };
30
+ }
package/src/analytics.js CHANGED
@@ -905,7 +905,7 @@ function runtimeConfig() {
905
905
  function isLoopbackOrigin(origin) {
906
906
  try {
907
907
  const parsed = new URL(origin);
908
- return parsed.protocol === "http:" && parsed.hostname === "127.0.0.1";
908
+ return parsed.protocol === "http:" && ["127.0.0.1", "localhost"].includes(parsed.hostname);
909
909
  } catch {
910
910
  return false;
911
911
  }
@@ -1051,17 +1051,10 @@ export async function initAnalytics() {
1051
1051
  void recordDeviceProfile();
1052
1052
  if (resolved.distribution === "npm") {
1053
1053
  track("app:session_started");
1054
- // This follows the session marker so the two events form one
1055
- // capability baseline in funnel queries.
1056
- void recordMotionBackendState();
1057
1054
  if (resolved.firstLaunch) {
1058
1055
  const heardFrom = resolved.firstLaunchHeardFrom;
1059
1056
  track("install:first_launch", heardFrom ? { heard_from: heardFrom } : {});
1060
1057
  }
1061
- } else {
1062
- // Hosted sessions have PostHog's native session marker rather than
1063
- // the npm-only custom event above.
1064
- void recordMotionBackendState();
1065
1058
  }
1066
1059
  } catch {
1067
1060
  console.info("[analytics] initialization failed");
@@ -1070,17 +1063,6 @@ export async function initAnalytics() {
1070
1063
  await initPromise;
1071
1064
  }
1072
1065
 
1073
- async function recordMotionBackendState() {
1074
- let health = null;
1075
- try {
1076
- const response = await fetch("/ardy/health", { signal: AbortSignal.timeout(5000) });
1077
- if (response.ok) health = await response.json();
1078
- } catch {
1079
- // No bridge is a normal hosted/demo state.
1080
- }
1081
- track("motion:backend_state", motionBackendState(health));
1082
- }
1083
-
1084
1066
  export function track(event, props = {}) {
1085
1067
  if (!initialized || !enabled || !posthog) return;
1086
1068
  try {
@@ -98,6 +98,11 @@ export function createAppContext({
98
98
  closed = true; activeComposition = null;
99
99
  return remember(changed);
100
100
  },
101
+ // A savepoint inside the open composition (a script inside an agent turn): rollback(point)
102
+ // restores each owner to it, and an owner first touched after it to where it began. The
103
+ // composition stays open, so edits made before the savepoint still land with it.
104
+ savepoint() { check(); return new Map([...members].map(([handle, row]) => [handle, row.session.mark()])); },
105
+ rollback(point = new Map()) { check(); for (const [handle, row] of [...members].reverse()) row.session.rollback(point.get(handle)); },
101
106
  cancel(options) {
102
107
  if (closed) return false;
103
108
  closed = true; activeComposition = null;
package/src/app-stage.jsx CHANGED
@@ -22,8 +22,6 @@ export {
22
22
  characterModelUrl,
23
23
  CHARACTER_MODEL_LABELS,
24
24
  nextCharacterId,
25
- DEMO_MOTION_URL,
26
- DEMO_MOTION_PROMPT,
27
25
  OBJECT_DELETE_UNDO_MS,
28
26
  ASSET_DELETE_UNDO_MS,
29
27
  CLAY,
@@ -64,7 +62,7 @@ export {
64
62
 
65
63
  export * from "./stage-attach-frames.js";
66
64
 
67
- export { Character, MotionTrails } from "./stage-characters.jsx";
65
+ export { Character } from "./stage-characters.jsx";
68
66
 
69
67
  export {
70
68
  ShotRig,
@@ -105,40 +103,10 @@ export {
105
103
  } from "./stage-capture.jsx";
106
104
  export {
107
105
  TIMELINE_FPS,
108
- ARDY_FPS,
109
- toArdyFrame,
110
- posePlacementFrame,
111
- toArdyFrameEntries,
112
- toArdySegments,
113
106
  DEFAULT_DURATION_S,
114
107
  DEFAULT_PLAYBACK_SPEED,
115
- BRIDGE_RECHECK_MS,
116
- ARDY_PROMPT_HORIZON_FRAMES,
117
108
  MAX_WAYPOINTS,
118
- ARDY_PROMPT_MAX,
119
- ARDY_DURATION_MIN,
120
- ARDY_DURATION_MAX,
121
- ARDY_SEED_MAX,
122
- ARDY_PRESERVE_DEFAULT,
123
- DEFAULT_PROMPT_CLIPS,
124
- buildPromptSchedule,
125
- ikTracksInRange,
126
- PRESERVE_TRACK_LIMBS,
127
- PRESERVE_LIMB_ORDER,
128
- preserveLimbLabel,
129
- preserveTracksSummary,
130
- LINE_EDIT_TRACK_OPTIONS,
131
- lineTrackLabel,
132
- LINE_EDIT_DEFAULT_TRACK,
133
- LINE_CURVE_MARKER_STRIDE,
134
- MIN_CURVE_POINTS,
135
- LINE_PREVIEW_DEBOUNCE_MS,
136
- LINE_CAPABILITY_RETRY_MS,
137
- LINE_EDIT_REFUSALS,
138
- LINE_CURVE_REFUSALS,
139
- MULTIMODEL_REASONS,
140
- MULTIMODEL_SAMPLE_FPS,
141
- } from "./ardy-bridge-contract.js";
109
+ } from "./timeline-defaults.js";
142
110
 
143
111
  /* ------------------------------------------------------------------ 3D --- */
144
112
 
@@ -3,7 +3,7 @@ import { ko } from "./locale.js";
3
3
  import { CHARACTER_MODEL_IDS, PROXY_FIGURE_MODEL } from "./scenes.js";
4
4
  import { OBJECT_LIBRARY } from "./scene-objects.js";
5
5
  import { displayObjectLabel } from "./object-catalog.jsx";
6
- import { assetAspect, isMeshAssetId, isSupportedMeshType } from "./scene-assets.js";
6
+ import { assetAspect, isImageAssetId, isMeshAssetId, isSupportedMeshType } from "./scene-assets.js";
7
7
  import { assetKind, formatAssetBytes } from "./asset-shelf.js";
8
8
  import { assetRecord } from "./scene-asset-cache.js";
9
9
  import ResourceStatus from "./resource-status.jsx";
@@ -99,14 +99,15 @@ function loadThumb(id) {
99
99
  // card hides. A present record whose bytes fail below resolves null
100
100
  // instead, and the card stays visible so it can be deleted.
101
101
  if (!record) return undefined;
102
- if (isMeshAssetId(record.id) || isSupportedMeshType(record.type)) {
102
+ // An HDR environment file (hdr- id) is no picture either: it gets the same generic card.
103
+ if (!isImageAssetId(record.id) || isSupportedMeshType(record.type)) {
103
104
  // A GLB is not a picture. Decoding it as one would mark every
104
105
  // model "unreadable" in Manage storage — show a generic cube
105
106
  // instead, using the same card preview the catalogue already has.
106
107
  return {
107
108
  name: record.name,
108
109
  bytesLabel: formatAssetBytes(record.bytes.byteLength),
109
- kind: "mesh",
110
+ kind: isMeshAssetId(record.id) || isSupportedMeshType(record.type) ? "mesh" : "environment",
110
111
  mesh: true,
111
112
  };
112
113
  }
@@ -167,7 +168,7 @@ function StorageAssetRow({ id, onDelete, deleting, usageCount = 0, graphSignatur
167
168
  const usageLabel = ko(`Used by ${usageCount} scene object${usageCount === 1 ? "" : "s"}`, `${usageCount}개 씬 오브젝트에서 사용 중`);
168
169
  const deleteLabel = ko(`Delete ${name} from storage`, `${name}을(를) 저장소에서 삭제`);
169
170
  const confirmationId = `asset-storage-warning-${id}`;
170
- const kindLabel = thumb?.kind === "mesh" ? ko("Model", "모델") : thumb?.kind === "matte" ? ko("Matte", "매트") : ko("Image", "이미지");
171
+ const kindLabel = thumb?.kind === "mesh" ? ko("Model", "모델") : thumb?.kind === "environment" ? ko("HDR environment", "HDR 환경") : thumb?.kind === "matte" ? ko("Matte", "매트") : ko("Image", "이미지");
171
172
  return (
172
173
  <li className="asset-storage-row">
173
174
  {mesh ? (
@@ -486,7 +487,7 @@ export function FolderGrid({ folder, query, ...props }) {
486
487
  />
487
488
  );
488
489
  });
489
- if (!takeVersions.length) empty = ko("No takes for this character yet. Generate Motion creates one.", "이 인물의 테이크가 아직 없어요. Generate Motion으로 만들 수 있어요.");
490
+ if (!takeVersions.length) empty = ko("No takes for this character yet. Ask the agent to compile one.", "이 인물의 테이크가 아직 없어요. 에이전트에게 모션 컴파일을 요청하세요.");
490
491
  } else if (folder === "poses") {
491
492
  tiles = poses.map((pose, index) => ({ pose, index, label: pose?.name || pose?.label || ko(`Pose ${index + 1}`, `포즈 ${index + 1}`) }))
492
493
  .filter(({ label }) => matchesQuery(label, query))
@@ -59,6 +59,26 @@ function cloneRailFollow(value) {
59
59
  return null;
60
60
  }
61
61
 
62
+ /**
63
+ * What a rail shot's head holds: up to 4 cast ids (their middle, frame by
64
+ * frame, at their chest or at a world height) or one world point. Null = the follow subject (every older shot).
65
+ * Like the crane, an aim and a lens of its own (focalMm) ride the rail and
66
+ * are left out of the block without one, so untouched shots stay byte-stable.
67
+ */
68
+ function cloneRailAim(value) {
69
+ const ids = Array.isArray(value?.ids) ? value.ids.filter((id) => typeof id === "string" && id).slice(0, 4) : [];
70
+ if (ids.length) return Number.isFinite(value.height) ? { ids, height: value.height } : { ids };
71
+ const point = value?.point;
72
+ return point && [point.x, point.y, point.z].every(Number.isFinite) ? { point: { x: point.x, y: point.y, z: point.z } } : null;
73
+ }
74
+
75
+ /** The aim and lens fields a block with a rail carries (none without one). */
76
+ export function railShotFields(value, cameraRail) {
77
+ const aim = cameraRail ? cloneRailAim(value.aim) : null;
78
+ const focalMm = cameraRail && Number.isFinite(value.focalMm) && value.focalMm > 0 ? value.focalMm : null;
79
+ return { ...(aim ? { aim } : {}), ...(focalMm ? { focalMm } : {}) };
80
+ }
81
+
62
82
  /** Build a complete block from a partial or stored camera value. */
63
83
  export function createCameraBlock(input = {}) {
64
84
  const value = input && typeof input === "object" ? input : {};
@@ -82,6 +102,7 @@ export function createCameraBlock(input = {}) {
82
102
  railFollow: cloneRailFollow(value.railFollow),
83
103
  craneHeight,
84
104
  dollyTiming: timingIsFlat(dollyTiming) ? null : dollyTiming,
105
+ ...railShotFields(value, cameraRail),
85
106
  };
86
107
  }
87
108
 
@@ -0,0 +1,120 @@
1
+ // Pure camera direction helpers shared by playback, export and the agent's
2
+ // receipts: the per-frame follow/rail camera of every shot, where a rail
3
+ // shot's head aims, and the stops a chain of camera keys makes.
4
+
5
+ import { createCameraBlock } from "./camera-block.js";
6
+ import { buildFollowTrack, buildRail, buildRailFollowTrack } from "./camera-follow.js";
7
+ import { shortestArc } from "./camera-move.js";
8
+ import { RAIL_SCHEDULE_LEGACY, RAIL_SCHEDULE_RANGE, resolveRailSchedule } from "./camera-rail-schedule.js";
9
+ import { shotAtFrame } from "./cuts.js";
10
+ import { objectTransformAt } from "./object-path.js";
11
+ import { placementAt } from "./root-path.js";
12
+ import { sampleAt } from "./sample-at.js";
13
+ import { SUBJECT_HEIGHT_M, focalMmToFov } from "./shot.js";
14
+ import { takeHips } from "./motion3/take.js";
15
+
16
+ const DEG = Math.PI / 180;
17
+ /** where a character's aim sits on its body: chest height, as a share of its stature, or above a take's hips */
18
+ const CHEST = 0.75, HIPS_TO_CHEST = 0.35;
19
+
20
+ /**
21
+ * The follow and rail cameras of every shot, one sample per timeline frame
22
+ * (null where a shot keys its camera or its rail is off). A rail shot with
23
+ * an aim holds that aim instead of the follow subject and runs its rail on
24
+ * schedule (no distance pull, no pitch offset), with its own lens when the
25
+ * block names one. aimAt(id, frame) answers one cast member's aim point.
26
+ */
27
+ export function buildShotCameraTracks({ shots, subjectTrack, fps, yawDeg = 0, aimAt = () => null, filmback = {} }) {
28
+ const combined = new Array(subjectTrack.length).fill(null);
29
+ const initialDir = { x: Math.sin(yawDeg * DEG), z: Math.cos(yawDeg * DEG) };
30
+ for (const shot of shots) {
31
+ const camera = createCameraBlock(shot.camera);
32
+ if (camera.mode === "keys") continue;
33
+ const start = shot.startFrame, subjectSlice = subjectTrack.slice(start, Math.min(subjectTrack.length, shot.endFrame + 1));
34
+ const params = { ...camera.followCam, craneHeight: camera.craneHeight, dollyTiming: camera.dollyTiming, initialDir };
35
+ const rail = camera.mode === "rail" ? buildRail(camera.cameraRail) : null;
36
+ if (!rail) {
37
+ buildFollowTrack(subjectSlice, fps, params).forEach((sample, offset) => { combined[start + offset] = sample; });
38
+ continue;
39
+ }
40
+ const schedule = resolveRailSchedule({ railFollow: camera.railFollow, cameraRail: camera.cameraRail, frameCount: subjectSlice.length });
41
+ if (schedule.kind !== RAIL_SCHEDULE_RANGE && schedule.kind !== RAIL_SCHEDULE_LEGACY) continue;
42
+ const from = start + schedule.startFrame, length = schedule.endFrame - schedule.startFrame + 1;
43
+ const aim = camera.aim ? Array.from({ length }, (_, offset) => railAimPoint(camera.aim, from + offset, aimAt)) : null;
44
+ const aimed = aim?.every(Boolean) ? aim : null;
45
+ const track = aimed
46
+ ? buildRailFollowTrack(aimed, fps, rail, { ...params, aimHeight: aimed.reduce((sum, point) => sum + point.y, 0) / length, distanceInfluence: 0, pitchOffsetDeg: 0 })
47
+ : buildRailFollowTrack(subjectSlice.slice(schedule.startFrame, schedule.endFrame + 1), fps, rail, params);
48
+ const fovDeg = camera.focalMm ? focalMmToFov(camera.focalMm, filmback.sensorId, filmback.aspectRatio) / DEG : null;
49
+ track.forEach((sample, offset) => { combined[from + offset] = fovDeg ? { ...sample, fovDeg } : sample; });
50
+ }
51
+ return combined;
52
+ }
53
+
54
+ /** A rail aim at one frame: its world point, or the middle of its cast (null when one is missing). */
55
+ function railAimPoint(aim, frame, aimAt) {
56
+ if (aim.point) return aim.point;
57
+ const points = aim.ids.map((id) => aimAt(id, frame));
58
+ if (points.some((point) => !point)) return null;
59
+ const mean = (axis) => points.reduce((sum, point) => sum + point[axis], 0) / points.length;
60
+ return { x: mean("x"), y: aim.height ?? mean("y"), z: mean("z") };
61
+ }
62
+
63
+ /**
64
+ * One cast member's aim point at a frame: a character's chest where its take
65
+ * (or placement) puts it, an object's middle where its route (or placement)
66
+ * puts it. clipOf(character) is the take the character plays.
67
+ */
68
+ export function castAimPoint(id, frame, { characters = [], objects = [], shots = [], clipOf = () => null, frameCount, fps }) {
69
+ const character = characters.find((entry) => entry.id === id);
70
+ if (character) {
71
+ const clip = clipOf(character), scale = character.scale ?? 1, yaw = (character.rot ?? 0) * DEG;
72
+ // A take v3 moves the hips in the character's placement frame (motion3/take.js), not its root.
73
+ if (clip?.hips) {
74
+ const [x, y, z] = takeHips(clip, frame);
75
+ return { x: character.x + (x * Math.cos(yaw) + z * Math.sin(yaw)) * scale, y: (character.y ?? 0) + (y + HIPS_TO_CHEST) * scale, z: character.z + (-x * Math.sin(yaw) + z * Math.cos(yaw)) * scale };
76
+ }
77
+ const at = placementAt(character, frame, { shotAt: (at) => shotAtFrame(shots, at),
78
+ takeRoot: clip ? sampleAt({ frameCount: clip.frames, motion: clip }, null, frame).subject : null });
79
+ return { x: at.x, y: (character.y ?? 0) + CHEST * SUBJECT_HEIGHT_M * (character.scale ?? 1), z: at.z };
80
+ }
81
+ const object = objects.find((entry) => entry.id === id);
82
+ if (!object) return null;
83
+ const at = objectTransformAt(object, frame, { frameCount, fps }) ?? object;
84
+ return { x: at.x, y: at.y + ((object.height ?? 0) * (object.scaleY ?? 1)) / 2, z: at.z };
85
+ }
86
+
87
+ /** The follow subject's path, where its take v3 moves its body rather than its root (a follow or
88
+ * aimless rail camera keeps up with the walk); any other subject keeps the path it has. */
89
+ export function followSubjectTrack(subjectTrack, subjectId, cast) {
90
+ const character = cast.characters?.find((entry) => entry.id === subjectId);
91
+ if (!subjectTrack || !character || !cast.clipOf(character)?.hips) return subjectTrack;
92
+ return subjectTrack.map((point, frame) => castAimPoint(subjectId, frame, cast) ?? point);
93
+ }
94
+
95
+ /**
96
+ * The frames of the keys a camera halts at for no reason: an interior key the
97
+ * move runs on through in the same direction. Every key eases in and out, so
98
+ * the camera slows to a stop there; a hold, a reversal or a new direction is
99
+ * a stop the shot means and is not listed.
100
+ */
101
+ export function cameraKeyStops(keys = []) {
102
+ const steps = keys.slice(1).map((key, index) => {
103
+ const a = keys[index].framing, b = key.framing;
104
+ return [b.pos.x - a.pos.x, b.pos.y - a.pos.y, b.pos.z - a.pos.z, shortestArc(a.yaw, b.yaw), b.pitch - a.pitch, (b.fovDeg - a.fovDeg) * DEG];
105
+ });
106
+ const length = (step) => Math.hypot(...step);
107
+ return keys.slice(1, -1).filter((_, index) => {
108
+ const u = steps[index], v = steps[index + 1], dot = u.reduce((sum, value, axis) => sum + value * v[axis], 0);
109
+ return length(u) > 0.02 && length(v) > 0.02 && dot > 0.5 * length(u) * length(v);
110
+ }).map((key) => key.frame);
111
+ }
112
+
113
+ /** A receipt warning for a shot whose camera keys halt a move (none when they do not). */
114
+ export function cameraKeyWarnings(shot) {
115
+ const stops = cameraKeyStops(shot.cameraKeys);
116
+ if (!stops.length) return [];
117
+ const listed = `${stops.slice(0, 4).join(", ")}${stops.length > 4 ? ` +${stops.length - 4}` : ""}`;
118
+ return [{ code: "CAMERA_KEY_STOPS", id: shot.id, count: stops.length,
119
+ message: `Camera stops at keys ${listed} (keys ease in/out); use a rail (shot.setCameraRail) or 2 keys.`.slice(0, 120) }];
120
+ }
@@ -6,7 +6,7 @@ import { isProxyFigure } from "./scenes.js";
6
6
  import { isKo } from "./locale.js";
7
7
  import { StudioProtocolError } from "./studio-agent-protocol.js";
8
8
 
9
- export const CHARACTER_CAPABILITIES = Object.freeze(["rig", "ik", "pose", "motion", "mocap", "physics", "trails", "lineEdit"]);
9
+ export const CHARACTER_CAPABILITIES = Object.freeze(["rig", "ik", "pose", "motion"]);
10
10
  const PROXY_BODY_RADIUS = 0.22;
11
11
  const PROXY_BODY_HEIGHT = 1.45;
12
12
  const PROXY_HEAD_RADIUS = 0.12;
@@ -36,7 +36,7 @@ export function proxyFigureBounds(character, placement = character) {
36
36
  return { min, max };
37
37
  }
38
38
 
39
- /** `{ rig, ik, pose, motion, mocap, physics, trails, lineEdit }` - all false
39
+ /** `{ rig, ik, pose, motion }` - all false
40
40
  * for a capsule figure, all true for a rigged character. */
41
41
  export function characterCapabilities(character) {
42
42
  const rigged = !isProxyFigure(character);
@@ -48,13 +48,7 @@ const FEATURE_LABELS = {
48
48
  rig: ["the rig", "리그"],
49
49
  ik: ["IK", "IK"],
50
50
  pose: ["Pose mode", "포즈 모드"],
51
- motion: ["Motion generation", "모션 생성"],
52
- mocap: ["Motion capture", "모션 캡처"],
53
- photoPose: ["Photo pose", "사진 포즈"],
54
- physics: ["Physics", "물리 보정"],
55
- collision: ["Collision cleanup", "충돌 정리"],
56
- trails: ["Motion trails", "궤적선"],
57
- lineEdit: ["Line edit", "라인 편집"],
51
+ motion: ["Compiled motion", "모션 컴파일"],
58
52
  take: ["Loading a take", "테이크 불러오기"],
59
53
  };
60
54
 
@@ -3,6 +3,7 @@
3
3
  // idempotency. Synchronous UI actions stay synchronous (including user gestures).
4
4
  import { HISTORY_LIMIT } from './history.js';
5
5
  import { StudioProtocolError, StudioSchemas, validateStudioSchema, validateReceipt, validateStudioIdentity } from './studio-agent-protocol.js';
6
+ import { agentTurnControls, markAgentTurn } from './agent-turn-mark.js';
6
7
 
7
8
  // A batching/retention adapter over the existing object history owner. The
8
9
  // native store still owns every snapshot and transition; this tracks only the
@@ -62,9 +63,7 @@ export function createCommandBus({ registry, ports }) {
62
63
  const object = properties => ({ type: 'object', properties, required: Object.keys(properties), additionalProperties: false });
63
64
  const argsSchema = { type: 'object', properties: {}, required: [], additionalProperties: true };
64
65
  const controls = {
65
- 'agent.turn.begin': object({ turnId: identifier }),
66
- 'agent.turn.finish': object({ turnId: identifier }),
67
- 'agent.turn.cancel': object({ turnId: identifier }),
66
+ ...agentTurnControls(object, identifier),
68
67
  'run.begin': object({ id: identifier, args: argsSchema }),
69
68
  'run.update': object({ txId: identifier, args: argsSchema }),
70
69
  'run.commit': object({ txId: identifier }),
@@ -94,6 +93,8 @@ export function createCommandBus({ registry, ports }) {
94
93
  }
95
94
  if (agentTurn?.txId !== args.turnId) fail('STALE_TARGET', 'The agent turn is no longer open.');
96
95
  const turn = agentTurn;
96
+ // A script's mark, and the rollback to it that keeps the turn open (agent-turn-mark.js).
97
+ if (id === 'agent.turn.mark' || id === 'agent.turn.rollback') return transactionReceipt(id, markAgentTurn(id, turn), request, before);
97
98
  // A rolled-back turn restores every owner it touched and leaves no entry.
98
99
  if (id === 'agent.turn.cancel') { agentTurn = null; turn.session?.cancel(); return transactionReceipt(id, turn, request, before); }
99
100
  try { turn.historyEntryId = turn.session?.commit().historyEntryId; }
@@ -219,7 +220,7 @@ export function createCommandBus({ registry, ports }) {
219
220
  if (entry.kind === 'mutation' && changed && !historyEntryId && !turn) fail('UNCERTAIN_APPLY', `${entry.id} changed the scene without one undoable entry.`);
220
221
  const completed = entry.kind === 'job' || entry.kind === 'document' || (entry.kind === 'mutation' && changed && (turn || after.revision > before.revision + 1));
221
222
  const ids = completed || changed ? result.affectedIds : entry.kind === 'transient' ? [before.host.sceneId] : [];
222
- if (turn && changed) for (const id of ids) turn.affectedIds.add(id);
223
+ if (turn && changed) for (const id of ids) { turn.affectedIds.add(id); turn.mark?.since.add(id); }
223
224
  const build = level => validateReceipt({ ok: true, commandId: request.commandId, receiptId: crypto.randomUUID(), host: before.host,
224
225
  action: entry.id, summary: result.summary, status: completed ? 'completed' : entry.kind === 'transient' ? 'transient' : changed ? 'applied' : 'noop',
225
226
  authored: changed, ...(completed ? { kind: entry.kind, ...(result.output === undefined ? {} : { output: result.output }), ...(same(before.host, after.host) ? {} : { nextHost: after.host }) } : entry.kind === 'transient' ? { view: { before: before.viewRevision ?? 0, after: after.viewRevision ?? 0 } } : { mutated: changed }),
@@ -292,8 +293,9 @@ export function createCommandBus({ registry, ports }) {
292
293
  // Job controls observe/cancel an admitted identity; completion itself
293
294
  // can advance the revision while their wire request is in transit.
294
295
  // Document identity and the job's own publication fence still apply.
295
- if (!['job.await', 'job.cancel', 'agent.turn.finish', 'agent.turn.cancel'].includes(id) && request.expectedRevision !== before.revision) fail('STALE_SCENE', 'Authored state changed; obtain fresh intent.');
296
- if (before.busy && !turn && !transactions.has(validated.txId) && !id.startsWith('job.')) fail('TARGET_BUSY', 'Finish the current editor gesture first.');
296
+ // A readOnly action (declared data, e.g. motion.qa) only measures: no revision or gesture fence, the host check above still holds.
297
+ if (!entry?.readOnly && !['job.await', 'job.cancel', 'agent.turn.mark', 'agent.turn.rollback', 'agent.turn.finish', 'agent.turn.cancel'].includes(id) && request.expectedRevision !== before.revision) fail('STALE_SCENE', 'Authored state changed; obtain fresh intent.');
298
+ if (!entry?.readOnly && before.busy && !turn && !transactions.has(validated.txId) && !id.startsWith('job.')) fail('TARGET_BUSY', 'Finish the current editor gesture first.');
297
299
  }
298
300
  if (controls[id]) return mapResult(control(id, validated, request, before), remember, rejected);
299
301
  if (transactions.size) fail('TARGET_BUSY', 'Finish or cancel the open command transaction first.');
@@ -9,8 +9,7 @@ import { createCharacterEntry, CHARACTER_KIND_IDS, POSTURES, PROXY_FIGURE_MODEL,
9
9
  import { KIND_FIXED_REFUSAL } from '../character-kind.js';
10
10
  import { track } from '../analytics.js';
11
11
  import { DEFAULT_POSE } from '../poses.js';
12
- import { createStableItemId, updateStableItem, removeStableItem } from '../stable-items.js';
13
- import { movePromptClipFrames } from '../ardy/prompt-clips.js';
12
+ import { createStableItemId } from '../stable-items.js';
14
13
  import { STUDIO_TOOL_SCHEMAS, StudioSchemas } from '../studio-agent-protocol.js';
15
14
 
16
15
  const id = StudioSchemas.TargetGuard.properties.targetId;
@@ -21,7 +20,7 @@ const record = { type: 'object', properties: {}, additionalProperties: true };
21
20
  const newCharacter = { ...record, properties: { model: { type: 'string', enum: [...CHARACTER_KIND_IDS] }, posture: { type: 'string', enum: [...POSTURES] } }, required: [] };
22
21
  const characterPatch = { ...record, properties: { posture: { type: 'string', description: `One of ${POSTURES.join(', ')}; anything else stands.` } }, required: [] };
23
22
  const mutation = (id, label, schema, exposure = 'open') => ({ id, label, description: label, kind: 'mutation', undoDomain: 'cast', input: schema, exposure });
24
- const blocks = elementSetSchema('character').properties.set.properties.layer.properties.promptClips;
23
+
25
24
  const semantic = [
26
25
  mutation('character.set', 'Set character fields', elementSetSchema('character')),
27
26
  mutation('character.add', 'Add character', input({ character: newCharacter })),
@@ -29,7 +28,7 @@ const semantic = [
29
28
  mutation('character.update', 'Update character', input({ characterId: id, patch: characterPatch })),
30
29
  studioActionDeclaration('character.move'),
31
30
  mutation('character.setPose', 'Set character pose', input({ characterId: id, pose: { oneOf: [id, record, { type: 'null' }] }, clearMotion: { type: 'boolean' } }, ['characterId', 'pose'])),
32
- mutation('character.setPromptBlocks', 'Set prompt blocks', input({ characterId: id, blocks })),
31
+
33
32
  mutation('characters.arrange', 'Arrange characters', STUDIO_TOOL_SCHEMAS.arrange_characters),
34
33
  mutation('cast.replace', 'Replace cast', input({ characters: { type: 'array', items: record, minItems: 1 } }), 'ui-only'),
35
34
  mutation('cast.setCustomPoses', 'Set pose library', input({ poses: { type: 'array', items: record } }), 'ui-only'),
@@ -37,9 +36,6 @@ const semantic = [
37
36
  mutation('cast.removePose', 'Remove saved pose', input({ id }), 'ui-only'),
38
37
  mutation('cast.showExtras', 'Show extra subjects', input({ show: { type: 'boolean' } }), 'ui-only'),
39
38
  mutation('cast.setLayer', 'Set character layer', input({ characterId: id, layer: record }), 'ui-only'),
40
- ...['add', 'move', 'resize', 'change', 'remove'].map(verb => mutation(`character.${verb}PromptBlock`, `${verb} prompt block`,
41
- input({ characterId: id, id, frame: { type: 'number' }, edge: { type: 'string', enum: ['start', 'end'] }, text: { type: 'string', maxLength: 2000 } },
42
- verb === 'add' ? ['characterId', 'frame'] : verb === 'remove' ? ['characterId', 'id'] : verb === 'change' ? ['characterId', 'id', 'text'] : verb === 'resize' ? ['characterId', 'id', 'edge', 'frame'] : ['characterId', 'id', 'frame']), 'ui-only')),
43
39
  ];
44
40
  export const declarations = Object.freeze([...semantic, ...["character.addWaypoint", "character.moveWaypoint", "character.removeWaypoint", "character.clearWaypoints"].map(studioActionDeclaration)]);
45
41
 
@@ -56,29 +52,6 @@ export function register(registry, ports) {
56
52
  const object = ports.state().objects.find(row => row.id === value) ?? fail('STALE_TARGET', `Object ${value} is not in this scene.`);
57
53
  if (object.attach) fail('INVALID_ARGUMENT', 'A character cannot be grouped under an object that a character carries.');
58
54
  };
59
- function promptEdit(command, args) {
60
- const before = character(args.characterId), clips = before.layer.promptClips;
61
- let next;
62
- if (command === 'character.addPromptBlock') {
63
- const snapped = Math.max(0, Math.round(args.frame / 48) * 48);
64
- const blocked = clips.some(clip => snapped < clip.endFrame && snapped + 48 > clip.startFrame);
65
- const startFrame = blocked ? Math.max(snapped, ...clips.map(clip => clip.endFrame)) : snapped;
66
- next = [...clips, { id: createStableItemId('prompt-clip'), startFrame, endFrame: startFrame + 48, text: '' }];
67
- } else if (command === 'character.removePromptBlock') next = removeStableItem(clips, args.id, 'promptClips');
68
- else if (command === 'character.changePromptBlock') next = updateStableItem(clips, args.id, clip => ({ ...clip, text: args.text }), 'promptClips');
69
- else if (command === 'character.movePromptBlock') next = movePromptClipFrames(clips, args.id, args.frame, 48);
70
- else {
71
- next = updateStableItem(clips, args.id, clip => {
72
- const snapped = Math.max(0, Math.round(args.frame / 48) * 48);
73
- return args.edge === 'start' ? { ...clip, startFrame: Math.min(Math.max(snapped, clip.endFrame - 120), clip.endFrame - 48) }
74
- : { ...clip, endFrame: Math.min(Math.max(clip.startFrame + 48, snapped), clip.startFrame + 120) };
75
- }, 'promptClips');
76
- const resized = next.find(clip => clip.id === args.id);
77
- if (next.some(clip => clip.id !== args.id && resized.startFrame < clip.endFrame && resized.endFrame > clip.startFrame)) return;
78
- }
79
- patch(args.characterId, { layer: { ...before.layer, promptClips: next } });
80
- owner().extendTimeline(next.reduce((end, clip) => Math.max(end, clip.endFrame), 24));
81
- }
82
55
  const methods = {
83
56
  'character.add': ({ character: value }) => {
84
57
  const row = createCharacterEntry({ ...value, id: value.id ?? createStableItemId('character') }, owner().read().length);
@@ -94,9 +67,7 @@ export function register(registry, ports) {
94
67
  },
95
68
  'character.move': ({ characterId, ...value }) => patch(characterId, value),
96
69
  'character.setPose': ({ characterId, pose: value, clearMotion }) => { character(characterId); owner().applyPose(characterId, pose(value), clearMotion); },
97
- 'character.setPromptBlocks': ({ characterId, blocks }) => {
98
- const entry = character(characterId); patch(characterId, { layer: { ...entry.layer, promptClips: blocks } });
99
- },
70
+
100
71
  'cast.replace': ({ characters }) => owner().write(characters),
101
72
  'cast.setCustomPoses': ({ poses }) => owner().writeState(before => ({ ...before, customPoses: poses })),
102
73
  'cast.savePose': ({ pose, characterId, clearMotion }) => {
@@ -127,7 +98,7 @@ export function register(registry, ports) {
127
98
  for (const declaration of semantic.slice(1)) registry.register({ ...declaration, available: mounted, run(args, context) {
128
99
  if (declaration.id === 'characters.arrange') { const plan = owner().arrange(args); return { affectedIds: plan.affectedIds, summary: 'Arranged characters.', checks: plan.checks, warnings: plan.warnings }; }
129
100
  const before = owner().read();
130
- if (declaration.id.endsWith('PromptBlock')) promptEdit(declaration.id, args); else methods[declaration.id](args);
101
+ methods[declaration.id](args);
131
102
  // The Assets tile and the MCP live path report themselves; this is the agent and
132
103
  // any other non-UI bus caller adding a capsule figure.
133
104
  if (declaration.id === 'character.add' && context?.origin && context.origin !== 'ui' && isProxyFigure(owner().read().find(row => !before.some(prior => prior.id === row.id)))) track('cast:proxy_added', { surface: 'agent' });
@@ -5,7 +5,7 @@ import { registerElementKind } from '../elements.js';
5
5
  const elements = STUDIO_ELEMENTS.filter(row => row.path.startsWith('character.')).map(row => {
6
6
  const { min, max, gizmo, ...element } = row;
7
7
  if (row.path === 'character.position') return { ...element, documentPath: { x: 'x', y: 'y', z: 'z' } };
8
- if (row.path === 'character.promptBlocks') return { ...element, documentPath: 'layer.promptClips' };
8
+
9
9
  if (row.path === 'character.pose') return { ...element, readback: item => ({ text: item?.pose?.id ?? null }) };
10
10
  return element;
11
11
  });
@@ -1,14 +1,9 @@
1
1
  import { registerElementKind } from '../elements.js';
2
- import { wrapAngle } from '../../scene-objects.js';
3
2
 
4
3
  const elements = [
5
- ...['anchorX', 'anchorZ', 'rotationDeg'].map(field => ({ path: `motion.take.${field}`, type: 'number' })),
6
4
  { path: 'motion.take.prompt', type: 'string' },
7
5
  { path: 'motion.ikKeys', type: 'array', readOnly: true },
8
6
  { path: 'motion.takeRecipe', type: 'object', readOnly: true },
9
7
  { path: 'motion.takeVersions', type: 'array', readOnly: true },
10
8
  ];
11
- registerElementKind('motion', { collection: true, elements, normalize: row => ({ ...row, take: row.take && { ...row.take,
12
- anchorX: Math.max(-240, Math.min(240, row.take.anchorX ?? 0)), anchorZ: Math.max(-240, Math.min(240, row.take.anchorZ ?? 0)),
13
- rotationDeg: wrapAngle(row.take.rotationDeg ?? 0),
14
- } }) });
9
+ registerElementKind('motion', { collection: true, elements, normalize: row => ({ ...row, take: row.take && { ...row.take } }) });