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.
- package/CHANGELOG.md +60 -0
- package/README.md +12 -30
- package/bin/agent/agent-routes.mjs +40 -113
- package/bin/agent/agent-runner.mjs +9 -10
- package/bin/agent/cliproxy-provider.mjs +135 -0
- package/bin/agent/credential-store.mjs +30 -7
- package/bin/agent/pi-tools.mjs +4 -1
- package/bin/agent/provider-keys.mjs +32 -0
- package/bin/agent/provider-login.mjs +110 -0
- package/bin/agent/provider-routes.mjs +99 -0
- package/bin/agent/providers.mjs +19 -97
- package/bin/agent/script-tool.mjs +17 -4
- package/bin/agent/studio-motion-reference.mjs +23 -0
- package/bin/agent/studio-prompt.mjs +1 -1
- package/bin/agent/studio-rebase-text.mjs +32 -0
- package/bin/agent/studio-rebase.mjs +26 -39
- package/bin/agent/studio-tools.mjs +9 -28
- package/bin/agent/tool-arguments.mjs +49 -0
- package/bin/agent/turn-frames.mjs +66 -0
- package/bin/codex-auth.mjs +9 -2
- package/bin/cozyclay.mjs +10 -116
- package/bin/live/cli.mjs +3 -3
- package/bin/mcp-runtime.mjs +2 -12
- package/bin/package-signature.mjs +0 -4
- package/dist/app/index.html +7 -6
- package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
- package/dist/assets/analytics-C9sUnndd.js +1 -0
- package/dist/assets/app-CCh--1Hc.js +5087 -0
- package/dist/assets/app-f-fkV5uP.css +1 -0
- package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
- package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
- package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-CC4xe4HM.js} +1 -1
- package/dist/assets/{landing-ChUvB_zL.js → landing-DjHD8u7k.js} +1 -1
- package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
- package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
- package/dist/assets/workflow-C4yKH1p4.js +179 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/index.html +3 -3
- package/dist/sw.js +3 -6
- package/dist/workflow/index.html +5 -4
- package/mcp/LIVE-PROTOCOL.md +7 -47
- package/mcp/README.md +16 -26
- package/mcp/live-hub.mjs +6 -10
- package/mcp/package.json +1 -3
- package/mcp/tool-handlers.mjs +4 -168
- package/package.json +3 -22
- package/src/App.jsx +150 -2624
- package/src/agent-progress.js +79 -0
- package/src/agent-turn-mark.js +30 -0
- package/src/analytics.js +1 -19
- package/src/app-context.js +5 -0
- package/src/app-stage.jsx +2 -34
- package/src/asset-pane.jsx +6 -5
- package/src/camera-block.js +21 -0
- package/src/camera-direction.js +120 -0
- package/src/character-kind.js +3 -9
- package/src/command-bus.js +8 -6
- package/src/commands/cast.js +5 -34
- package/src/commands/elements/character.js +1 -1
- package/src/commands/elements/motion.js +1 -6
- package/src/commands/elements/object.js +6 -2
- package/src/commands/elements/shot.js +4 -1
- package/src/commands/elements.js +38 -14
- package/src/commands/index.js +3 -1
- package/src/commands/kits.js +37 -0
- package/src/commands/motion-timing.js +57 -0
- package/src/commands/motion.js +9 -152
- package/src/commands/motion3-compile.js +65 -0
- package/src/commands/motion3.js +207 -0
- package/src/commands/objects.js +6 -4
- package/src/commands/shot.js +49 -11
- package/src/commands/stage.js +14 -1
- package/src/commands/view.js +4 -1
- package/src/document-mirrors.js +42 -0
- package/src/document-store.js +4 -0
- package/src/domains/cast.js +19 -194
- package/src/domains/motion/motion-range-pin-rules.js +1 -1
- package/src/domains/motion/take-source.js +51 -0
- package/src/domains/motion.js +60 -1838
- package/src/domains/scenes.js +12 -55
- package/src/domains/shots.js +2 -2
- package/src/domains/stage.js +4 -2
- package/src/draft-ghost.js +46 -0
- package/src/draft-ghost.jsx +129 -0
- package/src/dualview.jsx +4 -3
- package/src/effects/EffectsLayer.jsx +112 -0
- package/src/effects/effect-meshes.js +232 -0
- package/src/effects/effect-record.js +103 -0
- package/src/effects/emitter.js +59 -0
- package/src/effects/simulate.js +261 -0
- package/src/effects/state.js +59 -0
- package/src/execution-telemetry.js +2 -3
- package/src/first-success-guide.jsx +5 -5
- package/src/ghost-set.js +257 -0
- package/src/held-object-edit.jsx +113 -0
- package/src/held-object-pose.js +175 -0
- package/src/hierarchy-model.js +1 -1
- package/src/hierarchy-panel.jsx +1 -1
- package/src/imported-mesh.jsx +103 -0
- package/src/kits/held.js +84 -0
- package/src/kits/index.js +70 -0
- package/src/kits/smoking.js +57 -0
- package/src/light-blockers.js +93 -0
- package/src/motion/beat-board-model.js +84 -0
- package/src/motion/beat-board.css +124 -0
- package/src/motion/beat-board.jsx +59 -0
- package/src/motion/center-of-mass.js +66 -0
- package/src/motion/fix-collisions.js +850 -0
- package/src/motion/physics-review.js +118 -0
- package/src/{ardy → motion}/physics-support.js +1 -76
- package/src/motion/rig-snapshot.js +22 -0
- package/src/motion/take-segments.jsx +140 -0
- package/src/{ardy → motion}/timeline-coordinates.js +0 -21
- package/src/{ardy → motion}/timeline.css +1 -2
- package/src/{ardy → motion}/timeline.jsx +42 -413
- package/src/motion/waypoints.js +76 -0
- package/src/motion3/beats.js +83 -0
- package/src/motion3/body.js +158 -0
- package/src/motion3/bones.js +47 -0
- package/src/motion3/carried.js +47 -0
- package/src/motion3/compile-job.js +75 -0
- package/src/motion3/compile-worker.js +10 -0
- package/src/motion3/compiler/anchors.js +105 -0
- package/src/motion3/compiler/arm-orient.js +128 -0
- package/src/motion3/compiler/channels.js +125 -0
- package/src/motion3/compiler/compile.js +205 -0
- package/src/motion3/compiler/gait-rhythm.js +97 -0
- package/src/motion3/compiler/grip.js +171 -0
- package/src/motion3/compiler/head-steady.js +48 -0
- package/src/motion3/compiler/hold.js +134 -0
- package/src/motion3/compiler/idle.js +113 -0
- package/src/motion3/compiler/limbs.js +170 -0
- package/src/motion3/compiler/math.js +72 -0
- package/src/motion3/compiler/pose.js +162 -0
- package/src/motion3/compiler/quat.js +49 -0
- package/src/motion3/compiler/skeleton.js +74 -0
- package/src/motion3/compiler/validate-common.js +32 -0
- package/src/motion3/compiler/validate-goals.js +118 -0
- package/src/motion3/compiler/validate-objects.js +143 -0
- package/src/motion3/compiler/validate-parts.js +116 -0
- package/src/motion3/compiler/validate.js +122 -0
- package/src/motion3/compiler/vocab.js +73 -0
- package/src/motion3/compiler/walk-pelvis.js +275 -0
- package/src/motion3/compiler/walk.js +207 -0
- package/src/motion3/compiler/wrist-clamp.js +32 -0
- package/src/motion3/delta.js +47 -0
- package/src/motion3/describe.js +174 -0
- package/src/motion3/docs.js +27 -0
- package/src/motion3/edit-carry.js +21 -0
- package/src/motion3/fix/ease.js +16 -0
- package/src/motion3/fix/ground.js +37 -0
- package/src/motion3/fix/index.js +52 -0
- package/src/motion3/fix/push.js +71 -0
- package/src/motion3/fix/reach.js +46 -0
- package/src/motion3/fix/retime.js +79 -0
- package/src/motion3/fix/self.js +211 -0
- package/src/motion3/fix/slide.js +70 -0
- package/src/motion3/fix/turn.js +51 -0
- package/src/motion3/follow.js +39 -0
- package/src/motion3/free-motion.js +173 -0
- package/src/motion3/grip-choice.js +130 -0
- package/src/motion3/hold-plan.js +157 -0
- package/src/motion3/json-patch.js +77 -0
- package/src/motion3/key-aim.js +105 -0
- package/src/motion3/key-edit.js +82 -0
- package/src/motion3/lenient.js +127 -0
- package/src/motion3/object-keys.js +236 -0
- package/src/motion3/object-pose.js +182 -0
- package/src/motion3/object-tracks.js +12 -0
- package/src/motion3/pipeline.js +102 -0
- package/src/motion3/playback.js +51 -0
- package/src/motion3/probes.js +91 -0
- package/src/motion3/qa/body-checks.js +34 -0
- package/src/motion3/qa/check-names.js +14 -0
- package/src/motion3/qa/checks.js +121 -0
- package/src/motion3/qa/contact-checks.js +51 -0
- package/src/motion3/qa/explain.js +80 -0
- package/src/motion3/qa/fmt.js +5 -0
- package/src/motion3/qa/foot-checks.js +72 -0
- package/src/motion3/qa/geometry.js +77 -0
- package/src/motion3/qa/goals.js +191 -0
- package/src/motion3/qa/ground.js +60 -0
- package/src/motion3/qa/hints-contact.js +179 -0
- package/src/motion3/qa/hints-goal.js +160 -0
- package/src/motion3/qa/hints-knee.js +24 -0
- package/src/motion3/qa/hints-motion.js +209 -0
- package/src/motion3/qa/hints-object.js +91 -0
- package/src/motion3/qa/index.js +42 -0
- package/src/motion3/qa/key-checks.js +82 -0
- package/src/motion3/qa/near.js +96 -0
- package/src/motion3/qa/object-checks.js +135 -0
- package/src/motion3/qa/patch.js +161 -0
- package/src/motion3/qa/severity.js +38 -0
- package/src/motion3/qa/spec-checks.js +65 -0
- package/src/motion3/qa/style.js +218 -0
- package/src/motion3/qa/wrist-checks.js +69 -0
- package/src/motion3/qa/wrist-obs.js +60 -0
- package/src/motion3/refs.js +120 -0
- package/src/motion3/repair-tune.js +121 -0
- package/src/motion3/repair.js +83 -0
- package/src/motion3/report.js +67 -0
- package/src/motion3/rigs/index.js +14 -0
- package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
- package/src/motion3/rigs/x-bot-tpose.json +1 -0
- package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
- package/src/motion3/rigs/y-bot-tpose.json +1 -0
- package/src/motion3/scene.js +60 -0
- package/src/motion3/schema/brief.js +89 -0
- package/src/motion3/schema/fields.js +44 -0
- package/src/motion3/schema/spec-examples.js +46 -0
- package/src/motion3/schema/topics.js +111 -0
- package/src/motion3/schema/types.js +147 -0
- package/src/motion3/search.js +117 -0
- package/src/motion3/second-hand.js +112 -0
- package/src/motion3/skills/basic.js +82 -0
- package/src/motion3/skills/book.js +76 -0
- package/src/motion3/skills/car.js +145 -0
- package/src/motion3/skills/frames-auto.js +29 -0
- package/src/motion3/skills/index.js +83 -0
- package/src/motion3/skills/info.js +65 -0
- package/src/motion3/skills/smoke-kit.js +109 -0
- package/src/motion3/skills/smoke.js +160 -0
- package/src/motion3/spec-store.js +59 -0
- package/src/motion3/take-edit.js +155 -0
- package/src/motion3/take.js +63 -0
- package/src/motion3/timeline.js +127 -0
- package/src/motion3/views.js +121 -0
- package/src/motion3/walk-plan.js +119 -0
- package/src/object-catalog.jsx +7 -1
- package/src/object-colors.js +98 -0
- package/src/object-gizmo-parts.js +115 -0
- package/src/object-gizmo.jsx +37 -122
- package/src/object-glow.js +139 -0
- package/src/object-hidden.js +33 -0
- package/src/object-look.js +196 -0
- package/src/object-size.js +10 -0
- package/src/object-travel.js +71 -6
- package/src/panels/CameraPanel.jsx +7 -7
- package/src/panels/HdrEnvironmentControls.jsx +59 -0
- package/src/panels/LightPanel.jsx +25 -4
- package/src/panels/ObjectLookPanel.jsx +88 -0
- package/src/panels/PosePanel.jsx +2 -17
- package/src/panels/RigControlPanel.jsx +5 -53
- package/src/panels/SubjectBox.jsx +4 -1
- package/src/panels/details.css +14 -0
- package/src/posestudio.jsx +4 -27
- package/src/project-browser.jsx +3 -4
- package/src/project-resources.js +18 -31
- package/src/project.js +13 -104
- package/src/props.jsx +25 -280
- package/src/rail-dot-drag.js +23 -0
- package/src/render-passes.js +10 -0
- package/src/resource-status.jsx +1 -0
- package/src/result-modal.jsx +4 -19
- package/src/room.jsx +20 -12
- package/src/scene-assets.js +8 -6
- package/src/scene-light-place.js +72 -0
- package/src/scene-lights.jsx +148 -0
- package/src/scene-objects.js +31 -139
- package/src/scenes.js +15 -105
- package/src/semantic-edit.js +2 -5
- package/src/set-pieces.jsx +183 -0
- package/src/settings-menu.jsx +3 -11
- package/src/shell/BottomDock.jsx +9 -51
- package/src/shell/DetailsSlot.jsx +16 -200
- package/src/shell/GhostsMenuGroup.jsx +74 -0
- package/src/shell/LibrarySlot.jsx +1 -3
- package/src/shell/MenuBar.jsx +3 -3
- package/src/shell/PreferencesDialog.jsx +15 -264
- package/src/shell/PreferencesSlot.jsx +2 -8
- package/src/shell/TopBar.jsx +5 -84
- package/src/shell/ViewportToolbar.jsx +10 -70
- package/src/shell/agent-glass.css +19 -6
- package/src/shell/details-selection.js +42 -0
- package/src/shell/ghosts-menu.css +22 -0
- package/src/shell/glass-regions.css +29 -3
- package/src/shell/preferences.css +6 -1
- package/src/shot-authoring.js +4 -7
- package/src/soft-shadows.js +37 -0
- package/src/stage-attach-frames.js +12 -12
- package/src/stage-canvas-rigs.jsx +3 -3
- package/src/stage-capture.jsx +42 -8
- package/src/stage-characters.jsx +22 -216
- package/src/stage-editor-gizmos.jsx +6 -6
- package/src/stage-environment.js +93 -0
- package/src/stage-hdr-builtins.js +107 -0
- package/src/stage-hdr-image.js +116 -0
- package/src/stage-hdr-library.js +70 -0
- package/src/stage-hdr-map.js +94 -0
- package/src/stage-hdr.js +130 -0
- package/src/stage-lighting.js +149 -0
- package/src/stage-occlusion.js +210 -0
- package/src/stage-sky.jsx +159 -0
- package/src/stage-startup-defaults.js +5 -9
- package/src/studio-actions.js +12 -14
- package/src/studio-agent-commands.js +19 -16
- package/src/studio-agent-motion.js +9 -9
- package/src/studio-agent-protocol.js +41 -71
- package/src/studio-app-binding.js +25 -45
- package/src/studio-asset-list.js +17 -0
- package/src/studio-contact-sheet.js +37 -5
- package/src/studio-elements.js +27 -7
- package/src/studio-entity-bounds.js +18 -0
- package/src/studio-error-codes.js +8 -0
- package/src/studio-export-framing.js +152 -0
- package/src/studio-lighting-check.js +103 -0
- package/src/studio-motion-visual.js +150 -0
- package/src/studio-schema-errors.js +80 -0
- package/src/studio-support-gap.js +74 -0
- package/src/styles.css +1 -19
- package/src/timeline-defaults.js +5 -0
- package/src/timeline-extent.js +8 -20
- package/src/tutorial-walk.js +17 -0
- package/src/ui-scrub.js +1 -1
- package/src/use-shot-camera-tracks.js +12 -0
- package/src/view-ghosts.js +97 -0
- package/src/view-ghosts.jsx +49 -0
- package/src/workflow/AgentPanel.jsx +47 -166
- package/src/workflow/AgentProgressCard.jsx +107 -0
- package/src/workflow/AgentReplyText.jsx +37 -0
- package/src/workflow/AgentTurnSteps.jsx +84 -0
- package/src/workflow/LoginPanel.jsx +140 -0
- package/src/workflow/WorkflowBuilder.jsx +7 -23
- package/src/workflow/agent-client.js +54 -51
- package/src/workflow/agent-panel.css +71 -1
- package/src/workflow/agent-progress-card.css +167 -0
- package/src/workflow/agent-progress-view.js +47 -0
- package/src/workflow/agent-turn-steps.css +113 -0
- package/src/workflow/agent-turn.js +191 -0
- package/src/workflow/login-client.js +34 -0
- package/src/workflow/motion-input.js +18 -31
- package/src/workflow/node-schema.js +1 -1
- package/src/workflow/reply-markdown.js +69 -0
- package/src/workflow/scene-asset-sync.js +8 -8
- package/tools/agent-session-stats.mjs +61 -0
- package/tools/dev-full.mjs +5 -56
- package/tools/eslint-baseline.json +12 -13
- package/tools/knip-baseline.json +0 -179
- package/tools/loc-baseline.json +31 -81
- package/tools/motion3/extract-body.mjs +103 -0
- package/tools/motion3/extract-rig.mjs +85 -0
- package/tools/process-supervisor.mjs +0 -88
- package/tools/run-tests.mjs +8 -124
- package/bin/agent/motion-runtime.mjs +0 -76
- package/dist/ardy/cskel27-rest.json +0 -557
- package/dist/assets/analytics-C9pMpW7s.js +0 -1
- package/dist/assets/app-B_2XB9rw.css +0 -1
- package/dist/assets/app-DAuvKPP8.js +0 -4920
- package/dist/assets/config-Bx7pYsD9.js +0 -1
- package/dist/assets/config-DeoFTYzb.css +0 -1
- package/dist/assets/demo-CvKuBgy5.js +0 -1
- package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
- package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
- package/dist/assets/ticket-BPk2Ni95.js +0 -1
- package/dist/assets/workflow-jJRBR94Z.js +0 -179
- package/dist/demo/walk-then-stop.npz +0 -0
- package/mcp/ardy-prompts.mjs +0 -218
- package/src/ardy/auto-fix-panel.css +0 -108
- package/src/ardy/auto-physics.js +0 -1194
- package/src/ardy/client.js +0 -153
- package/src/ardy/convert.js +0 -313
- package/src/ardy/cskel27-neutral.js +0 -39
- package/src/ardy/cskel27.js +0 -77
- package/src/ardy/export.js +0 -165
- package/src/ardy/fix-collisions.js +0 -2257
- package/src/ardy/foot-lock.js +0 -230
- package/src/ardy/motion-calibration.js +0 -118
- package/src/ardy/motion-edit.js +0 -213
- package/src/ardy/motion-url.js +0 -55
- package/src/ardy/mp4-duration.js +0 -102
- package/src/ardy/npz.js +0 -595
- package/src/ardy/physics-panel.css +0 -21
- package/src/ardy/physics-panel.jsx +0 -89
- package/src/ardy/physics-refine.js +0 -111
- package/src/ardy/physics-review.js +0 -567
- package/src/ardy/physics-temporal.js +0 -54
- package/src/ardy/platform-fit-panel.css +0 -64
- package/src/ardy/platform-fit-panel.jsx +0 -41
- package/src/ardy/platform-fit.js +0 -326
- package/src/ardy/playback.js +0 -711
- package/src/ardy/pose-pin.js +0 -97
- package/src/ardy/prompt-clips.js +0 -30
- package/src/ardy/retime.js +0 -211
- package/src/ardy/root-drop.js +0 -369
- package/src/ardy/timeline-resize.js +0 -13
- package/src/ardy/to-cskel27.js +0 -225
- package/src/ardy/trim.js +0 -33
- package/src/ardy/waypoints.js +0 -369
- package/src/ardy-bridge-contract.js +0 -281
- package/src/domains/motion/motion-video-capture.js +0 -664
- package/src/i2v-motion-client.js +0 -77
- package/src/i2v-motion-studio.jsx +0 -180
- package/src/line-edit.js +0 -1348
- package/src/motion/generation.js +0 -105
- package/src/motion-readiness-ui.jsx +0 -67
- package/src/motion-readiness.js +0 -40
- package/src/motion-resources.js +0 -177
- package/src/motion-store.js +0 -65
- package/src/motion-trail.js +0 -601
- package/src/multimodel-ingest.js +0 -394
- package/src/panels/PromptBlocksPanel.jsx +0 -444
- package/src/panels/VideoCapturePanel.jsx +0 -181
- package/src/pose-extract/browser.js +0 -42
- package/src/pose-extract/detector.js +0 -100
- package/src/pose-extract/fit.js +0 -227
- package/src/pose-extract/image-frame.js +0 -51
- package/src/pose-extract/index.js +0 -58
- package/src/pose-extract/keyframes.js +0 -74
- package/src/pose-extract/landmarks.js +0 -162
- package/src/pose-extract/mirror.js +0 -153
- package/src/pose-extract/take.js +0 -131
- package/src/pose-extract/video-frames.js +0 -91
- package/src/take-recipe.js +0 -165
- package/src/trail-key-conflicts.js +0 -39
- package/src/trail-pick.js +0 -59
- package/src/use-i2v-capture-modal.js +0 -15
- package/tools/ardy/BRIDGE.md +0 -37
- package/tools/ardy/GVHMR-NPZ.md +0 -68
- package/tools/ardy/README.md +0 -124
- package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
- package/tools/ardy/artifacts.mjs +0 -34
- package/tools/ardy/bridge.mjs +0 -1732
- package/tools/ardy/bvh-cskel27.mjs +0 -1265
- package/tools/ardy/cclay_constrained_generate.py +0 -561
- package/tools/ardy/cclay_gvhmr_worker.py +0 -264
- package/tools/ardy/cclay_motion_edit.py +0 -510
- package/tools/ardy/cclay_sequence_generate.py +0 -705
- package/tools/ardy/cclay_worker.py +0 -304
- package/tools/ardy/dump-npz.py +0 -205
- package/tools/ardy/extract-rest.mjs +0 -299
- package/tools/ardy/extract.mjs +0 -507
- package/tools/ardy/footage.mjs +0 -467
- package/tools/ardy/gvhmr-floor.mjs +0 -102
- package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
- package/tools/ardy/gvhmr_fastpath.py +0 -168
- package/tools/ardy/gvhmr_trajectory.py +0 -240
- package/tools/ardy/merge-text-encoder.py +0 -160
- package/tools/ardy/mocap-metrics.mjs +0 -200
- package/tools/ardy/mocap-quality-gate.mjs +0 -110
- package/tools/ardy/motion-stabilize.mjs +0 -348
- package/tools/ardy/npz.mjs +0 -414
- package/tools/ardy/pose-to-npz.mjs +0 -106
- package/tools/ardy/prompt-limits.mjs +0 -5
- package/tools/ardy/run-edit-on-box.sh +0 -73
- package/tools/ardy/run-local.mjs +0 -233
- package/tools/ardy/run-on-box.sh +0 -593
- package/tools/ardy/run-sequence-on-box.sh +0 -177
- package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
- package/tools/ardy/runners/index.mjs +0 -18
- package/tools/ardy/runners/local.mjs +0 -455
- package/tools/ardy/runners/proc.mjs +0 -125
- package/tools/ardy/runners/remote.mjs +0 -252
- package/tools/ardy/setup-local.mjs +0 -189
- package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
- package/tools/ardy/setup-text-encoder.py +0 -306
- package/tools/ardy/smpl-cskel27.mjs +0 -178
- package/tools/ardy/visual-qa.mjs +0 -196
- package/tools/ardy/vq-car.mjs +0 -52
- package/tools/bench/EXP3.md +0 -61
- package/tools/bench/cclay_bench_extract_incam.py +0 -125
- package/tools/bench/cclay_bench_extract_obs.py +0 -204
- package/tools/bench/cclay_bench_runner.py +0 -42
- package/tools/bench/cube-contact.mjs +0 -162
- package/tools/bench/exp3.mjs +0 -152
- package/tools/bench/extract-bench-lib.mjs +0 -216
- package/tools/bench/extract-bench.mjs +0 -302
- package/tools/bench/fal-generate.mjs +0 -88
- package/tools/bench/fit/README.md +0 -189
- package/tools/bench/fit/camera.mjs +0 -59
- package/tools/bench/fit/contact.mjs +0 -419
- package/tools/bench/fit/footlock.mjs +0 -234
- package/tools/bench/fit/motion.mjs +0 -85
- package/tools/bench/fit/pin.mjs +0 -27
- package/tools/bench/fit/remote.mjs +0 -105
- package/tools/bench/fit-bench.mjs +0 -126
- package/tools/bench/fit-sanity.mjs +0 -45
- package/tools/bench/metrics.mjs +0 -313
- package/tools/bench/obs/depth.mjs +0 -258
- package/tools/bench/obs/extrinsics.mjs +0 -175
- package/tools/bench/obs/fit_mannequin_betas.py +0 -340
- package/tools/bench/obs/ground.mjs +0 -294
- package/tools/bench/obs/heading.mjs +0 -151
- package/tools/bench/obs/ladder.mjs +0 -555
- package/tools/bench/obs/mannequin-betas.json +0 -124
- package/tools/bench/obs/remote.mjs +0 -83
- package/tools/bench/obs/rest_joints.py +0 -52
- package/tools/bench/obs/ybot-targets.mjs +0 -49
- package/tools/bench/obs-bench.mjs +0 -520
- package/tools/bench/score.mjs +0 -343
- package/tools/bench/summarize.mjs +0 -86
- package/tools/demo-worker/README.md +0 -137
- package/tools/demo-worker/api-client.mjs +0 -233
- package/tools/demo-worker/generate.mjs +0 -301
- package/tools/demo-worker/index.mjs +0 -466
- package/tools/demo-worker/sign-core.mjs +0 -112
- package/tools/demo-worker/sign.mjs +0 -161
- package/tools/gt-render/browser.mjs +0 -182
- package/tools/gt-render/camera-math.mjs +0 -276
- package/tools/gt-render/page.mjs +0 -267
- package/tools/gt-render/render.mjs +0 -514
- package/tools/gt-render/scene-box.mjs +0 -31
- package/tools/gt-render/take-transform.mjs +0 -87
- package/tools/kimodo/compare-seam.mjs +0 -108
- package/tools/kimodo/constraints.mjs +0 -244
- package/tools/kimodo/edit.mjs +0 -259
- package/tools/kimodo/effector-constraints.mjs +0 -200
- package/tools/kimodo/generate.mjs +0 -646
- package/tools/kimodo/local-output.mjs +0 -265
- package/tools/kimodo/measure-pose.mjs +0 -65
- package/tools/kimodo/measure-preserve.mjs +0 -899
- package/tools/kimodo/measure-waypoints.mjs +0 -55
- package/tools/kimodo/mlx-generate.py +0 -111
- package/tools/kimodo/pose-constraints.mjs +0 -248
- package/tools/kimodo/preserve-mask.mjs +0 -543
- package/tools/kimodo/read-npz.mjs +0 -189
- package/tools/kimodo/run-edit-on-box.mjs +0 -155
- package/tools/kimodo/run-sequence-on-box.mjs +0 -178
- package/tools/kimodo/runner.mjs +0 -284
- package/tools/kimodo/seam-batch.mjs +0 -77
- package/tools/kimodo/setup-local.mjs +0 -30
- package/tools/kimodo/setup-on-box.sh +0 -339
- package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
- package/tools/morphgs/assets/gen_truth.py +0 -39
- package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
- package/tools/morphgs/assets/playback-check.mjs +0 -30
- package/tools/morphgs/demo-gate.sh +0 -26
- package/tools/morphgs/fbx2morphgs.mjs +0 -68
- package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
- package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
- package/tools/morphgs/setup-on-cluster.sh +0 -126
- package/tools/projflow/driver.py +0 -1069
- package/tools/projflow/generate.mjs +0 -805
- package/tools/projflow/hml22-to-cskel27.mjs +0 -735
- package/tools/projflow/line-edit-job.mjs +0 -547
- package/tools/projflow/replay.mjs +0 -452
- package/tools/projflow/runner.mjs +0 -269
- package/tools/projflow/service.mjs +0 -742
- package/tools/track/DESIGN.md +0 -513
- package/tools/track/backfill-provenance.mjs +0 -116
- package/tools/track/budget.mjs +0 -8
- package/tools/track/check-rig.mjs +0 -209
- package/tools/track/diagnostics.schema.json +0 -60
- package/tools/track/export-rig.mjs +0 -256
- package/tools/track/fallback.mjs +0 -7
- package/tools/track/fk-parity-fixture.mjs +0 -161
- package/tools/track/gate.mjs +0 -349
- package/tools/track/masks.mjs +0 -318
- package/tools/track/metrics.mjs +0 -201
- package/tools/track/publish-obs.mjs +0 -108
- package/tools/track/py/check_env.py +0 -61
- package/tools/track/py/eval_lr.py +0 -229
- package/tools/track/py/lr_viterbi.py +0 -294
- package/tools/track/py/masks.py +0 -405
- package/tools/track/py/objective.py +0 -382
- package/tools/track/py/rig.py +0 -508
- package/tools/track/py/scene.py +0 -326
- package/tools/track/py/test_joint_indices.py +0 -123
- package/tools/track/py/test_lr_viterbi.py +0 -195
- package/tools/track/py/test_masks.py +0 -229
- package/tools/track/py/test_rig.py +0 -288
- package/tools/track/py/test_scene.py +0 -354
- package/tools/track/py/test_track.py +0 -328
- package/tools/track/py/track.py +0 -411
- package/tools/track/remote.mjs +0 -140
- package/tools/track/rig-dump.mjs +0 -227
- package/tools/track/run-box-tests.mjs +0 -14
- package/tools/track/run-box.mjs +0 -168
- package/tools/track/setup-box.sh +0 -59
- package/tools/track/study-2d.mjs +0 -728
- /package/src/{ardy → motion}/collision-blockers.js +0 -0
- /package/src/{ardy → motion}/ground.js +0 -0
- /package/src/{ardy → motion}/ik-drag.js +0 -0
- /package/src/{ardy → motion}/ik-key-json.js +0 -0
- /package/src/{ardy → motion}/ik.js +0 -0
- /package/src/{ardy → motion}/physics-surface.js +0 -0
- /package/src/{ardy → motion}/playback-clock.js +0 -0
- /package/src/{ardy → motion}/range-pin.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,66 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 3.0.0 - 2026-10-19
|
|
6
|
+
|
|
7
|
+
Motion is computed, not generated. The Studio agent writes a motion spec (key
|
|
8
|
+
poses, skills, walks, contacts and goals) and a deterministic compiler in the
|
|
9
|
+
browser bakes it frame by frame and measures it with QA. The agent compiles,
|
|
10
|
+
reads the numbers and fixes the spec in seconds, with no GPU host.
|
|
11
|
+
|
|
12
|
+
Breaking
|
|
13
|
+
|
|
14
|
+
- The motion-generation backend (ARDY with Kimodo) is removed, and with it
|
|
15
|
+
text-to-motion generation, AI video, video motion capture, line edit,
|
|
16
|
+
AutoPhysics, platform fit and the collision fixer. `npm run dev` starts no
|
|
17
|
+
motion bridge and `CCLAY_KIMODO_HOST` is no longer read.
|
|
18
|
+
- Motion is stored as a new take, `cozyclay.take.v3`: per-bone rotations on
|
|
19
|
+
the X Bot and Y Bot rigs, the hips' path, object tracks and the spec that
|
|
20
|
+
made it. A 2.x project still opens, but its generated (npz) motion is
|
|
21
|
+
dropped when the project is read; compile new motion for its characters.
|
|
22
|
+
|
|
23
|
+
Motion compiler
|
|
24
|
+
|
|
25
|
+
- `motion.compile`, `motion.qa`, `motion.sample`, `motion.describe` and
|
|
26
|
+
`motion.keyObject`. Specs combine keys, generators and skills (`standIdle`,
|
|
27
|
+
`walkTo`, `sitInSeat`, `exitCar`, `reachGrip`, `smokeCigarette`), with
|
|
28
|
+
two-bone IK limbs, finger presets and hand anchors on the world, the body,
|
|
29
|
+
bones, moving objects or the mouth.
|
|
30
|
+
- QA measures collisions with scene objects and the body's own skin, foot
|
|
31
|
+
slides, floating, reach, balance, elbow, knee and wrist limits and pops, and
|
|
32
|
+
names each issue's cause with a hint. Hints become patches that
|
|
33
|
+
`repair` rounds apply; `goals` (near, above, moved, still, speed, ordering)
|
|
34
|
+
state what the motion must do.
|
|
35
|
+
- Held props: grab and release, carried groups, keyed and falling objects,
|
|
36
|
+
prop kits and aim keys; the arm follows what the hand holds.
|
|
37
|
+
- The walk rolls heel to toe, sets its hips from the legs, keeps an uneven
|
|
38
|
+
natural rhythm, and scales its knees, ankles, arm swing and trunk with its
|
|
39
|
+
pace. A held pose breathes and shifts its weight.
|
|
40
|
+
- Compiles run in a Web Worker, off the editor's main thread. The Sequencer
|
|
41
|
+
cuts, retimes and trims an installed take.
|
|
42
|
+
|
|
43
|
+
Look
|
|
44
|
+
|
|
45
|
+
- Practical point and spot lights, glow, lighting looks
|
|
46
|
+
(`stage.applyLighting`), sky light with ambient occlusion and HDR
|
|
47
|
+
environment maps.
|
|
48
|
+
- Deterministic effects: smoke, exhale, steam, fire, sparks, dust, rain, snow
|
|
49
|
+
and a motion trail.
|
|
50
|
+
- Characters default to white clay; the outline ink is at half strength.
|
|
51
|
+
|
|
52
|
+
Agent and Studio
|
|
53
|
+
|
|
54
|
+
- The agent's motion drafts show while it works: as beats on the timeline,
|
|
55
|
+
as a ghost on the character and in the panel, which also shows the running
|
|
56
|
+
turn's reasoning and steps. Replies render their Markdown.
|
|
57
|
+
- Contact sheets pick beat frames, add views that show depth, and draw
|
|
58
|
+
trails and onion-skin ghosts; the viewport has ghosts too.
|
|
59
|
+
- The agent cuts for a new view and lays a camera move as one rail.
|
|
60
|
+
- Connect a model runs the agent runtime's own provider login; keys are
|
|
61
|
+
checked before they are kept. Supervisor review is off by default.
|
|
62
|
+
- Objects scale down to 1 cm, inspect reports each object's world box,
|
|
63
|
+
verify reports support gaps, and a follow dolly goes up to 40 m/s.
|
|
64
|
+
|
|
5
65
|
## 2.1.1 - 2026-10-13
|
|
6
66
|
|
|
7
67
|
- Include the sampled framing fix, embedded composition-guide controls, and
|
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ That is the whole install. Not sure yet? **[Try it in the browser first](https:/
|
|
|
42
42
|
npx cozyclay --scene city-block
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
The
|
|
45
|
+
The camera tutorial seeds a compiled walk, so you can scrub the timeline, drive the cameras and draw a dolly rail straight away. New motion is compiled in the browser from a motion spec — no GPU box or sidecar needed.
|
|
46
46
|
|
|
47
47
|
## Demo
|
|
48
48
|
|
|
@@ -57,9 +57,8 @@ https://github.com/user-attachments/assets/1d0113e5-6922-443d-affc-1bdabc666247
|
|
|
57
57
|
| **Cut and move the camera** | Add shots on the timeline, draw a dolly rail on the Top-View, set speed, height and crane, and preview the move through the shot camera. Each shot carries a **Target model** (Seedance 2.5, Kling 2, Veo 3, self-hosted MiniMax-H3) and is flagged when the cut runs past that model's limits. |
|
|
58
58
|
| **Export for AI video** | One **Export ▾** menu: a keyframe pack (first/last frame, clip, camera JSON, prompt, README) as a zip, an mp4 of the shot, depth + normal conditioning passes, a storyboard contact sheet, and an OTIO cut list. Seedance 2.5 reads the pack's greybox clip as its white-model reference video, and MiniMax H3, Wan 3.0, LTX Desktop and fal render-to-real accept the same clip. The **Shot Prompt** turns the framing into a structured prompt for the model you picked. |
|
|
59
59
|
| **Undo anything** | Every scene mutation goes through one history store: a drag, a scrub, an inspector edit, an agent's camera key is exactly one undo entry. `Esc` cancels an in-flight drag and restores the pre-drag transform. |
|
|
60
|
-
| **
|
|
61
|
-
| **
|
|
62
|
-
| **Direct it with an AI** | Open the **Agent panel** (`Cmd/Ctrl+B`), sign in with your ChatGPT account, and ask for a shot in plain language; the conversation survives reloads and you can paste a reference screenshot into it. Or connect Claude — or any MCP client — or drive the Studio from a terminal with `cclay live`. All three place the cast, frame "a low wide profile", generate multi-phase motion, and the viewport moves in front of you. See [AI control](#ai-control). |
|
|
60
|
+
| **Compile motion** | The agent writes a motion spec — key poses, hand anchors, props, walks, hinged doors — and a deterministic compiler bakes every frame on the X Bot or Y Bot rig, then QA measures collisions, foot slide, reach and the contacts the spec declares ([`docs/motion3-agent-guide.md`](docs/motion3-agent-guide.md)). A compiled take is saved as its spec and replays identically; sparse IK keys and range pins correct any frame on top. |
|
|
61
|
+
| **Direct it with an AI** | Open the **Agent panel** (`Cmd/Ctrl+B`), sign in with your ChatGPT account, and ask for a shot in plain language; the conversation survives reloads and you can paste a reference screenshot into it. Or connect Claude — or any MCP client — or drive the Studio from a terminal with `cclay live`. All three place the cast, frame "a low wide profile", compile motion, and the viewport moves in front of you. See [AI control](#ai-control). |
|
|
63
62
|
|
|
64
63
|
## Modes
|
|
65
64
|
|
|
@@ -68,9 +67,9 @@ A project is either a **Storyboard** or an **Animation**. Pick it on the start s
|
|
|
68
67
|
| | Storyboard | Animation |
|
|
69
68
|
| --- | --- | --- |
|
|
70
69
|
| **Choose it when** | You want a row of still panels: framing, who stands where, a caption and a hold per panel | You want moving shots: motion on a timeline, camera moves, takes |
|
|
71
|
-
| **Timeline** | The **Board** dock tab: one card per panel with a greybox thumbnail, caption, hold, duplicate, delete and drag-to-reorder | Shots on the timeline, with
|
|
70
|
+
| **Timeline** | The **Board** dock tab: one card per panel with a greybox thumbnail, caption, hold, duplicate, delete and drag-to-reorder | Shots on the timeline, with the compiled take |
|
|
72
71
|
| **Cast** | Rigged characters or capsule figures; each panel can keep its own placement ("Use stage placement" clears it) | Rigged characters with Pose and Motion mode; capsule figures for blocking |
|
|
73
|
-
| **Motion tools** | Hidden: no
|
|
72
|
+
| **Motion tools** | Hidden: no Motion mode (`4`) | Available |
|
|
74
73
|
| **Agent** | **Agent · Storyboard** builds one panel per request and never generates motion | The full Studio tool set |
|
|
75
74
|
| **Exports** | **Stylize** a panel through the image route, **Send to Workflow** (the stylized image or the greybox) from its card, plus the Export ▾ menu: **Contact sheet (PNG)**, **Panel pack (zip)**, **All panels (zip)**, **Animatic (mp4)**, **Cut list (OTIO)** and **Depth + normal (PNG)**; File ▸ **Export as Animation project…** turns the panels into an Animation. There is no keyframe pack or shot video in a storyboard | Export ▾: keyframe pack, mp4, depth + normal passes, contact sheet, OTIO cut list |
|
|
76
75
|
|
|
@@ -101,18 +100,7 @@ npm install
|
|
|
101
100
|
npm run dev
|
|
102
101
|
```
|
|
103
102
|
|
|
104
|
-
Open `http://127.0.0.1:5180/app/` for the Studio (the root redirects there) and `http://127.0.0.1:5180/workflow/` for the Workflow canvas. `npm run dev` starts the studio together with its local
|
|
105
|
-
|
|
106
|
-
### Motion generation
|
|
107
|
-
|
|
108
|
-
Generating new motion needs Kimodo on a machine you can reach. Point the Studio at it and install the worker once:
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
CCLAY_KIMODO_HOST=user@your-gpu-box npm run kimodo:setup
|
|
112
|
-
CCLAY_KIMODO_HOST=user@your-gpu-box npx cozyclay
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
The installer detects OS, architecture, RAM and CUDA and picks the best-supported backend for that host — kimodo-mlx on a big Apple Silicon machine, kimodo.cpp with Metal or CPU otherwise, the upstream PyTorch stack on NVIDIA. An SSH-accessible NVIDIA box is the classic target and the only route with the full feature set; the same box runs GVHMR for video and photo mocap. Routes, overrides and local-only limits are in [`docs/kimodo-setup.md`](docs/kimodo-setup.md).
|
|
103
|
+
Open `http://127.0.0.1:5180/app/` for the Studio (the root redirects there) and `http://127.0.0.1:5180/workflow/` for the Workflow canvas. `npm run dev` starts the studio together with its local OAuth and agent sidecar; `npm run dev:ui` starts the browser UI alone.
|
|
116
104
|
|
|
117
105
|
## AI control
|
|
118
106
|
|
|
@@ -153,8 +141,8 @@ The studio ships an [MCP](https://modelcontextprotocol.io) server, so any MCP cl
|
|
|
153
141
|
|
|
154
142
|
Drop that into `claude_desktop_config.json` (or any MCP client config) and restart the client. The first run automatically installs the MCP SDK's 95-package tree; opening the studio never waits on it, so those dependencies are fetched only when you actually want the server.
|
|
155
143
|
|
|
156
|
-
- **Editor open?** Tool calls move the visible viewport — camera, cast, set,
|
|
157
|
-
- **No editor?** Scene and project tools run headless: block scenes, derive film vocabulary ("wide shot · right profile · knee level · 24mm"), render AI video prompts, and write `.cclayproject` files. `capture_frame
|
|
144
|
+
- **Editor open?** Tool calls move the visible viewport — camera, cast, set, compiled motion on the timeline.
|
|
145
|
+
- **No editor?** Scene and project tools run headless: block scenes, derive film vocabulary ("wide shot · right profile · knee level · 24mm"), render AI video prompts, and write `.cclayproject` files. `capture_frame` and `apply_batch` require the live editor.
|
|
158
146
|
|
|
159
147
|
Tools, transports and the live-control protocol are documented in [`mcp/README.md`](mcp/README.md).
|
|
160
148
|
|
|
@@ -195,13 +183,13 @@ Every control's home is recorded in [`docs/studio-ui-ia.md`](docs/studio-ui-ia.m
|
|
|
195
183
|
|
|
196
184
|
| Read | For |
|
|
197
185
|
| --- | --- |
|
|
198
|
-
| [`docs/
|
|
186
|
+
| [`docs/motion3-agent-guide.md`](docs/motion3-agent-guide.md) | How the agent writes a motion spec, compiles it and reads QA |
|
|
199
187
|
| [`docs/agent-cli.md`](docs/agent-cli.md) | Driving the Studio from a terminal with `cclay live` |
|
|
200
188
|
| [`docs/agent-prompt-to-scene.md`](docs/agent-prompt-to-scene.md) | The ask / infer / omit rule the Agent follows when turning a video prompt into blocking, and the one-panel-per-request rule in storyboards |
|
|
201
189
|
| [`mcp/README.md`](mcp/README.md) | MCP tools, transports, and the live-control protocol |
|
|
202
190
|
| [`docs/studio-ui-ia.md`](docs/studio-ui-ia.md) | Where every Studio control lives and why |
|
|
203
191
|
| [`docs/privacy.md`](docs/privacy.md) | The full analytics disclosure: events, identifiers, opt-out, internal QA marking |
|
|
204
|
-
| [`workers/api/README.md`](workers/api/README.md) | The
|
|
192
|
+
| [`workers/api/README.md`](workers/api/README.md) | The cozyclay.org API Worker: sign-in and the hosted video-model route |
|
|
205
193
|
| [`docs/releasing.md`](docs/releasing.md) | Publishing a signed npm release |
|
|
206
194
|
| [`CHANGELOG.md`](CHANGELOG.md) | What changed in each release |
|
|
207
195
|
|
|
@@ -217,7 +205,7 @@ Longer reads on the site: [Greybox to AI video](https://cozyclay.org/greybox-to-
|
|
|
217
205
|
| `npm run test:objects` | Gizmo interaction in a real browser — needs `npm run dev:ui` in another shell |
|
|
218
206
|
| `npm run test:theme` / `test:appearance` / `test:layout` | UI theme, appearance, layout |
|
|
219
207
|
| `npm run test:lifecycle` | Dev-server process lifecycle |
|
|
220
|
-
| `npm run test:ardy` |
|
|
208
|
+
| `npm run test:ardy` | IK pipeline, range pins and timeline helpers |
|
|
221
209
|
| `node tools/run-tests.mjs` | Every Node verification file (what CI runs) |
|
|
222
210
|
| `npm run qa:browser -- node test/qa-camera-tutorial-browser.mjs` | The seven-step tutorial driven with real input, beacons pinned to their controls |
|
|
223
211
|
| `QA_URL=… CDP_PORT=… OUT=/tmp/count npm run qa:browser -- node tools/qa/studio-control-count.mjs` | Simultaneously visible controls per mode (budget: Scene ≤35 / Camera ≤38 / Motion ≤52) |
|
|
@@ -235,7 +223,7 @@ npm run qa:browser -- node <qa-script>
|
|
|
235
223
|
|
|
236
224
|
Found something broken, or want a feature? [Open an issue](https://github.com/NomaDamas/CozyClay/issues) — bug reports with a repro are the most useful thing you can send. Contributions are accepted under `AGPL-3.0-or-later`.
|
|
237
225
|
|
|
238
|
-
**Repository hygiene.** Generated motion archives, QA output, build output, logs and local runtime artifacts are not source files and must not be committed. Keep `
|
|
226
|
+
**Repository hygiene.** Generated motion archives, QA output, build output, logs and local runtime artifacts are not source files and must not be committed. Keep `artifacts/`, `dist/`, `.gjc/` and `.npz` files local.
|
|
239
227
|
|
|
240
228
|
Every runtime library except the two agent packages (`@earendil-works/pi-ai`, `@earendil-works/pi-agent-core`) intentionally lives in `devDependencies`, because the published npm package ships the prebuilt `dist/`, so `npx cozyclay` must not install the studio's dependency tree.
|
|
241
229
|
|
|
@@ -250,12 +238,6 @@ cclay telemetry off
|
|
|
250
238
|
|
|
251
239
|
`COZYCLAY_TELEMETRY=0` or `DO_NOT_TRACK=1` disables collection for a launch; **Settings ▾ → Privacy** is the same switch inside the Studio. The event list, what each identifier is, how internal QA traffic is marked and the first-edit definition are in [`docs/privacy.md`](docs/privacy.md).
|
|
252
240
|
|
|
253
|
-
## Hosted demo
|
|
254
|
-
|
|
255
|
-
Installing a GPU motion backend is the hard part, so `cozyclay.org` also runs a queued demo: a visitor writes one prompt, gets a ticket link, and a GPU box owned by the maintainer generates the motion and uploads it — the result opens in the studio itself. The composer pages, the Cloudflare Worker queue API and the GPU-box poller all live in this repository; the queue policy and runbooks are in [`workers/api/README.md`](workers/api/README.md).
|
|
256
|
-
|
|
257
241
|
## License & credits
|
|
258
242
|
|
|
259
243
|
GNU Affero General Public License v3.0 or later — see [`LICENSE`](LICENSE) and the transition details in [`LICENSING.md`](LICENSING.md). Modified network services must offer their users the corresponding source. Third-party projects retain their own licenses and copyright; see [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md).
|
|
260
|
-
|
|
261
|
-
The hosted demo worker may use an externally installed [NVIDIA ARDY](https://github.com/nv-tlabs/ardy) runtime. ARDY is a separate third-party project owned and maintained by NVIDIA; it is not included in this repository, and CozyClay is not affiliated with or endorsed by NVIDIA. The local Studio uses Kimodo instead.
|
|
@@ -10,6 +10,7 @@ import { createAgentTools, SYSTEM_PROMPT, pickWorkspace } from "./agent-tools.mj
|
|
|
10
10
|
import { createVideoAdapters, validateFalVideoRequest } from "./video-adapters.mjs";
|
|
11
11
|
import { createSessionStore, transcriptFromHistory } from "./session-store.mjs";
|
|
12
12
|
import { createAgentRunner } from "./agent-runner.mjs";
|
|
13
|
+
import { createProviderRoutes } from "./provider-routes.mjs";
|
|
13
14
|
import { STUDIO_SYSTEM_PROMPT_STORYBOARD } from "./studio-prompt.mjs";
|
|
14
15
|
|
|
15
16
|
// Values the codex backend accepts for reasoning.effort (its own 400 lists them).
|
|
@@ -85,9 +86,9 @@ const agentFailureCode = (error, signal, tool = false) => advisory(() => {
|
|
|
85
86
|
* One user message is one editor transaction: every Studio turn whose editor
|
|
86
87
|
* reports a previsMode opens agent.turn.begin and closes agent.turn.finish, so
|
|
87
88
|
* all of its mutations land as ONE history entry and one receipt frame. A
|
|
88
|
-
* context without a previsMode
|
|
89
|
-
* transactions
|
|
90
|
-
*
|
|
89
|
+
* context without a previsMode runs unwrapped (an editor that predates turn
|
|
90
|
+
* transactions). mark() records where a script starts; rollback() undoes only
|
|
91
|
+
* the edits after it and keeps the turn open, so earlier edits still land.
|
|
91
92
|
*/
|
|
92
93
|
const agentTurn = ({ hub, context, turnId, admission }) => {
|
|
93
94
|
const wrapped = Boolean(context.scene?.previsMode);
|
|
@@ -102,12 +103,11 @@ const agentTurn = ({ hub, context, turnId, admission }) => {
|
|
|
102
103
|
};
|
|
103
104
|
const open = async () => { await control("agent.turn.begin"); admission.turnId = turnId; };
|
|
104
105
|
return {
|
|
106
|
+
mark: async () => (admission.turnId === turnId ? control("agent.turn.mark") : null),
|
|
105
107
|
async rollback() {
|
|
106
108
|
if (admission.turnId !== turnId) return null;
|
|
107
|
-
|
|
108
|
-
const receipt = await control("agent.turn.cancel");
|
|
109
|
+
const receipt = await control("agent.turn.rollback");
|
|
109
110
|
await admission.refresh();
|
|
110
|
-
await open();
|
|
111
111
|
return receipt;
|
|
112
112
|
},
|
|
113
113
|
run: start => async function* (input) {
|
|
@@ -119,7 +119,7 @@ const agentTurn = ({ hub, context, turnId, admission }) => {
|
|
|
119
119
|
if (frame.type === "done") terminal = frame; else yield frame;
|
|
120
120
|
}
|
|
121
121
|
} finally {
|
|
122
|
-
//
|
|
122
|
+
// Finish only the transaction this run opened (a rollback keeps it open).
|
|
123
123
|
if (admission.turnId === turnId) {
|
|
124
124
|
delete admission.turnId;
|
|
125
125
|
const receipt = await control("agent.turn.finish");
|
|
@@ -381,9 +381,10 @@ export function createAgentHandler({ auth = defaultAuth, codex, models, codexBas
|
|
|
381
381
|
try { if (await auth.getAccessToken()) return true; } catch { /* an unavailable ChatGPT token is not a credential */ }
|
|
382
382
|
const registry = await ensureWorkflowModels();
|
|
383
383
|
if (typeof registry?.getAuth !== "function") return false;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
384
|
+
// the curated providers and any other one a pi login stored a credential for (#pi-login)
|
|
385
|
+
const [{ PROVIDERS }, { readCredentials }] = await Promise.all([import("./providers.mjs"), import("./provider-keys.mjs")]);
|
|
386
|
+
for (const id of new Set([...PROVIDERS.map((provider) => provider.id), ...Object.keys(readCredentials())])) {
|
|
387
|
+
try { if (await registry.getAuth(id)) return true; } catch { /* one broken provider does not break the gate */ }
|
|
387
388
|
}
|
|
388
389
|
return false;
|
|
389
390
|
};
|
|
@@ -395,9 +396,9 @@ export function createAgentHandler({ auth = defaultAuth, codex, models, codexBas
|
|
|
395
396
|
const studioOwnerTokens = new Map();
|
|
396
397
|
const parseCookies = req => Object.fromEntries(String(req.headers.cookie || "").split(";").map(part => part.trim().split("=")).filter(([key, value]) => key && value).map(([key, value]) => [key, decodeURIComponent(value)]));
|
|
397
398
|
const pruneStudioSessions = () => {
|
|
398
|
-
const now = clock();
|
|
399
|
-
for (const [id, session] of studioSessions) if (
|
|
400
|
-
const retired = [...studioSessions.entries()].filter(([, session]) =>
|
|
399
|
+
const now = clock(), idle = session => !(session.activeTurnId && session.controller && !session.controller.signal.aborted); // a running turn keeps its session
|
|
400
|
+
for (const [id, session] of studioSessions) if (idle(session) && now - session.updatedAt > 600_000) { studioSessions.delete(id); studioOwnerTokens.delete(id); for (const turn of session.turns.keys()) studioEvents.delete(turn); }
|
|
401
|
+
const retired = [...studioSessions.entries()].filter(([, session]) => idle(session)).sort((a, b) => a[1].updatedAt - b[1].updatedAt);
|
|
401
402
|
while (studioSessions.size > 256 && retired.length) { const [id, session] = retired.shift(); studioSessions.delete(id); studioOwnerTokens.delete(id); for (const turn of session.turns.keys()) studioEvents.delete(turn); }
|
|
402
403
|
};
|
|
403
404
|
const studioOwner = (req, sessionId, create = false) => {
|
|
@@ -425,13 +426,16 @@ export function createAgentHandler({ auth = defaultAuth, codex, models, codexBas
|
|
|
425
426
|
record.next = value.eventSeq;
|
|
426
427
|
const previous = record.events.at(-1);
|
|
427
428
|
if (value.type === "job.progress" && previous?.type === "job.progress" && previous.jobId === value.jobId) record.events[record.events.length - 1] = value;
|
|
429
|
+
// The panel's live frames (turn-frames.mjs, a script's nested calls) keep one replay slot per run of them.
|
|
430
|
+
else if (value.type === "thinking.delta" && previous?.type === "thinking.delta") record.events[record.events.length - 1] = { ...value, text: previous.text + value.text };
|
|
431
|
+
else if (value.type === "tool.progress" && previous?.type === "tool.progress" && previous.callId === value.callId) record.events[record.events.length - 1] = value;
|
|
428
432
|
else record.events.push(value);
|
|
429
433
|
if (record.events.length > 256) record.events.splice(0, record.events.length - 256);
|
|
430
434
|
if (["done", "error", "receipt"].includes(value.type)) record.terminal = true;
|
|
431
435
|
studioEvents.set(turnId, record); for (const listener of [...record.listeners]) listener(value);
|
|
432
436
|
};
|
|
433
|
-
|
|
434
|
-
|
|
437
|
+
// A sign-out, another account, or a provider key or server address that changed: the registry and every runner on it go.
|
|
438
|
+
const resetRegistry = () => {
|
|
435
439
|
for (const session of sessions.values()) session.controller?.abort();
|
|
436
440
|
for (const session of studioSessions.values()) session.controller?.abort();
|
|
437
441
|
for (const runner of [...workflowRunners.values(), ...studioRunners.values()]) void runner.close?.();
|
|
@@ -439,7 +443,9 @@ export function createAgentHandler({ auth = defaultAuth, codex, models, codexBas
|
|
|
439
443
|
workflowModels = models;
|
|
440
444
|
workflowModelsPromise = models ? Promise.resolve(models) : null;
|
|
441
445
|
sessions.clear(); studioSessions.clear(); studioEvents.clear(); studioOwnerTokens.clear();
|
|
442
|
-
}
|
|
446
|
+
};
|
|
447
|
+
const unsubscribe = auth.onAuthChange?.(({ kind }) => { if (kind === "signed_out" || kind === "replaced") resetRegistry(); });
|
|
448
|
+
const providerRoutes = createProviderRoutes({ auth, getModels: ensureWorkflowModels, onChange: resetRegistry, json, readBody });
|
|
443
449
|
|
|
444
450
|
const studioIdentity = host => Object.fromEntries(["workspaceId", "documentEpoch", "sceneId", "sceneEpoch"].map(key => [key, host[key]]));
|
|
445
451
|
const writeStudioStream = (res, record, after = 0) => {
|
|
@@ -473,27 +479,12 @@ export function createAgentHandler({ auth = defaultAuth, codex, models, codexBas
|
|
|
473
479
|
if (path === "/agent/stop") {
|
|
474
480
|
const session = studioSessions.get(value.sessionId);
|
|
475
481
|
if (!session || session.owner !== parseCookies(req).studio_owner || !session.turns.has(value.turnId)) throw new StudioProtocolError("AUTH_REQUIRED", "Studio stop is not owned by this session.");
|
|
476
|
-
|
|
477
|
-
//
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
// this Stop cannot acknowledge the current turn's different job.
|
|
483
|
-
if (value.jobId && session.activeJobId && value.jobId !== session.activeJobId) throw new StudioProtocolError("STALE_TARGET", "Stop does not target the active motion job.");
|
|
484
|
-
// Only this turn's held editor job can settle the tool quietly. A retired
|
|
485
|
-
// job from an earlier turn must never silence an unrelated turn's Stop.
|
|
486
|
-
const acknowledged = Boolean(session.activeJobId) && session.activeJobTurnId === value.turnId;
|
|
487
|
-
// The editor's outcome, not a sidecar abort, establishes application.
|
|
488
|
-
let outcome = null;
|
|
489
|
-
if (jobId) outcome = await session.controlJob('job.cancel', jobId);
|
|
490
|
-
if (jobId && session.activeJobId === jobId) { session.activeJobId = null; session.activeJobTurnId = null; }
|
|
491
|
-
// Let the acknowledged job settle its held tool with that outcome before
|
|
492
|
-
// aborting the model lane. Aborting the shared signal would interrupt the
|
|
493
|
-
// tool first and lose the editor's proof. Without a held job, abort both.
|
|
494
|
-
if (!acknowledged) session.controller?.abort();
|
|
495
|
-
await session.modelSession?.abort?.("studio stop", acknowledged ? { quiet: true } : undefined);
|
|
496
|
-
json(res, 200, { ok: true, status: jobId ? "stopped" : "detached", ...(outcome ? { outcome: { status: outcome.status ?? null, code: outcome.code ?? null, mutated: outcome.mutated ?? null } } : {}) }); return true;
|
|
482
|
+
// Studio turns hold no editor jobs since 3.0 (motion.compile answers
|
|
483
|
+
// synchronously), so a Stop names no job: it aborts the turn and the model lane.
|
|
484
|
+
if (value.jobId) throw new StudioProtocolError("STALE_TARGET", "Stop does not own that motion job.");
|
|
485
|
+
session.controller?.abort();
|
|
486
|
+
await session.modelSession?.abort?.("studio stop");
|
|
487
|
+
json(res, 200, { ok: true, status: "detached" }); return true;
|
|
497
488
|
}
|
|
498
489
|
if (!studioRuntime && !hub && hubDeps.hubPortBusy) throw new StudioProtocolError("CAPABILITY_MISSING", `The Studio live hub could not start: port ${hubDeps.hubPortBusy} is in use by another CozyClay or its dev server. Free that port and relaunch.`);
|
|
499
490
|
if (!studioRuntime && (!hub?.command || !hub?.workspaceId)) throw new StudioProtocolError("CAPABILITY_MISSING", "Studio execution is not installed.");
|
|
@@ -516,7 +507,7 @@ export function createAgentHandler({ auth = defaultAuth, codex, models, codexBas
|
|
|
516
507
|
studioOwner(req, value.sessionId, true);
|
|
517
508
|
let persisted = null;
|
|
518
509
|
try { persisted = sessionStore.read(value.sessionId); } catch { /* an unreadable history starts the session fresh */ }
|
|
519
|
-
session = { owner: studioOwnerTokens.get(value.sessionId), history: persisted?.history ?? [], persistedItems: persisted?.history?.length ?? 0, meta: persisted?.meta ?? null, turns: new Map(), controller: null,
|
|
510
|
+
session = { owner: studioOwnerTokens.get(value.sessionId), history: persisted?.history ?? [], persistedItems: persisted?.history?.length ?? 0, meta: persisted?.meta ?? null, turns: new Map(), controller: null, host: null, updatedAt: clock() };
|
|
520
511
|
studioSessions.set(value.sessionId, session);
|
|
521
512
|
}
|
|
522
513
|
session.updatedAt = clock();
|
|
@@ -530,7 +521,7 @@ export function createAgentHandler({ auth = defaultAuth, codex, models, codexBas
|
|
|
530
521
|
let replySegment = "", lastReply = "";
|
|
531
522
|
const send = event => {
|
|
532
523
|
if (event.type === "text.delta") { replySegment += event.text ?? ""; lastReply = replySegment; }
|
|
533
|
-
else if (!["execution_telemetry", "execution_tool_started"].includes(event.type)) {
|
|
524
|
+
else if (!["execution_telemetry", "execution_tool_started", "thinking.delta", "tool.compose", "tool.progress"].includes(event.type)) {
|
|
534
525
|
if (event.type === "tool.start") replySegment = "";
|
|
535
526
|
turnFrames.push(event);
|
|
536
527
|
}
|
|
@@ -562,49 +553,11 @@ export function createAgentHandler({ auth = defaultAuth, codex, models, codexBas
|
|
|
562
553
|
return refreshed;
|
|
563
554
|
},
|
|
564
555
|
};
|
|
565
|
-
|
|
566
|
-
// generate_motion below and a run_action job action share this gate.
|
|
567
|
-
const generation = { used: false, failures: 0 };
|
|
568
|
-
// The editor's command index (authoritative context) declares each action's
|
|
569
|
-
// generation and hub timeout; no sidecar list of actions exists.
|
|
570
|
-
const controlJob = async (action, jobId) => {
|
|
571
|
-
await admission.refresh();
|
|
572
|
-
return hub.command('run_action', { name: 'run_action', args: { action, args: { jobId, ...(action === 'job.await' ? { timeoutMs: 300_000 } : {}) } },
|
|
573
|
-
commandId: randomUUID(), host: admission.host, expectedRevision: admission.revision }, value.context.host.workspaceHandle, { timeoutMs: 300_000 });
|
|
574
|
-
};
|
|
575
|
-
session.controlJob = controlJob;
|
|
576
|
-
const onJob = async started => {
|
|
577
|
-
const jobId = started.jobId;
|
|
578
|
-
session.motionJobIds.add(jobId);
|
|
579
|
-
persistenceMeta.motionJobIds = [...session.motionJobIds];
|
|
580
|
-
session.activeJobId = jobId; session.activeJobTurnId = value.turnId;
|
|
581
|
-
const cancel = () => { void controlJob('job.cancel', jobId).catch(error => send({ type: 'error', code: error.code ?? 'CANCEL_FAILED', message: error.message })); };
|
|
582
|
-
controller.signal.addEventListener('abort', cancel, { once: true });
|
|
583
|
-
if (controller.signal.aborted) cancel();
|
|
584
|
-
send({ type: 'job.state', jobId, commandId: started.commandId, state: 'generating', phase: 'generating' });
|
|
585
|
-
try {
|
|
586
|
-
const outcome = await controlJob('job.await', jobId);
|
|
587
|
-
send({ type: 'job.state', jobId, commandId: outcome.commandId, state: outcome.ok ? 'installed' : outcome.code === 'CANCELLED' ? 'cancelled' : 'failed', phase: 'complete' });
|
|
588
|
-
return outcome;
|
|
589
|
-
} finally {
|
|
590
|
-
controller.signal.removeEventListener('abort', cancel);
|
|
591
|
-
if (session.activeJobId === jobId) { session.activeJobId = null; session.activeJobTurnId = null; }
|
|
592
|
-
}
|
|
593
|
-
};
|
|
594
|
-
const tools = createStudioTools({ liveHub: hub, workspaceHandle: value.context.host.workspaceHandle, previsMode: value.context.scene?.previsMode, session: { signal: controller.signal, admission, generation, onJob, actionIndex: current?.actionIndex ?? [], baseline: value.context }, resolveImage: async (id, correlation) => hub.command("resolve_studio_image", { imageId: id, ...correlation }, value.context.host.workspaceHandle) });
|
|
595
|
-
const motion = async args => {
|
|
596
|
-
// Private artifact IDs belonged to the retired sidecar runtime. Reuse
|
|
597
|
-
// retained editor takes through motion.loadVersion, not another installer.
|
|
598
|
-
if (args.source.kind !== 'generate') throw new StudioProtocolError('CAPABILITY_MISSING', 'Private motion artifacts are no longer retained. Use motion.loadVersion for an editor take.');
|
|
599
|
-
await tools.internal.invoke('inspect_studio', { scope: 'motion', ids: [args.characterId] });
|
|
600
|
-
const receipt = await tools.internal.invoke('generate_motion', args);
|
|
601
|
-
if (receipt.ok && receipt.status === 'completed') send({ type: 'receipt', receipt });
|
|
602
|
-
return receipt;
|
|
603
|
-
};
|
|
556
|
+
const tools = createStudioTools({ liveHub: hub, workspaceHandle: value.context.host.workspaceHandle, previsMode: value.context.scene?.previsMode, session: { signal: controller.signal, admission, actionIndex: current?.actionIndex ?? [], baseline: value.context }, resolveImage: async (id, correlation) => hub.command("resolve_studio_image", { imageId: id, ...correlation }, value.context.host.workspaceHandle) });
|
|
604
557
|
const modelTools = tools.map(tool => ({
|
|
605
558
|
...tool,
|
|
606
559
|
handler: async args => {
|
|
607
|
-
const result = await
|
|
560
|
+
const result = await tool.handler(args);
|
|
608
561
|
if (!result || !Array.isArray(result.visualRefs) || !result.visualRefs.length) return result;
|
|
609
562
|
const ref = result.visualRefs.find(item => item?.imageId || item?.id);
|
|
610
563
|
if (!ref) return result;
|
|
@@ -616,9 +569,11 @@ export function createAgentHandler({ auth = defaultAuth, codex, models, codexBas
|
|
|
616
569
|
const script = createScriptTool({ tools: modelTools, signal: controller.signal, emit: send });
|
|
617
570
|
const scriptTool = {
|
|
618
571
|
...script,
|
|
619
|
-
// A script that fails after it edited the scene rolls
|
|
620
|
-
// the model retries from the
|
|
572
|
+
// A script that fails after it edited the scene rolls back its own edits, so
|
|
573
|
+
// the model retries from the document the script started from, never from
|
|
574
|
+
// half a script; what the turn did before the script stays.
|
|
621
575
|
handler: async (args, options) => {
|
|
576
|
+
await turnTransaction.mark();
|
|
622
577
|
const result = await script.handler(args, options);
|
|
623
578
|
if (result.ok || !result.calls.some(call => call.ok && Number.isSafeInteger(call.revision?.after) && call.revision.after !== call.revision.before)) return result;
|
|
624
579
|
const rollback = await turnTransaction.rollback();
|
|
@@ -675,12 +630,12 @@ export function createAgentHandler({ auth = defaultAuth, codex, models, codexBas
|
|
|
675
630
|
else telemetry.finished(turnOutcome, turnFailureCode || "tool_failed");
|
|
676
631
|
// Marked before the done frame leaves, so a client that asks for the
|
|
677
632
|
// review the moment its stream ends finds it pending, not absent.
|
|
678
|
-
//
|
|
679
|
-
if (supervisor && process.env.COZYCLAY_SUPERVISOR !== "off" && value.supervisor
|
|
633
|
+
// Off by default: only a turn sent with value.supervisor === true is reviewed.
|
|
634
|
+
if (supervisor && process.env.COZYCLAY_SUPERVISOR !== "off" && value.supervisor === true && !controller.signal.aborted && shouldSupervise(turnFrames)) record.supervisor = "pending";
|
|
680
635
|
}
|
|
681
636
|
send(frame);
|
|
682
637
|
};
|
|
683
|
-
const persistenceMeta = { sceneName: value.context?.scene?.name ?? value.context?.sceneName ?? null, firstText: value.text
|
|
638
|
+
const persistenceMeta = { sceneName: value.context?.scene?.name ?? value.context?.sceneName ?? null, firstText: value.text };
|
|
684
639
|
const modelKey = value.model || (fauxProvider ? `${fauxProvider.provider?.id || fauxProvider.provider || "faux"}/scripted` : "gpt-6-astra");
|
|
685
640
|
let runner = studioRunners.get(value.sessionId);
|
|
686
641
|
if (!runner) {
|
|
@@ -789,35 +744,7 @@ export function createAgentHandler({ auth = defaultAuth, codex, models, codexBas
|
|
|
789
744
|
await session.modelSession.steer(value.text, images);
|
|
790
745
|
json(res, 200, { ok: true, queued: true }); return true;
|
|
791
746
|
}
|
|
792
|
-
if (
|
|
793
|
-
const { providerStatus } = await import("./providers.mjs");
|
|
794
|
-
try { json(res, 200, { providers: providerStatus({ auth }) }); }
|
|
795
|
-
catch { json(res, 500, { error: "provider credentials unavailable" }); }
|
|
796
|
-
return true;
|
|
797
|
-
}
|
|
798
|
-
if (/^\/agent\/providers\/[^/]+$/.test(path) && ["PUT", "DELETE"].includes(req.method)) {
|
|
799
|
-
const { PROVIDERS } = await import("./providers.mjs");
|
|
800
|
-
const keys = await import("./provider-keys.mjs");
|
|
801
|
-
const { createCredentialStore } = await import("./credential-store.mjs");
|
|
802
|
-
const id = path.slice("/agent/providers/".length);
|
|
803
|
-
if (!PROVIDERS.some((provider) => provider.id === id)) { json(res, 404, { error: "provider not found" }); return true; }
|
|
804
|
-
const credentials = createCredentialStore({ auth, keys });
|
|
805
|
-
let key;
|
|
806
|
-
if (req.method === "PUT") {
|
|
807
|
-
if (id === "openai-codex") { json(res, 400, { error: "Use ChatGPT sign-in for OpenAI Codex." }); return true; }
|
|
808
|
-
try {
|
|
809
|
-
const value = await readBody(req);
|
|
810
|
-
if (typeof value?.key !== "string" || !value.key.trim()) throw new Error("Invalid key.");
|
|
811
|
-
key = value.key.trim();
|
|
812
|
-
} catch { json(res, 400, { error: "a non-empty API key is required" }); return true; }
|
|
813
|
-
}
|
|
814
|
-
try {
|
|
815
|
-
if (req.method === "PUT") await credentials.modify(id, async () => ({ type: "api_key", key }));
|
|
816
|
-
else await credentials.delete(id);
|
|
817
|
-
json(res, 200, { ok: true });
|
|
818
|
-
} catch { json(res, 500, { error: "provider credentials could not be saved" }); }
|
|
819
|
-
return true;
|
|
820
|
-
}
|
|
747
|
+
if (await providerRoutes(req, res, path)) return true;
|
|
821
748
|
if (path === "/agent/sessions" && req.method === "GET") {
|
|
822
749
|
const surface = new URL(req.url, "http://127.0.0.1").searchParams.get("surface") || undefined;
|
|
823
750
|
try { json(res, 200, { sessions: sessionStore.list({ surface }) }); }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { summariseCanvasResult } from "./agent-tools.mjs";
|
|
2
|
+
import { createReasoningFrames } from "./turn-frames.mjs";
|
|
2
3
|
import { sanitizeUpstreamDetail } from "./agent-routes.mjs";
|
|
3
4
|
|
|
4
5
|
const DEFAULT_MODEL = "gpt-6-astra";
|
|
@@ -363,7 +364,7 @@ export function createAgentRunner({ models: suppliedModels, sessionStore, tools
|
|
|
363
364
|
|
|
364
365
|
const ensureHarness = async (input) => {
|
|
365
366
|
const { pi, models: registry } = await ensureModels();
|
|
366
|
-
const activePrompt = input.systemPrompt || (input.surface === "studio" ? (await import("./studio-
|
|
367
|
+
const activePrompt = input.systemPrompt || (input.surface === "studio" ? await (await import("./studio-motion-reference.mjs")).studioSystemPrompt() : systemPrompt);
|
|
367
368
|
const { resolveModel, resolveEffort } = await import("./providers.mjs");
|
|
368
369
|
const requested = input.model || DEFAULT_MODEL;
|
|
369
370
|
const slash = requested.indexOf("/");
|
|
@@ -449,27 +450,25 @@ export function createAgentRunner({ models: suppliedModels, sessionStore, tools
|
|
|
449
450
|
queue.push(frame);
|
|
450
451
|
};
|
|
451
452
|
const subscribe = (type, listener) => eventUnsubscribers.push(state.harness.events.on(type, listener));
|
|
453
|
+
// The reasoning summary and the tool call being written, for the panel (turn-frames.mjs).
|
|
454
|
+
const reasoning = createReasoningFrames(pushFrame, clock);
|
|
452
455
|
subscribe("message_update", (event) => {
|
|
456
|
+
reasoning.onMessageEvent(event.event);
|
|
453
457
|
if (event.event?.type === "text_delta") pushFrame({ type: "text.delta", text: event.event.delta });
|
|
454
458
|
if (event.event?.type === "error") pushFrame(errorFrame(event.event.error, event.event.reason === "aborted"));
|
|
455
459
|
});
|
|
456
|
-
|
|
460
|
+
const startTool = (event) => {
|
|
457
461
|
if (toolStarted.has(event.toolCallId)) return;
|
|
458
462
|
toolStarted.add(event.toolCallId);
|
|
459
463
|
toolStartedAt.set(event.toolCallId, clock());
|
|
460
464
|
const activeTools = state.lastInput?.tools || tools;
|
|
461
465
|
const tool = (Array.isArray(activeTools) ? activeTools : []).find((candidate) => candidate.name === event.toolName);
|
|
462
466
|
pushFrame({ type: "tool.start", callId: event.toolCallId, name: event.toolName, label: tool?.label || event.toolName.replaceAll("_", " "), args: summariseCanvasResult(event.args) });
|
|
463
|
-
}
|
|
467
|
+
};
|
|
468
|
+
subscribe("tool_start", startTool);
|
|
464
469
|
subscribe("tool_end", (event) => {
|
|
465
470
|
if (toolCompleted.has(event.toolCallId)) return;
|
|
466
|
-
|
|
467
|
-
toolStarted.add(event.toolCallId);
|
|
468
|
-
toolStartedAt.set(event.toolCallId, clock());
|
|
469
|
-
const activeTools = state.lastInput?.tools || tools;
|
|
470
|
-
const tool = (Array.isArray(activeTools) ? activeTools : []).find((candidate) => candidate.name === event.toolName);
|
|
471
|
-
pushFrame({ type: "tool.start", callId: event.toolCallId, name: event.toolName, label: tool?.label || event.toolName.replaceAll("_", " "), args: summariseCanvasResult(event.args) });
|
|
472
|
-
}
|
|
471
|
+
startTool(event);
|
|
473
472
|
toolCompleted.add(event.toolCallId);
|
|
474
473
|
const started = toolStartedAt.get(event.toolCallId) ?? clock();
|
|
475
474
|
const elapsedMs = Math.round(clock() - started);
|