cozyclay 2.1.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -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 +22 -6
- 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-BlT33CGf.js → first-shot-handoff-CC4xe4HM.js} +1 -1
- package/dist/assets/{landing-BSesrCpL.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/llms.txt +14 -0
- 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 +174 -2639
- 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 +48 -419
- 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/PoseImagePanel.jsx +1 -1
- 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 +11 -53
- package/src/shell/CompositionGuideButton.jsx +20 -0
- 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/ShotPreviewHeader.jsx +17 -0
- package/src/shell/TopBar.jsx +5 -83
- package/src/shell/ViewportToolbar.jsx +23 -93
- 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/shell/viewport.css +12 -0
- 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 +46 -16
- package/src/studio-agent-motion.js +9 -9
- package/src/studio-agent-protocol.js +46 -73
- package/src/studio-app-binding.js +29 -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/headless-studio.mjs +85 -0
- package/tools/knip-baseline.json +0 -179
- package/tools/loc-baseline.json +33 -83
- 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 +9 -124
- package/bin/agent/motion-runtime.mjs +0 -76
- package/dist/ardy/cskel27-rest.json +0 -557
- package/dist/assets/analytics-B41FmPYI.js +0 -1
- package/dist/assets/app-Do-Og5Pu.css +0 -1
- package/dist/assets/app-DwP-tl2x.js +0 -4920
- package/dist/assets/config-Bx7pYsD9.js +0 -1
- package/dist/assets/config-DeoFTYzb.css +0 -1
- package/dist/assets/demo-Dt0qZfS7.js +0 -1
- package/dist/assets/scene-asset-sync-BCWDJB1d.js +0 -19
- package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
- package/dist/assets/ticket-jqMhqL88.js +0 -1
- package/dist/assets/workflow-Br-K7Srs.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 -159
- 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
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
2
|
-
import { FiAlertTriangle, FiArrowUp, FiCheck, FiChevronRight, FiClock, FiDownload,
|
|
3
|
-
import {
|
|
4
|
-
ATTACHMENT_FAILED_NOTICE,
|
|
5
|
-
ATTACHMENT_LIMIT_NOTICE,
|
|
6
|
-
ATTACHMENT_TYPE_NOTICE,
|
|
7
|
-
attachmentFilesFromTransfer,
|
|
8
|
-
attachmentFromFile,
|
|
9
|
-
transferCarriesFiles,
|
|
10
|
-
} from "./attachment-image.js";
|
|
1
|
+
import { Fragment, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
2
|
+
import { FiAlertTriangle, FiArrowUp, FiCheck, FiChevronRight, FiClock, FiDownload, FiImage, FiMoreHorizontal, FiPaperclip, FiPlus, FiRotateCw, FiX } from "react-icons/fi";
|
|
3
|
+
import { ATTACHMENT_FAILED_NOTICE, ATTACHMENT_LIMIT_NOTICE, ATTACHMENT_TYPE_NOTICE, attachmentFilesFromTransfer, attachmentFromFile, transferCarriesFiles } from "./attachment-image.js";
|
|
11
4
|
import { isImeComposing } from "../ime.js";
|
|
12
5
|
import { useStudioShell } from "../shell/studio-shell-context.js";
|
|
13
6
|
import { ko } from "../locale.js";
|
|
@@ -31,10 +24,8 @@ import {
|
|
|
31
24
|
clampPanelWidth,
|
|
32
25
|
createAgentTransport,
|
|
33
26
|
panelPresentation,
|
|
34
|
-
providerEnvLabel,
|
|
35
27
|
requestHostImageAction,
|
|
36
|
-
|
|
37
|
-
formatElapsed,
|
|
28
|
+
formatTurnClock,
|
|
38
29
|
formatResetIn,
|
|
39
30
|
readStoredPanelWidth,
|
|
40
31
|
readStoredSupervisor,
|
|
@@ -47,6 +38,11 @@ import {
|
|
|
47
38
|
supervisorFollowUp,
|
|
48
39
|
} from "./agent-client.js";
|
|
49
40
|
import "./agent-panel.css";
|
|
41
|
+
import { AgentProgressCard } from "./AgentProgressCard.jsx";
|
|
42
|
+
import { LoginPanel } from "./LoginPanel.jsx";
|
|
43
|
+
import { TurnSteps } from "./AgentTurnSteps.jsx";
|
|
44
|
+
import { ReplyText } from "./AgentReplyText.jsx";
|
|
45
|
+
import { stepLabel, turnLayout } from "./agent-turn.js";
|
|
50
46
|
|
|
51
47
|
function relativeTime(value, now = Date.now()) {
|
|
52
48
|
const at = Date.parse(value || "");
|
|
@@ -65,32 +61,6 @@ function StatusDot({ tone, title }) {
|
|
|
65
61
|
return <span className={`agent-status-dot ${tone}`} title={title} aria-hidden="true" />;
|
|
66
62
|
}
|
|
67
63
|
|
|
68
|
-
function ToolCallCard({ call, presentation, onRetry }) {
|
|
69
|
-
const tone = call.status === "running" ? "busy" : ["failed", "cancelled"].includes(call.status) ? "alert" : "ok";
|
|
70
|
-
// The badge marks a call that edits the host surface, so it is drawn exactly
|
|
71
|
-
// for the families that surface names.
|
|
72
|
-
const hostTool = Object.hasOwn(presentation.toolLabels, call.name);
|
|
73
|
-
const detail = call.error ? `error: ${call.error}` : JSON.stringify(call.result ?? call.args ?? {}, null, 2);
|
|
74
|
-
return <div className={`agent-card agent-tool-card${call.status === "failed" ? " failed" : ""}`} data-tool-status={call.status} data-tool-name={call.name}>
|
|
75
|
-
<details>
|
|
76
|
-
<summary>
|
|
77
|
-
<StatusDot tone={tone} title={call.status} />
|
|
78
|
-
<span className="agent-tool-label">{resolveToolLabel(call, presentation.toolLabels)}</span>{hostTool && <span className="agent-tool-badge">{presentation.toolBadge}</span>}
|
|
79
|
-
<span className="agent-tool-elapsed">{call.status === "running" ? "running…" : call.status === "cancelled" ? (call.result?.status === "not_applied" ? "not applied" : "result unknown") : formatElapsed(call.elapsedMs)}</span>
|
|
80
|
-
<FiChevronRight size={12} aria-hidden="true" />
|
|
81
|
-
</summary>
|
|
82
|
-
<pre className="agent-tool-detail">{detail}</pre>
|
|
83
|
-
</details>
|
|
84
|
-
{call.failure && <div className="agent-error" role="alert">
|
|
85
|
-
<span>{call.failure.message || ERROR_COPY[call.failure.code] || "The turn failed."}</span>
|
|
86
|
-
<div className="agent-error-actions">
|
|
87
|
-
<button type="button" className="agent-ghost-button agent-error-retry" onClick={onRetry}>Retry</button>
|
|
88
|
-
<button type="button" className="agent-ghost-button agent-error-details" onClick={(event) => { const card = event.currentTarget.closest(".agent-tool-card"); const details = card?.querySelector("details"); if (details) details.open = !details.open; }}>Details</button>
|
|
89
|
-
</div>
|
|
90
|
-
</div>}
|
|
91
|
-
</div>;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
64
|
function ImageResultCard({ image, onUse, onUndo, onRegenerate, onOpen }) {
|
|
95
65
|
const applying = image.apply?.status === "applying";
|
|
96
66
|
const failed = image.apply?.status === "failed";
|
|
@@ -250,69 +220,15 @@ const RECOVERY_COPY = {
|
|
|
250
220
|
new_intent: "Tell the agent what to do with the changed target.",
|
|
251
221
|
reconcile: "The result is unknown; reconcile before changing this target.",
|
|
252
222
|
sign_in: "Sign in again to continue.",
|
|
223
|
+
fix_key: "Log in again under Connect a model, then retry.",
|
|
253
224
|
};
|
|
254
225
|
|
|
255
|
-
function FailureCard({ failure, onRetry }) {
|
|
226
|
+
function FailureCard({ failure, onRetry, onFixKey }) {
|
|
256
227
|
return <div className="agent-card agent-failure-card" data-failure-code={failure.code} role="alert">
|
|
257
228
|
<div className="agent-failure-head"><FiAlertTriangle size={12} aria-hidden="true" /><span className="agent-failure-code">{failure.code}</span></div>
|
|
258
229
|
<p className="agent-failure-message">{failure.message || "The command did not complete."}</p>
|
|
259
230
|
<p className="agent-failure-recovery">{RECOVERY_COPY[failure.recovery?.action] || RECOVERY_COPY.none}{failure.preserved?.authoredState === "unchanged" ? " Nothing in the scene changed." : failure.preserved?.authoredState === "unknown" ? " Whether the scene changed is unknown." : ""}</p>
|
|
260
|
-
{failure.recovery?.retryAllowed && <div className="agent-error-actions"
|
|
261
|
-
</div>;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// One provider's credential row. The key lives in this component and nowhere
|
|
265
|
-
// else: it is typed into a password field, handed to the transport once and
|
|
266
|
-
// dropped the moment the sidecar accepts it, so no store, no transcript and no
|
|
267
|
-
// screenshot can ever carry it. The sidecar answers with the SOURCE of a key
|
|
268
|
-
// (env / file), never with a key, which is what the row reports.
|
|
269
|
-
function ProviderKeyRow({ provider, onSave, onRemove }) {
|
|
270
|
-
const [draft, setDraft] = useState("");
|
|
271
|
-
const [busy, setBusy] = useState(false);
|
|
272
|
-
const [error, setError] = useState("");
|
|
273
|
-
const fromEnv = provider.authSource === "env";
|
|
274
|
-
const fromFile = provider.authSource === "file";
|
|
275
|
-
const envLabel = `set by ${providerEnvLabel(provider.id)}`;
|
|
276
|
-
const inputId = `agent-key-${provider.id}`;
|
|
277
|
-
const run = async (action) => {
|
|
278
|
-
setBusy(true);
|
|
279
|
-
setError("");
|
|
280
|
-
try {
|
|
281
|
-
await action();
|
|
282
|
-
// The key is gone from the page the instant it is stored.
|
|
283
|
-
setDraft("");
|
|
284
|
-
} catch (failure) {
|
|
285
|
-
// The sidecar's own refusal, which never quotes key material.
|
|
286
|
-
setError(failure?.message || "The sidecar refused that key.");
|
|
287
|
-
} finally {
|
|
288
|
-
setBusy(false);
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
return <div className="agent-key-row" data-provider={provider.id} data-provider-source={provider.authSource || "none"} data-provider-signed-in={provider.signedIn ? "true" : "false"}>
|
|
292
|
-
<div className="agent-key-head">
|
|
293
|
-
<StatusDot tone={provider.signedIn ? "ok" : ""} title={provider.signedIn ? "key configured" : "no key"} />
|
|
294
|
-
<label className="agent-key-label" htmlFor={inputId}>{provider.label}</label>
|
|
295
|
-
{/* The head states WHERE the key lives in two words; the variable's own
|
|
296
|
-
name belongs on the input it replaces, where there is room for it. */}
|
|
297
|
-
<span className="agent-key-source">{fromEnv ? "environment" : fromFile ? "saved on this machine" : "no key"}</span>
|
|
298
|
-
</div>
|
|
299
|
-
<div className="agent-key-controls">
|
|
300
|
-
<input
|
|
301
|
-
id={inputId}
|
|
302
|
-
className="agent-key-input"
|
|
303
|
-
type="password"
|
|
304
|
-
autoComplete="off"
|
|
305
|
-
spellCheck="false"
|
|
306
|
-
aria-label={`${provider.label} API key`}
|
|
307
|
-
placeholder={fromEnv ? envLabel : provider.signedIn ? "Replace key" : "Paste API key"}
|
|
308
|
-
disabled={fromEnv || busy}
|
|
309
|
-
value={draft}
|
|
310
|
-
onChange={(event) => { setDraft(event.target.value); setError(""); }}
|
|
311
|
-
/>
|
|
312
|
-
<button type="button" className="agent-key-save" disabled={fromEnv || busy || !draft.trim()} onClick={() => run(() => onSave(provider.id, draft))}>Save</button>
|
|
313
|
-
{fromFile && <button type="button" className="agent-ghost-button agent-key-remove" disabled={busy} onClick={() => run(() => onRemove(provider.id))}>Remove</button>}
|
|
314
|
-
</div>
|
|
315
|
-
{error && <p className="agent-key-error" role="alert">{error}</p>}
|
|
231
|
+
{failure.recovery?.retryAllowed && <div className="agent-error-actions">{failure.recovery.action === "fix_key" && onFixKey && <button type="button" className="agent-ghost-button agent-error-keys" onClick={onFixKey}>Log in again</button>}<button type="button" className="agent-ghost-button agent-error-retry" onClick={onRetry}>Retry</button></div>}
|
|
316
232
|
</div>;
|
|
317
233
|
}
|
|
318
234
|
|
|
@@ -336,7 +252,6 @@ export default function AgentPanel({
|
|
|
336
252
|
surface = "workflow",
|
|
337
253
|
buildContext = null,
|
|
338
254
|
onImageAction = null,
|
|
339
|
-
onI2vAction = null,
|
|
340
255
|
onReceipt = null,
|
|
341
256
|
}) {
|
|
342
257
|
// What this host shows around the shared conversation: labels, chips, the
|
|
@@ -405,10 +320,8 @@ export default function AgentPanel({
|
|
|
405
320
|
const [lightbox, setLightbox] = useState(null);
|
|
406
321
|
const [overlay, setOverlay] = useState(() => !embedded && (globalThis.innerWidth || 1440) < AGENT_PANEL_OVERLAY_BREAKPOINT);
|
|
407
322
|
const [historyOpen, setHistoryOpen] = useState(false);
|
|
408
|
-
//
|
|
323
|
+
// Connect a model (pi's login list, LoginPanel.jsx) reads its own list from the sidecar while it is open.
|
|
409
324
|
const [keysOpen, setKeysOpen] = useState(false);
|
|
410
|
-
const [providers, setProviders] = useState([]);
|
|
411
|
-
const [providersState, setProvidersState] = useState("loading");
|
|
412
325
|
const [historySessions, setHistorySessions] = useState([]);
|
|
413
326
|
const [restoreNotice, setRestoreNotice] = useState("");
|
|
414
327
|
// Why the last steer did not reach the running turn. It reads in the same
|
|
@@ -427,8 +340,6 @@ export default function AgentPanel({
|
|
|
427
340
|
buildContextRef.current = buildContext;
|
|
428
341
|
const imageActionRef = useRef(onImageAction);
|
|
429
342
|
imageActionRef.current = onImageAction;
|
|
430
|
-
const i2vActionRef = useRef(onI2vAction);
|
|
431
|
-
i2vActionRef.current = onI2vAction;
|
|
432
343
|
const receiptRef = useRef(onReceipt);
|
|
433
344
|
receiptRef.current = onReceipt;
|
|
434
345
|
|
|
@@ -479,10 +390,15 @@ export default function AgentPanel({
|
|
|
479
390
|
// is live and holding the turn's outcome for a few seconds after it ends.
|
|
480
391
|
const [activityNow, setActivityNow] = useState(() => Date.now());
|
|
481
392
|
const activity = model
|
|
482
|
-
? describeActivity(chat, { now: activityNow, toolLabel: (call) =>
|
|
393
|
+
? describeActivity(chat, { now: activityNow, toolLabel: (call) => stepLabel(call, presentation.toolLabels) })
|
|
483
394
|
: modelsState === "failed"
|
|
484
395
|
? { phase: "no-model", kind: "terminal", tone: "alert", text: "No model available — the agent service did not answer with one", ticking: false }
|
|
485
396
|
: { phase: "models", kind: "live", tone: "busy", text: "Loading models…", ticking: false };
|
|
397
|
+
// The transcript grouped per turn, and the motion progress card (above the latest turn's steps, so the live
|
|
398
|
+
// step stays at the bottom where the transcript scrolls) limited to that turn's drafts.
|
|
399
|
+
const layout = useMemo(() => turnLayout(items, { streaming }), [items, streaming]);
|
|
400
|
+
const turnSince = streaming ? chat.turnStartedAt : chat.lastTurn ? chat.lastTurn.endedAt - chat.lastTurn.durationMs : null;
|
|
401
|
+
const progressCard = <AgentProgressCard items={items} since={turnSince} live={streaming} />;
|
|
486
402
|
useEffect(() => {
|
|
487
403
|
if (!activity.ticking) return;
|
|
488
404
|
const timer = setInterval(() => setActivityNow(Date.now()), 500);
|
|
@@ -510,21 +426,6 @@ export default function AgentPanel({
|
|
|
510
426
|
return null;
|
|
511
427
|
}
|
|
512
428
|
}, [sessionState, transport]);
|
|
513
|
-
// The sidecar owns the credentials; this reads which providers have one and
|
|
514
|
-
// where it came from. The list is read when the keys section opens and again
|
|
515
|
-
// after every write — a key that just landed (or left) changes which models
|
|
516
|
-
// the composer may offer, so the model list is re-read with it.
|
|
517
|
-
const readProviders = useCallback(async () => {
|
|
518
|
-
try {
|
|
519
|
-
const list = await transport.providers?.();
|
|
520
|
-
if (!Array.isArray(list)) throw new Error("no provider list");
|
|
521
|
-
setProviders(list);
|
|
522
|
-
setProvidersState("ready");
|
|
523
|
-
} catch {
|
|
524
|
-
setProviders([]);
|
|
525
|
-
setProvidersState("failed");
|
|
526
|
-
}
|
|
527
|
-
}, [transport]);
|
|
528
429
|
// Everything an authentication change moves, read back once: WHO the session
|
|
529
430
|
// is (readiness is `signedIn || providersConfigured > 0`), which providers
|
|
530
431
|
// hold a credential, and WHICH models this session may therefore send to. A
|
|
@@ -535,13 +436,11 @@ export default function AgentPanel({
|
|
|
535
436
|
// into a composer without reloading the page, and what takes it away again
|
|
536
437
|
// when the last way in goes. No timer is involved.
|
|
537
438
|
const refreshProviderState = useCallback(async () => {
|
|
538
|
-
|
|
539
|
-
await readProviders();
|
|
540
|
-
await session;
|
|
439
|
+
await readAccount();
|
|
541
440
|
try {
|
|
542
441
|
applyModelList(await transport.models());
|
|
543
442
|
} catch { /* the composer keeps the list it was last advertised */ }
|
|
544
|
-
}, [applyModelList, readAccount,
|
|
443
|
+
}, [applyModelList, readAccount, transport]);
|
|
545
444
|
|
|
546
445
|
useEffect(() => {
|
|
547
446
|
let cancelled = false;
|
|
@@ -798,20 +697,13 @@ export default function AgentPanel({
|
|
|
798
697
|
// refresh above, because a key changes the session, not just a row.
|
|
799
698
|
const toggleProviderKeys = useCallback(() => {
|
|
800
699
|
setMenuOpen(false);
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
await transport.setProviderKey(id, key);
|
|
809
|
-
await refreshProviderState();
|
|
810
|
-
}, [refreshProviderState, transport]);
|
|
811
|
-
const removeProviderKey = useCallback(async (id) => {
|
|
812
|
-
await transport.removeProviderKey(id);
|
|
813
|
-
await refreshProviderState();
|
|
814
|
-
}, [refreshProviderState, transport]);
|
|
700
|
+
setKeysOpen(!keysOpen);
|
|
701
|
+
}, [keysOpen]);
|
|
702
|
+
// Opens (never toggles) Connect a model, pi's login list: the signed-out card and a refused key both lead here.
|
|
703
|
+
const openProviderKeys = useCallback(() => {
|
|
704
|
+
setMenuOpen(false);
|
|
705
|
+
setKeysOpen(true);
|
|
706
|
+
}, []);
|
|
815
707
|
|
|
816
708
|
// Clearing the transcript also retires the session, so what the author sees
|
|
817
709
|
// and what the model remembers cannot diverge.
|
|
@@ -927,8 +819,8 @@ export default function AgentPanel({
|
|
|
927
819
|
{resetLabel && <span className="agent-account-reset">resets in {resetLabel}</span>}
|
|
928
820
|
</div>}
|
|
929
821
|
<button type="button" role="menuitem" onClick={clearContext}>Clear context</button>
|
|
930
|
-
<button type="button" role="menuitem" className="agent-menu-keys" aria-expanded={keysOpen} onClick={toggleProviderKeys}>
|
|
931
|
-
<button type="button" role="menuitem" onClick={signOut}>Sign out</button>
|
|
822
|
+
<button type="button" role="menuitem" className="agent-menu-keys" aria-expanded={keysOpen} onClick={toggleProviderKeys}>Connect a model…</button>
|
|
823
|
+
{account?.signedIn && <button type="button" role="menuitem" onClick={signOut}>Sign out</button>}
|
|
932
824
|
</div>}
|
|
933
825
|
</span>
|
|
934
826
|
{!embedded && <button type="button" className="agent-icon-button agent-collapse" onClick={() => setCollapsed(true)} aria-label="Collapse agent panel" title="Collapse agent panel (Cmd/Ctrl+B)"><FiChevronRight size={13} /></button>}
|
|
@@ -939,28 +831,21 @@ export default function AgentPanel({
|
|
|
939
831
|
|
|
940
832
|
{/* An inline section, not a modal: the panel stays the one place the
|
|
941
833
|
conversation and the credentials it runs on are managed. */}
|
|
942
|
-
{keysOpen && <
|
|
943
|
-
<div className="agent-keys-head">
|
|
944
|
-
<h3 className="agent-keys-title">Provider keys</h3>
|
|
945
|
-
<button type="button" className="agent-icon-button agent-keys-close" aria-label="Close provider keys" onClick={() => setKeysOpen(false)}><FiX size={11} aria-hidden="true" /></button>
|
|
946
|
-
</div>
|
|
947
|
-
<p className="agent-keys-hint">Keys are stored by the local CozyClay service and are never shown again.</p>
|
|
948
|
-
{providersState === "loading" && <p className="agent-keys-note">Loading providers…</p>}
|
|
949
|
-
{providersState === "failed" && <p className="agent-keys-note alert" role="alert">The provider list is unavailable.</p>}
|
|
950
|
-
{providers.filter((entry) => entry.id !== "openai-codex").map((entry) => <ProviderKeyRow key={entry.id} provider={entry} onSave={saveProviderKey} onRemove={removeProviderKey} />)}
|
|
951
|
-
</section>}
|
|
834
|
+
{keysOpen && <LoginPanel onClose={() => setKeysOpen(false)} onChatGPT={signIn} onChanged={refreshProviderState} />}
|
|
952
835
|
|
|
953
836
|
<div className="agent-transcript" ref={transcriptRef} aria-live="polite" aria-label="Conversation" data-agent-transcript="true">
|
|
954
837
|
{authState === "signed-out" && <div className="agent-state-card" data-agent-card="signed-out">
|
|
955
|
-
<h3>
|
|
956
|
-
<p>
|
|
838
|
+
<h3>Connect a model</h3>
|
|
839
|
+
<p>Sign in with ChatGPT, or log in to another provider with a subscription, an API key, or your own proxy server. Sign-in happens in your browser — no token is ever stored in this page.</p>
|
|
957
840
|
<button type="button" className="agent-primary-button agent-signin" onClick={signIn}>Sign in with ChatGPT</button>
|
|
841
|
+
<button type="button" className="agent-ghost-button agent-use-key" onClick={openProviderKeys}>Log in with another provider</button>
|
|
958
842
|
</div>}
|
|
959
843
|
|
|
960
844
|
{authState === "signing-in" && <div className="agent-state-card" data-agent-card="signing-in">
|
|
961
845
|
<span className="agent-spinner" aria-hidden="true" />
|
|
962
846
|
<h3>Waiting for your browser…</h3>
|
|
963
|
-
<p>Finish the ChatGPT sign-in in the
|
|
847
|
+
<p>Finish the ChatGPT sign-in in the browser page that just opened. This panel picks up the session when you come back.</p>
|
|
848
|
+
{account?.authorizeUrl && <a className="agent-ghost-button agent-signin-link" href={account.authorizeUrl} target="_blank" rel="noopener noreferrer">Open the sign-in page again</a>}
|
|
964
849
|
</div>}
|
|
965
850
|
|
|
966
851
|
{authState === "no-entitlement" && <div className="agent-state-card" data-agent-card="no-entitlement">
|
|
@@ -977,22 +862,28 @@ export default function AgentPanel({
|
|
|
977
862
|
</div>
|
|
978
863
|
</div>}
|
|
979
864
|
|
|
980
|
-
{
|
|
865
|
+
{/* One step list per turn (AgentTurnSteps.jsx); the motion progress card sits just above the latest one. */}
|
|
866
|
+
{layout.map((entry) => {
|
|
867
|
+
if (entry.type === "steps") return <Fragment key={entry.id}>
|
|
868
|
+
{entry.last && progressCard}
|
|
869
|
+
<div className="agent-row"><TurnSteps block={entry} now={activityNow} toolLabels={presentation.toolLabels} formatClock={formatTurnClock} onRetry={() => runTurn(chat.lastPrompt)} /></div>
|
|
870
|
+
</Fragment>;
|
|
871
|
+
const { item } = entry;
|
|
981
872
|
if (item.kind === "user") return <div className="agent-row user" key={item.id}>
|
|
982
873
|
{item.attachments?.length > 0 && <div className="agent-bubble-attachments">
|
|
983
874
|
{item.attachments.map((attachment, index) => <img key={`${item.id}-${index}`} src={attachment.dataUrl} alt={attachment.name || "Attached image"} onClick={() => setLightbox({ dataUrl: attachment.dataUrl, prompt: attachment.name })} />)}
|
|
984
875
|
</div>}
|
|
985
876
|
<div className="agent-bubble">{item.text}</div>
|
|
986
877
|
</div>;
|
|
987
|
-
if (item.kind === "assistant") return <div className="agent-row assistant" key={item.id}><div className="agent-assistant-text"
|
|
988
|
-
if (item.kind === "tool") return <div className="agent-row" key={item.id}><ToolCallCard call={item} presentation={presentation} onRetry={() => runTurn(chat.lastPrompt)} /></div>;
|
|
878
|
+
if (item.kind === "assistant") return <div className="agent-row assistant" key={item.id}><div className="agent-assistant-text"><ReplyText text={item.text} tail={streaming && <span className="agent-caret">▌</span>} /></div></div>;
|
|
989
879
|
if (item.kind === "job") return <div className="agent-row" key={item.id}><JobCard job={item} onStop={stopTurn} onAccept={(job) => store.acceptJob(job.jobId)} /></div>;
|
|
990
880
|
if (item.kind === "receipt") return <div className="agent-row" key={item.id}><ReceiptCard item={item} /></div>;
|
|
991
|
-
if (item.kind === "failure") return <div className="agent-row" key={item.id}><FailureCard failure={item.failure} onRetry={() => runTurn(chat.lastPrompt)} /></div>;
|
|
881
|
+
if (item.kind === "failure") return <div className="agent-row" key={item.id}><FailureCard failure={item.failure} onRetry={() => runTurn(chat.lastPrompt)} onFixKey={openProviderKeys} /></div>;
|
|
992
882
|
if (item.kind === "supervisor") return <div className="agent-row" key={item.id}><SupervisorCard note={item} onSend={(text) => { store.setDraft(text); runTurn(text); }} /></div>;
|
|
993
883
|
return <div className="agent-row" key={item.id}><ImageResultCard image={item} onUse={(image) => store.applyImage(image.id)} onUndo={(image) => store.undoImage(image.id)} onRegenerate={() => runTurn(chat.lastPrompt)} onOpen={setLightbox} /></div>;
|
|
994
884
|
})}
|
|
995
885
|
|
|
886
|
+
{!layout.some((entry) => entry.type === "steps" && entry.last) && progressCard}
|
|
996
887
|
{rateLimit && <PausedCard resetAt={rateLimit.resetAt} onRetry={() => { store.clearRateLimit(); runTurn(chat.lastPrompt); }} onSwitchModel={switchModel} />}
|
|
997
888
|
</div>
|
|
998
889
|
|
|
@@ -1001,6 +892,7 @@ export default function AgentPanel({
|
|
|
1001
892
|
{authenticated && <div className="agent-activity" data-agent-activity={activity.phase} data-agent-activity-kind={activity.kind} role="status" aria-live="polite">
|
|
1002
893
|
<span className={`agent-activity-indicator ${activity.tone}`} aria-hidden="true" />
|
|
1003
894
|
<span className="agent-activity-text">{activity.text}</span>
|
|
895
|
+
{activity.detail && <span className="agent-activity-detail">{activity.detail}</span>}
|
|
1004
896
|
</div>}
|
|
1005
897
|
|
|
1006
898
|
{authenticated && <div
|
|
@@ -1062,17 +954,6 @@ export default function AgentPanel({
|
|
|
1062
954
|
</button>
|
|
1063
955
|
)}
|
|
1064
956
|
<span className="agent-composer-spacer" aria-hidden="true" />
|
|
1065
|
-
{surface === "studio" && i2vActionRef.current && <button
|
|
1066
|
-
type="button"
|
|
1067
|
-
className="agent-attach-chip agent-i2v-action"
|
|
1068
|
-
data-testid="i2v-agent-action"
|
|
1069
|
-
aria-label="Generate motion"
|
|
1070
|
-
title="Generate motion from this message"
|
|
1071
|
-
disabled={composerDisabled || !draft.trim() || streaming}
|
|
1072
|
-
onClick={() => { const instruction = draft.trim(); store.setDraft(""); i2vActionRef.current?.(instruction); }}
|
|
1073
|
-
>
|
|
1074
|
-
<FiFilm size={13} aria-hidden="true" />
|
|
1075
|
-
</button>}
|
|
1076
957
|
{/* While a turn runs, Stop is still the way out of it; on a surface
|
|
1077
958
|
that can be steered, Send becomes the way INTO it. */}
|
|
1078
959
|
{streaming
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// The live progress card in the agent panel (agent-progress.js). While the agent works on motion it shows what
|
|
2
|
+
// the agent actually produced so far: how many drafts it compiled and their QA trend, the latest draft's goals
|
|
3
|
+
// (met or not, value against need) and heaviest issues, whether that draft is installed, and the images the
|
|
4
|
+
// agent asked to see (click to enlarge, over the whole window). Nothing here is invented progress. A new turn
|
|
5
|
+
// starts it over.
|
|
6
|
+
import { useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
7
|
+
import { createPortal } from "react-dom";
|
|
8
|
+
import { FiCheck, FiX } from "react-icons/fi";
|
|
9
|
+
import { clearProgress, draftsOf, latestDraft, progressSheets, progressVersion, subscribeProgress } from "../agent-progress.js";
|
|
10
|
+
import { ko } from "../locale.js";
|
|
11
|
+
import { goalRows, issueRows, trendPoints, turnCount } from "./agent-progress-view.js";
|
|
12
|
+
import "./agent-progress-card.css";
|
|
13
|
+
|
|
14
|
+
const W = 220, H = 40;
|
|
15
|
+
|
|
16
|
+
function Trend({ totals }) {
|
|
17
|
+
const { points, first, last } = trendPoints(totals, { width: W, height: H });
|
|
18
|
+
if (!points.length) return null;
|
|
19
|
+
const [lx, ly] = points[points.length - 1];
|
|
20
|
+
const label = ko(`QA issues by draft: ${totals.join(", ")}`, `초안별 QA 문제 수: ${totals.join(", ")}`);
|
|
21
|
+
return <div className="agent-progress-trend">
|
|
22
|
+
<svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={label} preserveAspectRatio="none">
|
|
23
|
+
<line className="base" x1={4} y1={H - 4} x2={W - 4} y2={H - 4} />
|
|
24
|
+
{points.length > 1 && <polyline className="line" points={points.map(p => p.join(",")).join(" ")} />}
|
|
25
|
+
{points.slice(0, -1).map(([x, y], i) => <circle key={i} className="dot" cx={x} cy={y} r={1.8} />)}
|
|
26
|
+
<circle className={`end ${last === 0 ? "ok" : "issue"}`} cx={lx} cy={ly} r={3.4} />
|
|
27
|
+
</svg>
|
|
28
|
+
<span className="agent-progress-trend-text">{totals.length > 1 ? `${first} → ${last}` : `${last}`}</span>
|
|
29
|
+
</div>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The panel's own lightbox stays inside the panel; a contact sheet needs the window.
|
|
33
|
+
function SheetViewer({ sheet, onClose }) {
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const onKey = event => { if (event.key === "Escape") onClose(); };
|
|
36
|
+
window.addEventListener("keydown", onKey);
|
|
37
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
38
|
+
}, [onClose]);
|
|
39
|
+
return createPortal(<button type="button" className="agent-progress-viewer" aria-label={ko("Close image", "이미지 닫기")} onClick={onClose}>
|
|
40
|
+
<img src={sheet.dataUrl} alt={sheet.caption || ko("Image the agent checked", "에이전트가 확인한 이미지")} />
|
|
41
|
+
{sheet.caption && <span className="agent-progress-viewer-caption">{sheet.caption}</span>}
|
|
42
|
+
</button>, document.body);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** `since`: when the latest turn started (null: no turn yet). Drafts and images from before it are not this turn's
|
|
46
|
+
* work, such as the take the camera tutorial compiles at startup. `live`: that turn is still running. */
|
|
47
|
+
export function AgentProgressCard({ items, since = null, live = false }) {
|
|
48
|
+
useSyncExternalStore(subscribeProgress, progressVersion);
|
|
49
|
+
const [open, setOpen] = useState(null);
|
|
50
|
+
// The goal and issue lists fold to one line each: the panel is short and the live steps sit below the card.
|
|
51
|
+
const [shown, setShown] = useState({});
|
|
52
|
+
const toggle = key => setShown(now => ({ ...now, [key]: !now[key] }));
|
|
53
|
+
// a new turn (not a steer of the running one) starts the card over
|
|
54
|
+
const turns = turnCount(items), seen = useRef(turns);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (turns > seen.current) clearProgress();
|
|
57
|
+
seen.current = turns;
|
|
58
|
+
}, [turns]);
|
|
59
|
+
const ours = record => since !== null && record.at >= since;
|
|
60
|
+
const newest = latestDraft(), latest = newest && ours(newest) ? newest : null, sheets = progressSheets().filter(ours);
|
|
61
|
+
const viewing = open && sheets.find(sheet => sheet.seq === open);
|
|
62
|
+
if (!latest && !sheets.length) return null;
|
|
63
|
+
const drafts = latest ? draftsOf(latest.characterId).filter(ours) : [];
|
|
64
|
+
const goals = goalRows(latest?.qa.goals), issues = latest && !latest.qa.ok ? issueRows(latest.qa.groups) : [];
|
|
65
|
+
return <div className="agent-row">
|
|
66
|
+
<div className="agent-card agent-progress-card" data-agent-card="progress" data-drafts={drafts.length} data-installed={latest?.installed ? "true" : "false"} role="status" aria-live="polite">
|
|
67
|
+
{latest && <div className="agent-progress-head">
|
|
68
|
+
<span className="agent-progress-title">{ko("Draft", "초안")} {latest.n} · QA {latest.qa.total}</span>
|
|
69
|
+
<span className={`agent-progress-chip ${latest.installed ? "installed" : "draft"}`}>{latest.installed ? ko("Installed", "설치됨") : live ? ko("Not installed yet", "아직 설치 전") : ko("Not installed", "설치 안 됨")}</span>
|
|
70
|
+
</div>}
|
|
71
|
+
{latest?.label && <p className="agent-progress-label">{latest.label}</p>}
|
|
72
|
+
{drafts.length > 0 && <section className="agent-progress-section">
|
|
73
|
+
<h4>{ko(`QA issues across ${drafts.length} draft${drafts.length === 1 ? "" : "s"}`, `초안 ${drafts.length}개의 QA 문제 수`)}</h4>
|
|
74
|
+
<Trend totals={drafts.map(d => d.qa.total)} />
|
|
75
|
+
</section>}
|
|
76
|
+
{goals.length > 0 && <section className="agent-progress-section">
|
|
77
|
+
<button type="button" className="agent-progress-fold" aria-expanded={!!shown.goals} onClick={() => toggle("goals")}>
|
|
78
|
+
<h4>{ko("Goals", "목표")}</h4><span>{ko(`${goals.filter(g => g.ok).length} of ${goals.length} met`, `${goals.length}개 중 ${goals.filter(g => g.ok).length}개 충족`)}</span>
|
|
79
|
+
</button>
|
|
80
|
+
{shown.goals && <ul className="agent-progress-goals">
|
|
81
|
+
{goals.map((g, i) => <li key={i} data-goal-ok={g.ok ? "true" : "false"}>
|
|
82
|
+
<span className={`agent-progress-mark ${g.ok ? "ok" : "miss"}`} aria-label={g.ok ? ko("met", "충족") : ko("missed", "미충족")}>{g.ok ? <FiCheck size={11} /> : <FiX size={11} />}</span>
|
|
83
|
+
<span className="agent-progress-goal-name">{g.name}</span>
|
|
84
|
+
<span className="agent-progress-goal-value">{g.value}{g.need && <span className="need"> / {g.need}</span>}</span>
|
|
85
|
+
</li>)}
|
|
86
|
+
</ul>}
|
|
87
|
+
</section>}
|
|
88
|
+
{issues.length > 0 && <section className="agent-progress-section">
|
|
89
|
+
<button type="button" className="agent-progress-fold" aria-expanded={!!shown.issues} onClick={() => toggle("issues")}>
|
|
90
|
+
<h4>{ko("Largest issues", "가장 큰 문제")}</h4><span>{issues[0].kind} {issues[0].subject}</span>
|
|
91
|
+
</button>
|
|
92
|
+
{shown.issues && <ul className="agent-progress-issues">
|
|
93
|
+
{issues.map((row, i) => <li key={i}><span className="kind">{row.kind}</span> {row.subject} <span className="meta">×{row.count}{row.frames && ` · ${row.frames}`}</span></li>)}
|
|
94
|
+
</ul>}
|
|
95
|
+
</section>}
|
|
96
|
+
{sheets.length > 0 && <section className="agent-progress-section">
|
|
97
|
+
<h4>{ko("What the agent looked at", "에이전트가 본 화면")}</h4>
|
|
98
|
+
<div className="agent-progress-sheets">
|
|
99
|
+
{sheets.map(sheet => <button type="button" key={sheet.seq} className="agent-progress-sheet" title={sheet.caption} onClick={() => setOpen(sheet.seq)}>
|
|
100
|
+
<img src={sheet.dataUrl} alt={sheet.caption || ko("Image the agent checked", "에이전트가 확인한 이미지")} />
|
|
101
|
+
</button>)}
|
|
102
|
+
</div>
|
|
103
|
+
</section>}
|
|
104
|
+
</div>
|
|
105
|
+
{viewing && <SheetViewer sheet={viewing} onClose={() => setOpen(null)} />}
|
|
106
|
+
</div>;
|
|
107
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// The agent's reply as formatted text (reply-markdown.js): paragraphs, headings, lists, bold, code and tables,
|
|
2
|
+
// never the raw marks. `tail` (the streaming caret) sits at the end of the last line.
|
|
3
|
+
import { Fragment, useMemo } from "react";
|
|
4
|
+
import { replyBlocks } from "./reply-markdown.js";
|
|
5
|
+
|
|
6
|
+
function Spans({ spans }) {
|
|
7
|
+
return spans.map((span, index) => {
|
|
8
|
+
if (span.code) return <code key={index}>{span.text}</code>;
|
|
9
|
+
if (span.bold) return <strong key={index}>{span.text}</strong>;
|
|
10
|
+
return <Fragment key={index}>{span.text}</Fragment>;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function Block({ block, tail }) {
|
|
15
|
+
if (block.type === "h") return <p className="agent-reply-heading"><Spans spans={block.spans} />{tail}</p>;
|
|
16
|
+
if (block.type === "code") return <pre><code>{block.text}</code>{tail}</pre>;
|
|
17
|
+
if (block.type === "ul" || block.type === "ol") {
|
|
18
|
+
const List = block.type;
|
|
19
|
+
return <List start={block.start}>
|
|
20
|
+
{block.items.map((spans, index) => <li key={index}><Spans spans={spans} />{index === block.items.length - 1 && tail}</li>)}
|
|
21
|
+
</List>;
|
|
22
|
+
}
|
|
23
|
+
if (block.type === "table") return <>
|
|
24
|
+
<div className="agent-reply-table"><table>
|
|
25
|
+
<thead><tr>{block.head.map((spans, index) => <th key={index}><Spans spans={spans} /></th>)}</tr></thead>
|
|
26
|
+
<tbody>{block.rows.map((row, rowIndex) => <tr key={rowIndex}>{row.map((spans, index) => <td key={index}><Spans spans={spans} /></td>)}</tr>)}</tbody>
|
|
27
|
+
</table></div>
|
|
28
|
+
{tail}
|
|
29
|
+
</>;
|
|
30
|
+
return <p><Spans spans={block.spans} />{tail}</p>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function ReplyText({ text, tail = null }) {
|
|
34
|
+
const blocks = useMemo(() => replyBlocks(text), [text]);
|
|
35
|
+
if (!blocks.length) return tail || null;
|
|
36
|
+
return blocks.map((block, index) => <Block key={index} block={block} tail={index === blocks.length - 1 ? tail : null} />);
|
|
37
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// One turn's work in the agent panel (agent-turn.js turnLayout): a header that says how the turn stands (working,
|
|
2
|
+
// done, stopped, failed), how many steps and how long, and one compact row per step: the tool call with a plain
|
|
3
|
+
// label, what it was about and its one-line result; the reasoning the model streamed between calls; the call it is
|
|
4
|
+
// writing now. Open while the turn runs, folded to its header when the turn ends (a failed or stopped turn stays
|
|
5
|
+
// open, and a stopped one says what it left in the scene).
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
import { FiCheck, FiChevronRight, FiSquare, FiX } from "react-icons/fi";
|
|
8
|
+
import { ko } from "../locale.js";
|
|
9
|
+
import { scriptNow, stepLabel, stepResult, stepsHeader, stoppedNote, thinkingLine } from "./agent-turn.js";
|
|
10
|
+
import "./agent-turn-steps.css";
|
|
11
|
+
|
|
12
|
+
/** Step time: "0.6 s", "12 s", "1 m 05 s". */
|
|
13
|
+
function stepClock(ms) {
|
|
14
|
+
if (!Number.isFinite(ms) || ms < 0) return "";
|
|
15
|
+
const s = ms / 1000;
|
|
16
|
+
if (s < 10) return `${s.toFixed(1)} s`;
|
|
17
|
+
if (s < 60) return `${Math.round(s)} s`;
|
|
18
|
+
return `${Math.floor(s / 60)} m ${String(Math.round(s % 60)).padStart(2, "0")} s`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function StepIcon({ state }) {
|
|
22
|
+
if (state === "running") return <span className="agent-step-spin" aria-label={ko("running", "진행 중")} />;
|
|
23
|
+
if (state === "failed") return <FiX className="agent-step-icon alert" size={11} aria-label={ko("failed", "실패")} />;
|
|
24
|
+
if (state === "cancelled") return <FiSquare className="agent-step-icon warn" size={9} aria-label={ko("stopped", "중지됨")} />;
|
|
25
|
+
if (state === "note") return <span className="agent-step-dot" aria-hidden="true" />;
|
|
26
|
+
return <FiCheck className="agent-step-icon ok" size={11} aria-label={ko("done", "완료")} />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function Step({ step, live, isLast, now, toolLabels }) {
|
|
30
|
+
const [open, setOpen] = useState(false);
|
|
31
|
+
if (step.kind === "narration") return <li className="agent-step narration" data-step-kind="narration"><StepIcon state="note" /><span className="agent-step-text">{step.text}</span></li>;
|
|
32
|
+
if (step.kind === "thinking") {
|
|
33
|
+
const running = live && isLast;
|
|
34
|
+
const line = thinkingLine(step.text);
|
|
35
|
+
// from its first words until the next step began; a thought that arrived at once shows no time
|
|
36
|
+
const took = (running ? now : step.endedAt ?? step.updatedAt) - step.startedAt;
|
|
37
|
+
return <li className="agent-step thinking" data-step-kind="thinking">
|
|
38
|
+
<button type="button" className="agent-step-row" aria-expanded={open} onClick={() => setOpen(value => !value)}>
|
|
39
|
+
<StepIcon state={running ? "running" : "note"} />
|
|
40
|
+
<span className="agent-step-label">{ko("Thinking", "생각")}</span>
|
|
41
|
+
<span className="agent-step-subject">{open ? "" : line}</span>
|
|
42
|
+
<span className="agent-step-time">{took >= 1000 ? stepClock(took) : ""}</span>
|
|
43
|
+
</button>
|
|
44
|
+
{open && <p className="agent-step-thought">{step.text}</p>}
|
|
45
|
+
</li>;
|
|
46
|
+
}
|
|
47
|
+
const running = step.kind === "compose" || step.status === "running";
|
|
48
|
+
const label = step.kind === "compose" ? ko(`Preparing ${stepLabel(step, toolLabels)}`, `${stepLabel(step, toolLabels)} 준비 중`) : stepLabel(step, toolLabels);
|
|
49
|
+
const result = running ? (step.progress ? `${scriptNow(step)}${step.progress.summary ? ` — ${step.progress.summary}` : ""}` : "") : stepResult(step);
|
|
50
|
+
const time = running ? now - (step.startedAt ?? now) : step.elapsedMs;
|
|
51
|
+
return <li className={`agent-step ${step.kind}${step.status === "failed" ? " failed" : ""}`} data-step-kind={step.kind} data-tool-name={step.name} data-tool-status={step.kind === "compose" ? "composing" : step.status} data-action={step.action}>
|
|
52
|
+
<div className="agent-step-row">
|
|
53
|
+
<StepIcon state={running ? "running" : step.status} />
|
|
54
|
+
<span className="agent-step-label">{label}</span>
|
|
55
|
+
{step.subject && <span className="agent-step-subject">{step.subject}</span>}
|
|
56
|
+
<span className="agent-step-time">{stepClock(time)}</span>
|
|
57
|
+
</div>
|
|
58
|
+
{result && <p className="agent-step-result">{result}</p>}
|
|
59
|
+
</li>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function TurnSteps({ block, now, toolLabels, formatClock, onRetry }) {
|
|
63
|
+
const [chosen, setChosen] = useState(null);
|
|
64
|
+
const failed = block.end?.status === "failed" || block.steps.some(step => step.failure);
|
|
65
|
+
const stopped = block.end?.status === "stopped";
|
|
66
|
+
const open = chosen ?? (block.live || failed || stopped);
|
|
67
|
+
const header = stepsHeader(block, now, formatClock);
|
|
68
|
+
const failure = block.steps.find(step => step.failure)?.failure;
|
|
69
|
+
return <div className="agent-card agent-steps" data-agent-card="steps" data-steps-state={block.live ? "live" : block.end?.status ?? "done"} data-open={open ? "true" : "false"}>
|
|
70
|
+
<button type="button" className="agent-steps-head" aria-expanded={open} onClick={() => setChosen(!open)}>
|
|
71
|
+
<span className={`agent-status-dot ${header.tone}`} aria-hidden="true" />
|
|
72
|
+
<span className="agent-steps-title">{header.text}</span>
|
|
73
|
+
<FiChevronRight className="agent-steps-chevron" size={12} aria-hidden="true" />
|
|
74
|
+
</button>
|
|
75
|
+
{open && <ol className="agent-steps-list">
|
|
76
|
+
{block.steps.map((step, index) => <Step key={step.id} step={step} live={block.live} isLast={index === block.steps.length - 1} now={now} toolLabels={toolLabels} />)}
|
|
77
|
+
</ol>}
|
|
78
|
+
{stopped && <p className="agent-steps-note">{stoppedNote(block)}</p>}
|
|
79
|
+
{failure && <div className="agent-error" role="alert">
|
|
80
|
+
<span>{failure.message || ko("The turn failed.", "턴이 실패했어요.")}</span>
|
|
81
|
+
<div className="agent-error-actions"><button type="button" className="agent-ghost-button agent-error-retry" onClick={onRetry}>{ko("Retry", "다시 시도")}</button></div>
|
|
82
|
+
</div>}
|
|
83
|
+
</div>;
|
|
84
|
+
}
|